Move ASOP factory extraction tool to its own directory

Moved some source to common folder.
Added uploading script which is also shared by CE CDM partners.
Added README.

Test: m wv_factory_extraction_tool
Bug: 414642286
Change-Id: I565027b75528ab28f9f1eb8d9086c0213de992d0
This commit is contained in:
conglin
2025-06-10 18:20:43 +00:00
parent 1f77085571
commit 7496c1c84c
14 changed files with 555 additions and 7 deletions

View File

@@ -0,0 +1,34 @@
# Widevine Factory Extraction Tool for AOSP partners
This tool extracts the BCC and generates the Certificate Signing Request (CSR)
needed to be uploaded to Wideivine Provisioning server for Prov4 device registration.
## CSR extraction instructions:
1. Make `wv_factory_extraction_tool`:
- m wv_factory_extraction_tool
2. Locate build output and adb push it to the target device, e.g.:
- adb push out/target/product/{product name}/vendor/bin/hw/wv_factory_extraction_tool /vendor/bin/hw/wv_factory_extraction_tool
3. Restart Widevine service on the device to ensure a clean state
before running the tool:
- adb shell pkill -f -9 widevine
4. Run the wv_factory_extractor tool on the target device. By default,
the tool prints the CSR in JSON format directly to the console.
- adb shell /vendor/bin/hw/wv_factory_extraction_tool json_csr
or just
- adb shell /vendor/bin/hw/wv_factory_extraction_tool
For additional options, run the tool with the `help` argument:
- adb shell /vendor/bin/hw/wv_factory_extraction_tool help
## Uploading instructions:
1. Save the extracted CSR to `csr.json`.
2. Upload the `csr.json` file using `common/wv_upload_tool.py`:
python3 wv_upload_tool.py --credentials=cred.json --org-name={your organization name} --json-csr=csr.json
- Replace cred.json with the path to your OAuth 2.0 client credentials file.
You can obtain this file through the Google Cloud Platform.
- Replace {your organization name} with the name of your organization.

View File

@@ -0,0 +1,259 @@
//
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
#define LOG_TAG "wv_factory_extraction_tool"
#include <cppbor.h>
#include <cppbor_parse.h>
#include <sys/random.h>
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
#include "BccParser.h"
#include "WidevineProvisioner.h"
#include "log.h"
#include "properties.h"
constexpr size_t kChallengeSize = 64;
// The Google root key for the Endpoint Encryption Key chain, encoded as
// COSE_Sign1
inline constexpr uint8_t kCoseEncodedRootCert[] = {
0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x2a, 0xa4, 0x01, 0x01, 0x03,
0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x99, 0xb9, 0xee, 0xdd, 0x5e, 0xe4,
0x52, 0xf6, 0x85, 0xc6, 0x4c, 0x62, 0xdc, 0x3e, 0x61, 0xab, 0x57, 0x48,
0x7d, 0x75, 0x37, 0x29, 0xad, 0x76, 0x80, 0x32, 0xd2, 0xb3, 0xcb, 0x63,
0x58, 0xd9, 0x58, 0x40, 0x1e, 0x22, 0x08, 0x4b, 0xa4, 0xb7, 0xa4, 0xc8,
0xd7, 0x4e, 0x03, 0x0e, 0xfe, 0xb8, 0xaf, 0x14, 0x4c, 0xa7, 0x3b, 0x6f,
0xa5, 0xcd, 0xdc, 0xda, 0x79, 0xc6, 0x2b, 0x64, 0xfe, 0x99, 0x39, 0xaf,
0x76, 0xe7, 0x80, 0xfa, 0x66, 0x00, 0x85, 0x0d, 0x07, 0x98, 0x2a, 0xac,
0x91, 0x5c, 0xa7, 0x25, 0x14, 0x49, 0x06, 0x34, 0x75, 0xca, 0x8a, 0x27,
0x7a, 0xd9, 0xe3, 0x5a, 0x49, 0xeb, 0x02, 0x03};
// The Google Endpoint Encryption Key certificate, encoded as COSE_Sign1
inline constexpr uint8_t kCoseEncodedGeekCert[] = {
0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x4e, 0xa5, 0x01, 0x01, 0x02,
0x58, 0x20, 0xd0, 0xae, 0xc1, 0x15, 0xca, 0x2a, 0xcf, 0x73, 0xae, 0x6b,
0xcc, 0xcb, 0xd1, 0x96, 0x1d, 0x65, 0xe8, 0xb1, 0xdd, 0xd7, 0x4a, 0x1a,
0x37, 0xb9, 0x43, 0x3a, 0x97, 0xd5, 0x99, 0xdf, 0x98, 0x08, 0x03, 0x38,
0x18, 0x20, 0x04, 0x21, 0x58, 0x20, 0xbe, 0x85, 0xe7, 0x46, 0xc4, 0xa3,
0x42, 0x5a, 0x40, 0xd9, 0x36, 0x3a, 0xa6, 0x15, 0xd0, 0x2c, 0x58, 0x7e,
0x3d, 0xdc, 0x33, 0x02, 0x32, 0xd2, 0xfc, 0x5e, 0x1e, 0x87, 0x25, 0x5f,
0x72, 0x60, 0x58, 0x40, 0x9b, 0xcf, 0x90, 0xe2, 0x2e, 0x4b, 0xab, 0xd1,
0x18, 0xb1, 0x0e, 0x8e, 0x5d, 0x20, 0x27, 0x4b, 0x84, 0x58, 0xfe, 0xfc,
0x32, 0x90, 0x7e, 0x72, 0x05, 0x83, 0xbc, 0xd7, 0x82, 0xbe, 0xfa, 0x64,
0x78, 0x2d, 0x54, 0x10, 0x4b, 0xc0, 0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35,
0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2, 0xf5, 0x4f, 0xa5, 0xd4,
0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04};
std::vector<uint8_t> generateChallenge() {
std::vector<uint8_t> challenge(kChallengeSize);
ssize_t bytesRemaining = static_cast<ssize_t>(challenge.size());
uint8_t* writePtr = challenge.data();
while (bytesRemaining > 0) {
int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
if (bytesRead < 0) {
if (errno == EINTR) {
continue;
} else {
std::cerr << errno << ": " << strerror(errno) << std::endl;
exit(-1);
}
}
bytesRemaining -= bytesRead;
writePtr += bytesRead;
}
return challenge;
}
std::vector<uint8_t> getEekChain() {
cppbor::Array chain;
chain.add(cppbor::EncodedItem(std::vector<uint8_t>(
std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert))));
chain.add(cppbor::EncodedItem(std::vector<uint8_t>(
std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert))));
return chain.encode();
}
cppbor::Array composeCertificateRequest(
const std::vector<uint8_t>& protectedData,
const std::vector<uint8_t>& verifiedDeviceInfo,
const std::vector<uint8_t>& challenge) {
cppbor::Array macedKeysToSign =
cppbor::Array()
.add(std::vector<uint8_t>(0)) // empty protected headers as bstr
.add(cppbor::Map()) // empty unprotected headers
.add(cppbor::Null()) // nil for the payload
.add(std::vector<uint8_t>(0)); // MAC as returned from the HAL
cppbor::Array deviceInfo = cppbor::Array()
.add(cppbor::EncodedItem(verifiedDeviceInfo))
.add(cppbor::Map()); // Empty device info
cppbor::Array certificateRequest =
cppbor::Array()
.add(std::move(deviceInfo))
.add(challenge)
.add(cppbor::EncodedItem(protectedData))
.add(std::move(macedKeysToSign));
return certificateRequest;
}
cppbor::Array getCsr(widevine::WidevineProvisioner& provisioner) {
const std::vector<uint8_t> challenge = generateChallenge();
std::vector<uint8_t> verifiedDeviceInfo;
std::vector<uint8_t> protectedData;
if (!provisioner.GenerateCertificateRequest(
false, getEekChain(), verifiedDeviceInfo, protectedData)) {
std::cerr << "Failed to generate certificate request." << std::endl;
exit(-1);
}
auto csr =
composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge);
return csr;
}
std::unique_ptr<cppbor::Array> composeCertificateRequestV3(
const std::vector<uint8_t>& csr) {
auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
if (!parsedCsr) {
LOGE("Failed to parse input CSR.");
return nullptr;
}
if (!parsedCsr->asArray()) {
LOGE("Input CSR is not a CBOR array.");
return nullptr;
}
std::string fingerPrint;
if (!wvcdm::Properties::GetBuildInfo(&fingerPrint)) {
LOGE("Failed to get finger print.");
return nullptr;
}
cppbor::Map unverifiedDeviceInfo =
cppbor::Map().add("fingerprint", cppbor::Tstr(fingerPrint));
parsedCsr->asArray()->add(std::move(unverifiedDeviceInfo));
return std::unique_ptr<cppbor::Array>(parsedCsr.release()->asArray());
}
std::unique_ptr<cppbor::Array> getCsrV3(
widevine::WidevineProvisioner& provisioner) {
const std::vector<uint8_t> challenge = generateChallenge();
std::vector<uint8_t> csr;
if (!provisioner.GenerateCertificateRequestV2(challenge, &csr)) {
std::cerr << "Failed to generate certificate request v2." << std::endl;
exit(-1);
}
return composeCertificateRequestV3(csr);
}
void printHelp(const char* tool_name) {
fprintf(stdout, "Widevine Factory Extraction Tool for AOSP\n\n");
fprintf(stdout, "Usage: %s [command]\n\n", tool_name);
fprintf(stdout,
"This tool extracts BCC and device information and generates CSR "
"required for Widevine Provisioning 4.0 factory uploading.\n\n");
fprintf(stdout, "Commands:\n");
fprintf(stdout, " json_csr (default)\n");
fprintf(stdout,
" Generates and prints a JSON-formatted "
"Certificate Signing\n");
fprintf(stdout,
" Request (CSR) to be uploaded to the "
"Widevine provisioning server.\n\n");
fprintf(stdout, " bcc\n");
fprintf(stdout,
" Outputs the raw binary Bootloader "
"Certificate Chain (BCC).\n\n");
fprintf(stdout, " bcc_str\n");
fprintf(stdout,
" Outputs a human-readable, parsed string "
"representation of the BCC.\n\n");
fprintf(stdout, " device_info\n");
fprintf(
stdout,
" Outputs the raw binary device information blob.\n\n");
fprintf(stdout, " csr\n");
fprintf(stdout,
" Generates and outputs a legacy format Certificate "
"Signing Request (CSR) to be uploaded to RKP backend.\n\n");
fprintf(stdout, " csr_v3\n");
fprintf(stdout,
" Generates and outputs a V3 format Certificate "
"Signing Request (CSR) to be uploaded to RKP backend.\n\n");
fprintf(stdout, " help\n");
fprintf(stdout, " Displays this help message.\n");
}
int main(int argc, char** argv) {
widevine::WidevineProvisioner provisioner;
// Default to Widevine uploading request format "json_csr" if no arguments are
// provided.
const char* command = (argc > 1) ? argv[1] : "json_csr";
if (!std::strcmp(command, "json_csr")) {
std::string request;
if (provisioner.GenerateWidevineUploadRequest(request)) {
std::copy(request.begin(), request.end(),
std::ostream_iterator<char>(std::cout));
} else {
fprintf(stderr,
"Failed to generate Widevine uploading request json CSR.\n");
return 1;
}
} else if (!std::strcmp(command, "help")) {
printHelp(argv[0]);
} else if (!std::strcmp(command, "bcc")) {
auto bcc = provisioner.GetBcc();
fwrite(bcc.data(), 1, bcc.size(), stdout);
fflush(stdout);
} else if (!std::strcmp(command, "bcc_str")) {
auto bcc = provisioner.GetBcc();
widevine::BccParser bcc_parser;
std::string parsed_bcc = bcc_parser.Parse(bcc);
std::copy(parsed_bcc.begin(), parsed_bcc.end(),
std::ostream_iterator<char>(std::cout));
} else if (!std::strcmp(command, "device_info")) {
std::vector<uint8_t> deviceInfo;
if (provisioner.GetDeviceInfo(deviceInfo)) {
fwrite(deviceInfo.data(), 1, deviceInfo.size(), stdout);
fflush(stdout);
} else {
fprintf(stderr, "Failed to get device info.\n");
return 1;
}
} else if (!std::strcmp(command, "csr")) {
auto csr = getCsr(provisioner);
auto bytes = csr.encode();
std::copy(bytes.begin(), bytes.end(),
std::ostream_iterator<char>(std::cout));
} else if (!std::strcmp(argv[1], "csr_v3")) {
auto csr = getCsrV3(provisioner);
if (csr != nullptr) {
auto bytes = csr->encode();
std::copy(bytes.begin(), bytes.end(),
std::ostream_iterator<char>(std::cout));
} else {
fprintf(stderr, "Failed to generate CSR V3.\n");
return 1;
}
} else {
fprintf(stderr, "Error: Unknown command '%s'\n\n", command);
printHelp(argv[0]);
return 1;
}
return 0;
}

View File

@@ -0,0 +1,49 @@
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef BCC_PARSER_H_
#define BCC_PARSER_H_
#include <cppbor.h>
#include <stddef.h>
#include <stdint.h>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace widevine {
// BccParser processes a Provisioning 4.0 device root of trust. It extracts
// relevant pieces of information and outputs to std::string.
// Relevant documents:
// Android definition: go/remote-provisioning-hal#bcc.
// Google Dice Profile: go/dice-profile
class BccParser {
public:
explicit BccParser() {}
virtual ~BccParser() = default;
BccParser(const BccParser&) = delete;
BccParser& operator=(const BccParser&) = delete;
// Parse and verify a client generated root of trust. This message is part of
// an attestation model conforming to the Google Open Dice Profile. This
// message is received from a client device to attest it is a valid Widevine
// device.
virtual std::string Parse(const std::vector<uint8_t>& bcc);
private:
// Process and print CoseKey PubKeyEd25519 / PubKeyECDSA256.
bool ProcessDevicePublicKeyInfo(std::stringstream& ss,
const cppbor::Map& public_key_info_map);
// Process and print the DiceChainEntryPayload, which contains subject public
// key.
bool ProcessDiceChainEntryPayload(std::stringstream& ss,
std::string& payload);
};
} // namespace widevine
#endif // BCC_PARSER_H_

View File

@@ -0,0 +1,54 @@
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef DICE_CBOR_CONSTANTS_H_
#define DICE_CBOR_CONSTANTS_H_
namespace widevine {
// The BCC is encoded using RFC 8949- Concise Binary Object Representation
// (CBOR).
// The full definition of the following enums can be found here:
// go/remote-provisioning-hal#bcc.
// The device key is encoded in a cbor map. The key values are a mix of
// positive and negative integer values.
enum {
MAP_KEY_DEVICE_KEY_TYPE = 1,
MAP_KEY_DEVICE_KEY_ALGORITHM = 3,
MAP_KEY_DEVICE_KEY_OPS = 4,
MAP_KEY_DEVICE_KEY_CURVE = -1,
MAP_KEY_DEVICE_KEY_BYTES_0 = -2,
MAP_KEY_DEVICE_KEY_BYTES_1 = -3,
};
// The device key may be encoded in the BCC as either X,Y elliptic curve
// coordinates, or as raw bytes. The value is identified using
// MAP_KEY_DEVICE_KEY_TYPE.
enum {
DEVICE_KEY_ENCODING_UNKNOWN = 0,
DEVICE_KEY_BYTE_STRING = 1,
DEVICE_KEY_OCTET_PAIR = 2,
};
// Android/Widevine Dice Attestation allows two signing models. This is
// identified using MAP_KEY_DEVICE_KEY_ALGORITHM.
enum {
DEVICE_KEY_ALGORITHM_ES256 = -7, // EC key with SHA-256
DEVICE_KEY_ALGORITHM_EDDSA = -8, // Pure ED25519.
DEVICE_KEY_ALGORITHM_ES384 = -35, // EC key with SHA-384
};
// The curve used to generate the device public key is identified using the
// MAP_KEY_DEVICE_KEY_CURVE.
enum {
DEVICE_KEY_CURVE_P256 = 1,
DEVICE_KEY_CURVE_P384 = 2,
DEVICE_KEY_CURVE_ED25519 = 6,
};
} // namespace widevine
#endif // DICE_CBOR_CONSTANTS_H_

View File

@@ -0,0 +1,62 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WIDEVINE_PROVISIONER_H_
#define WIDEVINE_PROVISIONER_H_
#include <cppbor.h>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "WidevineOemcryptoInterface.h"
namespace widevine {
class WidevineProvisioner {
public:
WidevineProvisioner();
WidevineProvisioner(const WidevineProvisioner&) = delete;
WidevineProvisioner& operator=(const WidevineProvisioner&) = delete;
virtual ~WidevineProvisioner() = default;
std::vector<uint8_t> GetBcc();
bool GenerateCertificateRequest(
bool testMode, const std::vector<uint8_t>& endpointEncCertChain,
std::vector<uint8_t>& deviceInfo, std::vector<uint8_t>& protectedData);
bool GenerateCertificateRequestV2(const std::vector<uint8_t>& challenge,
std::vector<uint8_t>* csr);
bool GetDeviceInfo(std::vector<uint8_t>& device_info);
bool GenerateWidevineUploadRequest(std::string& request);
private:
bool GenerateProtectedData(
bool test_mode,
const std::vector<uint8_t>& endpoint_encryption_cert_chain,
std::vector<uint8_t> bcc, std::vector<uint8_t>& protected_data) const;
bool ValidateAndExtractEekPubAndId(
bool test_mode,
const std::vector<uint8_t>& endpoint_encryption_cert_chain,
std::vector<uint8_t>* eek_pub, std::vector<uint8_t>* eek_id) const;
cppbor::Array BuildCertReqRecipients(const std::vector<uint8_t>& pubkey,
const std::vector<uint8_t>& kid) const;
void InitializeCryptoInterface();
bool GetDeviceInfoCommon(cppbor::Map& device_info_map);
bool TryAddVerifiedDeviceInfo(cppbor::Map& device_info_map);
bool GetDeviceInfoV2(cppbor::Map& device_info_map);
void PopulateDeviceInfoFromCborMap(
const cppbor::Map& device_info_map,
std::map<std::string, std::string>& request_map);
std::unique_ptr<OEMCryptoInterface> crypto_interface_;
};
} // namespace widevine
#endif // WIDEVINE_PROVISIONER_H_

View File

@@ -0,0 +1,373 @@
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "BccParser.h"
#include <cppbor.h>
#include <cppbor_parse.h>
#include <stddef.h>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "DiceCborConstants.h"
namespace widevine {
namespace {
// Sized to hold a P256 public key uncompressed point compatible with X9.62.
// The key is formatted in an Z/X/Y format in which Z == 0x04 and X and Y are
// the public key coordinates. X and Y are each 32 bytes.
constexpr int kP256KeyCoordinateSizeBytes = 256 / 8;
// Sized to hold a P384 public key uncompressed point compatible with X9.62.
// The key is formatted in an Z/X/Y format in which Z == 0x04 and X and Y are
// the public key coordinates. X and Y are each 48 bytes.
constexpr int kP384KeyCoordinateSizeBytes = 384 / 8;
constexpr int kMarshaledP384KeySizeBytes = kP384KeyCoordinateSizeBytes * 2 + 1;
constexpr int kMaxMarshaledECKeySizeBytes = kMarshaledP384KeySizeBytes;
constexpr char kMarshaledECKeyZValue = 0x04;
constexpr int kED25519KeyDataItemSizeBytes = 32;
// The Issuer field key in BccEntryPayload.
constexpr int64_t kIssuer = 1;
// The Subject field key in BccEntryPayload.
constexpr int64_t kSubject = 2;
// The SubjectPublicKey field key in BccEntryPayload.
constexpr int64_t kSubjectPublicKey = -4670552;
std::string TypeNameFromType(cppbor::MajorType type) {
switch (type) {
case cppbor::UINT:
return "UINT";
case cppbor::NINT:
return "NINT";
case cppbor::BSTR:
return "BSTR";
case cppbor::TSTR:
return "TSTR";
case cppbor::ARRAY:
return "ARRAY";
case cppbor::MAP:
return "MAP";
case cppbor::SEMANTIC:
return "SEMANTIC";
case cppbor::SIMPLE:
return "SIMPLE";
default:
return "undefined type";
}
}
std::string GetIssuerSubjectFromBccEntryPayload(
const cppbor::Map* bcc_entry_payload) {
std::string issuer = "Issuer: ";
std::string subject = "Subject: ";
for (size_t i = 0; i < bcc_entry_payload->size(); ++i) {
const auto& entry = (*bcc_entry_payload)[i];
if (entry.first == nullptr || entry.first->asInt() == nullptr ||
entry.second == nullptr) {
continue;
}
if (entry.first->asInt()->value() == kIssuer) {
issuer += (entry.second->asTstr()->value() + "\n");
} else if (entry.first->asInt()->value() == kSubject) {
subject += (entry.second->asTstr()->value() + "\n");
}
}
return issuer + subject;
}
const cppbor::Bstr* GetSubjectPublicKeyFromBccEntryPayload(
const cppbor::Map* bcc_entry_payload) {
for (size_t i = 0; i < bcc_entry_payload->size(); ++i) {
const auto& entry = (*bcc_entry_payload)[i];
if (entry.first == nullptr || entry.first->asInt() == nullptr ||
entry.second == nullptr) {
continue;
}
if (entry.first->asInt()->value() == kSubjectPublicKey) {
return entry.second->asBstr();
}
}
return nullptr;
}
} // namespace
// BCC/DiceCertChain definition:
// https://source.corp.google.com/android-internal/hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
std::string BccParser::Parse(const std::vector<uint8_t>& bcc) {
std::stringstream ss;
auto [parsed_bcc, _, err_msg] = cppbor::parse(bcc);
if (parsed_bcc == nullptr) {
ss << "Failed to parse input BCC: " << err_msg.c_str() << "\n";
return ss.str();
}
if (parsed_bcc->asArray() == nullptr) {
ss << "Input BCC is not a CBOR array: "
<< TypeNameFromType(parsed_bcc->type()) << "\n";
return ss.str();
}
const cppbor::Array* bcc_array = parsed_bcc->asArray();
if (bcc_array->size() < 2) {
ss << "Input BCC should contain at least two elements, actual: "
<< bcc_array->size() << "\n";
return ss.str();
}
// The first element in the array contains the root device public key
// definition.
ss << "ROOT DEVICE PUBLIC KEY: \n";
const cppbor::Map* device_public_key_info = (*bcc_array)[0]->asMap();
if (device_public_key_info == nullptr) {
ss << "Invalid dice cert chain type for the first element"
<< "\n";
return ss.str();
}
if (!ProcessDevicePublicKeyInfo(ss, *device_public_key_info)) {
return ss.str();
}
// Parse each certificate in the chain. The structure of thr entries are
// COSE_Sign1 (untagged).
for (size_t i = 1; i < bcc_array->size(); ++i) {
ss << "\nCDI PUBLIC KEY " << i << ": \n";
const cppbor::Array* bcc_entry = (*bcc_array)[i]->asArray();
if (bcc_entry == nullptr) {
ss << "Invalid dice cert chain type"
<< "\n";
return ss.str();
}
// Skip CoseSign1 signature verification here, only extract pub keys
if (bcc_entry->size() != 4 || (*bcc_entry)[0]->type() != cppbor::BSTR ||
(*bcc_entry)[1]->type() != cppbor::MAP ||
(*bcc_entry)[2]->type() != cppbor::BSTR ||
(*bcc_entry)[3]->type() != cppbor::BSTR) {
ss << "Invalid signature array"
<< "\n";
return ss.str();
}
const std::vector<uint8_t>& key_payload =
(*bcc_entry)[2]->asBstr()->value();
std::string payload(key_payload.begin(), key_payload.end());
if (!ProcessDiceChainEntryPayload(ss, payload)) {
return ss.str();
}
}
return ss.str();
}
bool BccParser::ProcessDevicePublicKeyInfo(
std::stringstream& ss, const cppbor::Map& public_key_info_map) {
int key_encoding_format = DEVICE_KEY_ENCODING_UNKNOWN;
std::string device_key_bytes_0;
std::string device_key_bytes_1;
for (size_t index = 0; index < public_key_info_map.size(); ++index) {
std::pair<const std::unique_ptr<cppbor::Item>&,
const std::unique_ptr<cppbor::Item>&>
entry = public_key_info_map[index];
if (entry.first->type() != cppbor::NINT &&
entry.first->type() != cppbor::UINT) {
ss << "Invalid map key type " << TypeNameFromType(entry.first->type())
<< " in device key info"
<< "\n";
return false;
}
int map_key = entry.first->type() == cppbor::NINT
? entry.first->asNint()->value()
: entry.first->asInt()->value();
switch (map_key) {
case MAP_KEY_DEVICE_KEY_TYPE: {
if (entry.second->type() != cppbor::UINT) {
ss << "Invalid map value type "
<< TypeNameFromType(entry.second->type()) << " for device key"
<< "\n";
return false;
}
ss << "key encoding format: ";
int64_t value = entry.second->asUint()->value();
switch (value) {
case DEVICE_KEY_OCTET_PAIR:
key_encoding_format = DEVICE_KEY_OCTET_PAIR;
ss << "DEVICE_KEY_OCTET_PAIR"
<< "\n";
break;
case DEVICE_KEY_BYTE_STRING:
key_encoding_format = DEVICE_KEY_BYTE_STRING;
ss << "DEVICE_KEY_BYTE_STRING"
<< "\n";
break;
default:
ss << "Unhandled cbor value for device key format: " << value
<< "\n";
return false;
}
} break;
case MAP_KEY_DEVICE_KEY_ALGORITHM: {
if (entry.second->type() != cppbor::NINT) {
ss << "Invalid map value type "
<< TypeNameFromType(entry.second->type())
<< " for device key algorithm"
<< "\n";
return false;
}
ss << "key algorithm type: ";
int64_t value = entry.second->asNint()->value();
switch (value) {
case DEVICE_KEY_ALGORITHM_ES256:
ss << "ECDSA_SHA256";
break;
case DEVICE_KEY_ALGORITHM_ES384:
ss << "ECDSA_SHA384";
break;
case DEVICE_KEY_ALGORITHM_EDDSA:
ss << "EDDSA";
break;
}
ss << "\n";
} break;
case MAP_KEY_DEVICE_KEY_OPS:
// The OPS is an array. Ignored for now.
break;
case MAP_KEY_DEVICE_KEY_CURVE: {
if (entry.second->type() != cppbor::UINT) {
ss << "Invalid map value type "
<< TypeNameFromType(entry.second->type())
<< " for device key curve"
<< "\n";
return false;
}
ss << "curve: ";
int64_t value = entry.second->asUint()->value();
switch (value) {
case DEVICE_KEY_CURVE_ED25519:
ss << "ED25519";
break;
case DEVICE_KEY_CURVE_P256:
ss << "P256";
break;
case DEVICE_KEY_CURVE_P384:
ss << "P384";
break;
default:
ss << "Invalid map value " << value << " for device key curve"
<< "\n";
return false;
}
ss << "\n";
} break;
case MAP_KEY_DEVICE_KEY_BYTES_0:
case MAP_KEY_DEVICE_KEY_BYTES_1:
// BCC encodes keys as either two X, Y octet strings or a single
// octet string. The format used depends on the key type.
if (entry.second->type() != cppbor::BSTR) {
ss << "Unexpected cbor type for device key bytes"
<< "\n";
return false;
}
const std::vector<uint8_t>& key_bytes = entry.second->asBstr()->value();
// Key byte length depends upon the key type.
if (key_bytes.size() != kED25519KeyDataItemSizeBytes &&
key_bytes.size() != kP256KeyCoordinateSizeBytes &&
key_bytes.size() != kP384KeyCoordinateSizeBytes) {
ss << "Malformed device public key data size of" << key_bytes.size()
<< "\n";
return false;
}
std::string& key_bytes_str = map_key == MAP_KEY_DEVICE_KEY_BYTES_0
? device_key_bytes_0
: device_key_bytes_1;
key_bytes_str.assign(key_bytes.begin(), key_bytes.end());
}
}
if (device_key_bytes_0.empty() ||
(key_encoding_format == DEVICE_KEY_OCTET_PAIR &&
device_key_bytes_1.empty())) {
ss << "Malformed device public key definition. Missing device public key "
"bytes"
<< "\n";
return false;
}
std::string device_key_bytes;
if (key_encoding_format == DEVICE_KEY_OCTET_PAIR) {
// Key is an ECDSA elliptic key. We need to return the ANSI X9.62
// marshaled public key. Generate the marshaled key if needed. The
// marshaled key is needed to create an ECPublicKey object.
// std::string* device_key_bytes = public_key_info.mutable_key_bytes();
device_key_bytes.reserve(kMaxMarshaledECKeySizeBytes);
device_key_bytes.resize(1);
device_key_bytes[0] = kMarshaledECKeyZValue;
device_key_bytes.append(device_key_bytes_0);
device_key_bytes.append(device_key_bytes_1);
} else {
device_key_bytes = device_key_bytes_0;
}
std::ostringstream ss_hex;
ss_hex << std::setfill('0');
for (size_t i = 0; i < device_key_bytes.size(); i++) {
ss_hex << std::setw(2) << std::hex << static_cast<int>(device_key_bytes[i]);
}
ss << "public key bytes: " << ss_hex.str() << "\n";
return true;
}
bool BccParser::ProcessDiceChainEntryPayload(std::stringstream& ss,
std::string& payload) {
if (payload.empty()) {
ss << "Empty bcc entry payload"
<< "\n";
return false;
}
auto parse_result = cppbor::parse(
reinterpret_cast<const uint8_t*>(payload.data()),
reinterpret_cast<const uint8_t*>(payload.data() + payload.size()));
std::unique_ptr<cppbor::Item> item = std::move(std::get<0>(parse_result));
std::string error_message = std::get<2>(parse_result);
if (item == nullptr || !error_message.empty()) {
ss << "Unable to parse bcc entry payload: " << error_message << "\n";
return false;
}
if (item->type() != cppbor::MAP) {
ss << "Unexpected bcc entry payload type"
<< "\n";
return false;
}
const std::string issuer_subject =
GetIssuerSubjectFromBccEntryPayload(item->asMap());
ss << issuer_subject;
const cppbor::Bstr* subject_public_key =
GetSubjectPublicKeyFromBccEntryPayload(item->asMap());
if (subject_public_key == nullptr) {
ss << "Bcc entry payload has no subject public key"
<< "\n";
return false;
}
// Now parse the serialized subject public key.
parse_result = cppbor::parse(
subject_public_key->value().data(),
subject_public_key->value().data() + subject_public_key->value().size());
item = std::move(std::get<0>(parse_result));
error_message = std::get<2>(parse_result);
if (item == nullptr || !error_message.empty()) {
ss << "Unable to parse serialized subject public key: " << error_message
<< "\n";
return false;
}
const cppbor::Map* subject_public_key_info = item->asMap();
if (subject_public_key_info == nullptr) {
ss << "Invalid subject public key type"
<< "\n";
return false;
}
return ProcessDevicePublicKeyInfo(ss, *subject_public_key_info);
}
} // namespace widevine

View File

@@ -0,0 +1,498 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "WidevineProvisioner.h"
#include <cppbor.h>
#include <cppbor_parse.h>
#include <keymaster/cppcose/cppcose.h>
#include <openssl/bn.h>
#include <openssl/curve25519.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <openssl/rand.h>
#include <string.h>
#include <memory>
#include <string>
#include <utility>
#include "WidevineOemcryptoInterface.h"
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
namespace widevine {
namespace {
const std::vector<std::vector<uint8_t>> kAuthorizedEekRoots = {
{0x99, 0xB9, 0xEE, 0xDD, 0x5E, 0xE4, 0x52, 0xF6, 0x85, 0xC6, 0x4C,
0x62, 0xDC, 0x3E, 0x61, 0xAB, 0x57, 0x48, 0x7D, 0x75, 0x37, 0x29,
0xAD, 0x76, 0x80, 0x32, 0xD2, 0xB3, 0xCB, 0x63, 0x58, 0xD9},
};
std::string EscapeJson(const std::string& input) {
std::string result;
for (std::string::const_iterator c = input.begin(); c != input.end(); ++c) {
switch (*c) {
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
default:
result += *c;
break;
}
}
return result;
}
std::string StringMapToJson(
const std::map<std::string, std::string>& string_map) {
std::string json = "{";
for (const auto& value_pair : string_map) {
json.append("\"" + value_pair.first + "\": " + "\"" + value_pair.second +
"\",");
}
json.resize(json.size() - 1); // Remove the last comma.
json.append("}");
return json;
}
} // namespace
WidevineProvisioner::WidevineProvisioner() {
InitializeCryptoInterface();
assert(crypto_interface_ != nullptr);
}
std::vector<uint8_t> WidevineProvisioner::GetBcc() {
std::vector<uint8_t> bcc;
OEMCryptoResult result = crypto_interface_->GetBcc(bcc);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get BCC, result = %d", result);
}
return bcc;
}
bool WidevineProvisioner::GenerateCertificateRequest(
bool testMode, const std::vector<uint8_t>& endpointEncCertChain,
std::vector<uint8_t>& deviceInfo, std::vector<uint8_t>& protectedData) {
if (!GetDeviceInfo(deviceInfo)) {
LOGE("Failed to get device_info.");
return false;
}
std::vector<uint8_t> bcc;
OEMCryptoResult result = crypto_interface_->GetBcc(bcc);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get BCC, result = %d", result);
return false;
}
if (!GenerateProtectedData(testMode, endpointEncCertChain, bcc,
protectedData)) {
LOGE("Failed to generate protected data.");
return false;
}
return true;
}
bool WidevineProvisioner::TryAddVerifiedDeviceInfo(
cppbor::Map& device_info_map) {
std::vector<uint8_t> verified_device_info;
OEMCryptoResult result =
crypto_interface_->GetVerifiedDeviceInformation(verified_device_info);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
// OEMCrypto v17 and earlier doesn't support GetDeviceInformation()
LOGI("OEMCrypto_GetDeviceInformation is not implemented.");
return true;
}
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get verified device information, result = %d", result);
return false;
}
auto [parsed, _, err] = cppbor::parse(
reinterpret_cast<const uint8_t*>(verified_device_info.data()),
verified_device_info.size());
if (!parsed || !parsed->asMap()) {
LOGE("Failed to parse the verified device info cbor: %s", err.c_str());
return false;
}
const cppbor::Map* verified_device_info_map = parsed->asMap();
for (size_t i = 0; i < verified_device_info_map->size(); i++) {
auto& [key_item, value_item] = (*verified_device_info_map)[i];
LOGI("Found device info %s", key_item->asTstr()->value().data());
if (value_item != nullptr && value_item->asTstr() != nullptr &&
value_item->asTstr()->value().empty()) {
LOGI("Value is empty. Skip");
continue;
}
device_info_map.add(key_item->clone(), value_item->clone());
}
return true;
}
bool WidevineProvisioner::GetDeviceInfoCommon(cppbor::Map& device_info_map) {
// Best effort to populate device info from TEE first
TryAddVerifiedDeviceInfo(device_info_map);
// Add device information from OS properties if the verified device info is
// not present
if (device_info_map.get("brand") == nullptr ||
device_info_map.get("brand")->asTstr()->value().empty()) {
std::string brand_name;
if (!wvcdm::Properties::GetBrandName(&brand_name) || brand_name.empty()) {
LOGE("Failed to get brand name.");
return false;
}
device_info_map.add(cppbor::Tstr("brand"), cppbor::Tstr(brand_name));
LOGI("use OS property brand: %s", brand_name.data());
} else {
LOGI("use verified brand: %s",
device_info_map.get("brand")->asTstr()->value().data());
}
if (device_info_map.get("manufacturer") == nullptr ||
device_info_map.get("manufacturer")->asTstr()->value().empty()) {
std::string company_name;
if (!wvcdm::Properties::GetCompanyName(&company_name) ||
company_name.empty()) {
LOGE("Failed to get company name.");
return false;
}
device_info_map.add(cppbor::Tstr("manufacturer"),
cppbor::Tstr(company_name));
LOGI("use OS property manufacturer: %s", company_name.data());
} else {
LOGI("use verified manufacture: %s",
device_info_map.get("manufacturer")->asTstr()->value().data());
}
if (device_info_map.get("model") == nullptr ||
device_info_map.get("model")->asTstr()->value().empty()) {
std::string model_name;
if (!wvcdm::Properties::GetModelName(&model_name) || model_name.empty()) {
LOGE("Failed to get model name.");
return false;
}
device_info_map.add(cppbor::Tstr("model"), cppbor::Tstr(model_name));
LOGI("use OS property model: %s", model_name.data());
} else {
LOGI("use verified model: %s",
device_info_map.get("model")->asTstr()->value().data());
}
if (device_info_map.get("device") == nullptr ||
device_info_map.get("device")->asTstr()->value().empty()) {
std::string device_name;
if (!wvcdm::Properties::GetDeviceName(&device_name) ||
device_name.empty()) {
LOGE("Failed to get device name.");
return false;
}
device_info_map.add(cppbor::Tstr("device"), cppbor::Tstr(device_name));
LOGI("use OS property device: %s", device_name.data());
} else {
LOGI("use verified device: %s",
device_info_map.get("device")->asTstr()->value().data());
}
if (device_info_map.get("product") == nullptr ||
device_info_map.get("product")->asTstr()->value().empty()) {
std::string product_name;
if (!wvcdm::Properties::GetProductName(&product_name) ||
product_name.empty()) {
LOGE("Failed to get product name.");
return false;
}
device_info_map.add(cppbor::Tstr("product"), cppbor::Tstr(product_name));
LOGI("use OS property product: %s", product_name.data());
} else {
LOGI("use verified product: %s",
device_info_map.get("product")->asTstr()->value().data());
}
std::string arch_name;
if (!wvcdm::Properties::GetArchitectureName(&arch_name) ||
arch_name.empty()) {
LOGE("Failed to get architecture name.");
return false;
}
device_info_map.add(cppbor::Tstr("architecture"), cppbor::Tstr(arch_name));
std::string build_info;
if (!wvcdm::Properties::GetBuildInfo(&build_info) || build_info.empty()) {
LOGE("Failed to get build info.");
return false;
}
device_info_map.add(cppbor::Tstr("fingerprint"), cppbor::Tstr(build_info));
std::string oemcrypto_build_info;
OEMCryptoResult result =
crypto_interface_->GetOEMCryptoBuildInfo(oemcrypto_build_info);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get oemcrypto build info, result = %d", result);
return false;
}
device_info_map.add(cppbor::Tstr("oemcrypto_build_info"),
cppbor::Tstr(oemcrypto_build_info));
return true;
}
bool WidevineProvisioner::GetDeviceInfo(std::vector<uint8_t>& device_info) {
auto device_info_map = cppbor::Map();
device_info_map.add(cppbor::Tstr("type"), cppbor::Tstr("widevine"));
device_info_map.add(cppbor::Tstr("version"), cppbor::Uint(2));
if (!GetDeviceInfoCommon(device_info_map)) return false;
device_info = device_info_map.canonicalize().encode();
return true;
}
bool WidevineProvisioner::GenerateProtectedData(
bool test_mode, const std::vector<uint8_t>& endpoint_encryption_cert_chain,
std::vector<uint8_t> bcc, std::vector<uint8_t>& protected_data) const {
// Encrypt |signedMac| and |bcc_| with GEEK.
std::vector<uint8_t> eek_pub;
std::vector<uint8_t> eek_id;
if (!ValidateAndExtractEekPubAndId(test_mode, endpoint_encryption_cert_chain,
&eek_pub, &eek_id)) {
LOGE("Failed to validate and extract the endpoint encryption key.");
return false;
}
std::vector<uint8_t> ephemeralPrivKey(X25519_PRIVATE_KEY_LEN);
std::vector<uint8_t> ephemeralPubKey(X25519_PUBLIC_VALUE_LEN);
X25519_keypair(ephemeralPubKey.data(), ephemeralPrivKey.data());
auto sessionKey = cppcose::x25519_HKDF_DeriveKey(
ephemeralPubKey, ephemeralPrivKey, eek_pub, true /* senderIsA */);
if (!sessionKey) {
LOGE("Failed to derive the session key.");
return false;
}
// Generate 4 bytes of random data as IV
std::vector<uint8_t> iv(cppcose::kAesGcmNonceLength);
if (RAND_bytes(iv.data(), iv.size()) != 1) {
LOGE("Failed to generate a random nonce.");
return false;
}
auto bcc_parse_result = cppbor::parse(bcc.data(), bcc.size());
cppbor::Item* bcc_item = std::get<0>(bcc_parse_result).get();
auto coseEncrypted = cppcose::constructCoseEncrypt(
*sessionKey, iv,
cppbor::Array() // payload
.add(cppbor::Array()) // Empty signedMac
.add(std::move(bcc_item->clone()))
.encode(),
{}, // aad
BuildCertReqRecipients(ephemeralPubKey, eek_id));
if (!coseEncrypted) {
LOGE("Failed to construct a COSE_Encrypt ProtectedData structure");
return false;
}
protected_data = coseEncrypted->encode();
return true;
}
bool WidevineProvisioner::ValidateAndExtractEekPubAndId(
bool test_mode, const std::vector<uint8_t>& endpoint_encryption_cert_chain,
std::vector<uint8_t>* eek_pub, std::vector<uint8_t>* eek_id) const {
auto parse_result = cppbor::parse(endpoint_encryption_cert_chain);
auto item = std::move(std::get<0>(parse_result));
if (!item || !item->asArray()) {
LOGE("Error parsing EEK chain: %s", std::get<2>(parse_result).c_str());
return false;
}
const cppbor::Array* certArr = item->asArray();
std::vector<uint8_t> lastPubKey;
for (size_t i = 0; i < certArr->size(); ++i) {
auto cosePubKey = cppcose::verifyAndParseCoseSign1(
certArr->get(i)->asArray(), std::move(lastPubKey), {} /* AAD */);
if (!cosePubKey) {
LOGE("Failed to validate EEK chain: %s",
cosePubKey.moveMessage().c_str());
return false;
}
lastPubKey = *std::move(cosePubKey);
// In prod mode the first pubkey should match a well-known Google public
// key.
if (!test_mode && i == 0) {
auto parsedPubKey = cppcose::CoseKey::parse(lastPubKey);
if (!parsedPubKey) {
LOGE("%s", parsedPubKey.moveMessage().c_str());
return false;
}
auto rawPubKey = parsedPubKey->getBstrValue(cppcose::CoseKey::PUBKEY_X);
if (!rawPubKey) {
LOGE("Key is missing required label 'PUBKEY_X'");
return false;
}
std::vector<uint8_t> matcher(rawPubKey->data(),
rawPubKey->data() + rawPubKey->size());
if (std::find(std::begin(kAuthorizedEekRoots),
std::end(kAuthorizedEekRoots),
matcher) == std::end(kAuthorizedEekRoots)) {
LOGE("Unrecognized root of EEK chain");
return false;
}
}
}
auto eek = cppcose::CoseKey::parseX25519(lastPubKey, true /* requireKid */);
if (!eek) {
LOGE("Failed to get EEK: %s", eek.moveMessage().c_str());
return false;
}
*eek_pub = eek->getBstrValue(cppcose::CoseKey::PUBKEY_X).value();
*eek_id = eek->getBstrValue(cppcose::CoseKey::KEY_ID).value();
return true;
}
cppbor::Array WidevineProvisioner::BuildCertReqRecipients(
const std::vector<uint8_t>& pubkey, const std::vector<uint8_t>& kid) const {
return cppbor::Array() // Array of recipients
.add(cppbor::Array() // Recipient
.add(cppbor::Map() // Protected
.add(cppcose::ALGORITHM, cppcose::ECDH_ES_HKDF_256)
.canonicalize()
.encode())
.add(cppbor::Map() // Unprotected
.add(cppcose::COSE_KEY,
cppbor::Map()
.add(cppcose::CoseKey::KEY_TYPE,
cppcose::OCTET_KEY_PAIR)
.add(cppcose::CoseKey::CURVE, cppcose::X25519)
.add(cppcose::CoseKey::PUBKEY_X, pubkey)
.canonicalize())
.add(cppcose::KEY_ID, kid)
.canonicalize())
.add(cppbor::Null())); // No ciphertext
}
bool WidevineProvisioner::GetDeviceInfoV2(cppbor::Map& device_info_map) {
if (!GetDeviceInfoCommon(device_info_map)) return false;
device_info_map.canonicalize();
return true;
}
bool WidevineProvisioner::GenerateCertificateRequestV2(
const std::vector<uint8_t>& challenge, std::vector<uint8_t>* csr) {
if (csr == nullptr) {
LOGE("CSR is null.");
return false;
}
// Prepare BCC
std::vector<uint8_t> bcc;
OEMCryptoResult result = crypto_interface_->GetBcc(bcc);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get BCC, result = %d", result);
return false;
}
// Prepare device info
auto device_info_map = cppbor::Map();
if (!GetDeviceInfoV2(device_info_map)) {
LOGE("Failed to get device_info.");
return false;
}
// Prepare signed CSR payload
auto device_info = device_info_map.encode();
std::vector<uint8_t> signed_csr_payload;
result = crypto_interface_->GetSignedCsrPayload(challenge, device_info,
signed_csr_payload);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get the signed CSR payload, result = %d", result);
return false;
}
// https://source.corp.google.com/android-internal/hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
*csr = cppbor::Array()
.add(1 /* version */)
.add(cppbor::Map() /* UdsCerts */)
.add(cppbor::EncodedItem(std::move(bcc)))
.add(cppbor::EncodedItem(std::move(signed_csr_payload)))
.encode();
return true;
}
// Caller ensures the validity of `device_info_map` as a `cppbor::Map&`.
void WidevineProvisioner::PopulateDeviceInfoFromCborMap(
const cppbor::Map& device_info_map,
std::map<std::string, std::string>& request_map) {
if (device_info_map.get("manufacturer")) {
request_map["company"] =
device_info_map.get("manufacturer")->asTstr()->value();
}
if (device_info_map.get("device")) {
request_map["name"] = device_info_map.get("device")->asTstr()->value();
}
if (device_info_map.get("architecture")) {
request_map["architecture"] =
device_info_map.get("architecture")->asTstr()->value();
}
if (device_info_map.get("model")) {
request_map["model"] = device_info_map.get("model")->asTstr()->value();
}
if (device_info_map.get("product")) {
request_map["product"] = device_info_map.get("product")->asTstr()->value();
}
if (device_info_map.get("fingerprint")) {
request_map["build_info"] =
device_info_map.get("fingerprint")->asTstr()->value();
}
if (device_info_map.get("oemcrypto_build_info")) {
request_map["oemcrypto_build_info"] = EscapeJson(
device_info_map.get("oemcrypto_build_info")->asTstr()->value());
}
}
bool WidevineProvisioner::GenerateWidevineUploadRequest(std::string& request) {
std::map<std::string, std::string> request_map;
auto bcc = GetBcc();
request_map["bcc"] = wvutil::Base64Encode(bcc);
auto device_info_map = cppbor::Map();
if (!GetDeviceInfoCommon(device_info_map)) return false;
PopulateDeviceInfoFromCborMap(device_info_map, request_map);
request = StringMapToJson(request_map);
return true;
}
void WidevineProvisioner::InitializeCryptoInterface() {
std::string oemcrypto_path;
if (!wvcdm::Properties::GetOEMCryptoPath(&oemcrypto_path)) {
LOGE("Failed to get OEMCrypto path.");
}
LOGI("OEMCrypto path is %s", oemcrypto_path.c_str());
crypto_interface_ = std::make_unique<OEMCryptoInterface>();
if (!crypto_interface_->Init(oemcrypto_path)) {
LOGE("Failed to initialize OEMCrypto interface.");
crypto_interface_.reset(nullptr);
}
}
} // namespace widevine

View File

@@ -0,0 +1,91 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Log - implemented using the standard Android logging mechanism
/*
* Qutoing from system/core/include/log/log.h:
* Normally we strip ALOGV (VERBOSE messages) from release builds.
* You can modify this (for example with "#define LOG_NDEBUG 0"
* at the top of your source file) to change that behavior.
*/
#ifndef LOG_NDEBUG
# ifdef NDEBUG
# define LOG_NDEBUG 1
# else
# define LOG_NDEBUG 0
# endif
#endif
#define LOG_TAG "Widevine"
#define LOG_BUF_SIZE 1024
#include "log.h"
#include <stdarg.h>
#include <stdio.h>
#include <utils/Log.h>
#include <string>
/*
* Uncomment the line below if you want to have the LOGV messages to print
* IMPORTANT : this will affect all of CDM
*/
// #define LOG_NDEBUG 0
namespace wvutil {
LogPriority g_cutoff = CDM_LOG_INFO;
void InitLogging() {}
void Log(const char* file, const char* function, int line, LogPriority level,
const char* format, ...) {
if (level > g_cutoff) return;
const char* filename = strrchr(file, '/');
filename = filename == nullptr ? file : filename + 1;
char buf[LOG_BUF_SIZE];
int len =
snprintf(buf, LOG_BUF_SIZE, "[%s(%d):%s] ", filename, line, function);
if (len < 0) len = 0;
if (static_cast<unsigned int>(len) < sizeof(buf)) {
va_list ap;
va_start(ap, format);
vsnprintf(buf + len, LOG_BUF_SIZE - len, format, ap);
va_end(ap);
}
android_LogPriority prio = ANDROID_LOG_VERBOSE;
switch (level) {
case CDM_LOG_ERROR:
prio = ANDROID_LOG_ERROR;
break;
case CDM_LOG_WARN:
prio = ANDROID_LOG_WARN;
break;
case CDM_LOG_INFO:
prio = ANDROID_LOG_INFO;
break;
case CDM_LOG_DEBUG:
prio = ANDROID_LOG_DEBUG;
break;
#if LOG_NDEBUG
case CDM_LOG_VERBOSE:
return;
#else
case CDM_LOG_VERBOSE:
prio = ANDROID_LOG_VERBOSE;
break;
#endif
}
__android_log_write(prio, LOG_TAG, buf);
}
} // namespace wvutil

View File

@@ -0,0 +1,101 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <android-base/properties.h>
#include <unistd.h>
#include <sstream>
#include <string>
#include "log.h"
#include "properties.h"
namespace {
bool GetAndroidProperty(const char* key, std::string* value) {
if (!key) {
LOGW("GetAndroidProperty: Invalid property key parameter");
return false;
}
if (!value) {
LOGW("GetAndroidProperty: Invalid property value parameter");
return false;
}
*value = android::base::GetProperty(key, "");
return value->size() > 0;
}
} // namespace
namespace wvcdm {
bool Properties::GetBrandName(std::string* brand_name) {
if (!brand_name) {
LOGW("Properties::GetBrandName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.brand", brand_name);
}
bool Properties::GetCompanyName(std::string* company_name) {
if (!company_name) {
LOGW("Properties::GetCompanyName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.manufacturer", company_name);
}
bool Properties::GetModelName(std::string* model_name) {
if (!model_name) {
LOGW("Properties::GetModelName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.model", model_name);
}
bool Properties::GetArchitectureName(std::string* arch_name) {
if (!arch_name) {
LOGW("Properties::GetArchitectureName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.cpu.abi", arch_name);
}
bool Properties::GetDeviceName(std::string* device_name) {
if (!device_name) {
LOGW("Properties::GetDeviceName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.device", device_name);
}
bool Properties::GetProductName(std::string* product_name) {
if (!product_name) {
LOGW("Properties::GetProductName: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.product.name", product_name);
}
bool Properties::GetBuildInfo(std::string* build_info) {
if (!build_info) {
LOGW("Properties::GetBuildInfo: Invalid parameter");
return false;
}
return GetAndroidProperty("ro.build.fingerprint", build_info);
}
bool Properties::GetOEMCryptoPath(std::string* library_name) {
if (!library_name) {
LOGW("Properties::GetOEMCryptoPath: Invalid parameter");
return false;
}
*library_name = "liboemcrypto.so";
return true;
}
} // namespace wvcdm