diff --git a/libwvdrmengine/tools/factory_upload_tool/Android.bp b/libwvdrmengine/tools/factory_upload_tool/Android.bp new file mode 100644 index 00000000..62ba6160 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/Android.bp @@ -0,0 +1,42 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "vendor_widevine_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // legacy_by_exception_only (by exception only) + default_applicable_licenses: ["vendor_widevine_license"], +} + +cc_binary { + name: "wv_factory_extraction_tool", + relative_install_path: "hw", + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libcppbor_external", + "libcppcose_rkp", + "libcrypto", + "liblog", + "libutils", + ], + srcs: [ + "cli.cpp", + "src/log.cpp", + "src/properties_android.cpp", + "src/WidevineProvisioner.cpp", + "src/WidevineOemcryptoInterface.cpp", + ], + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/cdm/util/include", + ], + local_include_dirs: [ + "include", + ], +} + diff --git a/libwvdrmengine/tools/factory_upload_tool/cli.cpp b/libwvdrmengine/tools/factory_upload_tool/cli.cpp new file mode 100644 index 00000000..eeabacb9 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/cli.cpp @@ -0,0 +1,139 @@ +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "WidevineProvisioner.h" + +constexpr size_t kChallengeSize = 16; + +// 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 generateChallenge() { + std::vector challenge(kChallengeSize); + + ssize_t bytesRemaining = static_cast(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 getEekChain() { + cppbor::Array chain; + chain.add(cppbor::EncodedItem( + std::vector(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)))); + chain.add(cppbor::EncodedItem( + std::vector(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)))); + return chain.encode(); +} + +cppbor::Array composeCertificateRequest( + const std::vector& protectedData, + const std::vector& verifiedDeviceInfo, + const std::vector& challenge) { + cppbor::Array macedKeysToSign = cppbor::Array() + .add(std::vector(0)) // empty protected headers as bstr + .add(cppbor::Map()) // empty unprotected headers + .add(cppbor::Null()) // nil for the payload + .add(std::vector(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 challenge = generateChallenge(); + std::vector verifiedDeviceInfo; + std::vector 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; +} + +int main(int argc, char** argv) { + if (argc<2) { + fprintf(stderr,"%s \n",argv[0]); + return 0; + } + widevine::WidevineProvisioner provisioner; + if (!std::strcmp(argv[1],"bcc")) { + auto bcc = provisioner.GetBcc(); + fwrite(bcc.data(), 1 , bcc.size(), stdout); + fflush(stdout); + } else if (!std::strcmp(argv[1],"device_info")) { + std::vector deviceInfo; + if (provisioner.GetDeviceInfo(deviceInfo)) { + fwrite(deviceInfo.data(), 1 , deviceInfo.size(), stdout); + fflush(stdout); + } + } else if (!std::strcmp(argv[1],"csr")) { + auto csr = getCsr(provisioner); + auto bytes = csr.encode(); + std::copy(bytes.begin(), bytes.end(), std::ostream_iterator(std::cout)); + } + return 0; +} diff --git a/libwvdrmengine/tools/factory_upload_tool/include/WidevineOemcryptoInterface.h b/libwvdrmengine/tools/factory_upload_tool/include/WidevineOemcryptoInterface.h new file mode 100644 index 00000000..be355532 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/include/WidevineOemcryptoInterface.h @@ -0,0 +1,52 @@ +// 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_OEMCRYPTO_INTERFACE_H_ +#define WIDEVINE_OEMCRYPTO_INTERFACE_H_ + +#include +#include +#include + +#include "OEMCryptoCENC.h" + +namespace widevine { + +class OEMCryptoInterface { + public: + OEMCryptoInterface() = default; + OEMCryptoInterface(const OEMCryptoInterface&) = delete; + OEMCryptoInterface& operator=(const OEMCryptoInterface&) = delete; + virtual ~OEMCryptoInterface(); + + // Initializes this interface by providing path to the OEMCrypto library. + bool Init(const std::string& oemcrypto_path); + + // Retrieves the boot certificate chain from OEMCrypto implementation. + OEMCryptoResult GetBcc(std::vector& bcc); + + // Retrieves the build information of the OEMCrypto library from OEMCrypto + // implementation. + OEMCryptoResult GetOEMCryptoBuildInfo(std::string& build_info); + + private: + typedef OEMCryptoResult (*Initialize_t)(); + typedef OEMCryptoResult (*Terminate_t)(); + typedef OEMCryptoResult (*GetBootCertificateChain_t)( + uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature, + size_t* additional_signature_size); + typedef OEMCryptoResult (*BuildInformation_t)(char* buffer, + size_t* buffer_length); + + Initialize_t Initialize = nullptr; + Terminate_t Terminate = nullptr; + GetBootCertificateChain_t GetBootCertificateChain = nullptr; + BuildInformation_t BuildInformation = nullptr; + + void* handle_ = nullptr; +}; + +} // namespace widevine + +#endif // WIDEVINE_OEMCRYPTO_INTERFACE_H_ \ No newline at end of file diff --git a/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h b/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h new file mode 100644 index 00000000..62b81d36 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h @@ -0,0 +1,53 @@ +// 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 + +#include +#include +#include +#include +#include + +#include "WidevineOemcryptoInterface.h" + +namespace widevine { + +class WidevineProvisioner { + public: + WidevineProvisioner(); + WidevineProvisioner(const WidevineProvisioner&) = delete; + WidevineProvisioner& operator=(const WidevineProvisioner&) = delete; + virtual ~WidevineProvisioner() = default; + + std::vector GetBcc(); + + bool GenerateCertificateRequest( + bool testMode, const std::vector& endpointEncCertChain, + std::vector& deviceInfo, std::vector& protectedData); + + bool GetDeviceInfo(std::vector& device_info); + + private: + bool GenerateProtectedData( + bool test_mode, + const std::vector& endpoint_encryption_cert_chain, + std::vector bcc, std::vector& protected_data) const; + bool ValidateAndExtractEekPubAndId( + bool test_mode, + const std::vector& endpoint_encryption_cert_chain, + std::vector* eek_pub, std::vector* eek_id) const; + cppbor::Array BuildCertReqRecipients(const std::vector& pubkey, + const std::vector& kid) const; + void InitializeCryptoInterface(); + + std::unique_ptr crypto_interface_; +}; + +} // namespace widevine + +#endif // WIDEVINE_PROVISIONER_H_ diff --git a/libwvdrmengine/tools/factory_upload_tool/include/properties.h b/libwvdrmengine/tools/factory_upload_tool/include/properties.h new file mode 100644 index 00000000..d61394f8 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/include/properties.h @@ -0,0 +1,34 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#ifndef WVCDM_CORE_PROPERTIES_H_ +#define WVCDM_CORE_PROPERTIES_H_ + +#include +#include +#include +#include + +#include "disallow_copy_and_assign.h" + +namespace wvcdm { + +// This class gives device information/meta data. +class Properties { + public: + static bool GetCompanyName(std::string* company_name); + static bool GetModelName(std::string* model_name); + static bool GetArchitectureName(std::string* arch_name); + static bool GetDeviceName(std::string* device_name); + static bool GetProductName(std::string* product_name); + static bool GetBuildInfo(std::string* build_info); + static bool GetOEMCryptoPath(std::string* library_name); + + private: + CORE_DISALLOW_COPY_AND_ASSIGN(Properties); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_PROPERTIES_H_ diff --git a/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp b/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp new file mode 100644 index 00000000..dee299c6 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp @@ -0,0 +1,143 @@ +// 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 "WidevineOemcryptoInterface.h" + +#include + +#include "OEMCryptoCENC.h" +#include "clock.h" +#include "file_store.h" +#include "log.h" + +// These macros lookup the obfuscated name used for OEMCrypto. +#define QUOTE_DEFINE(A) #A +#define QUOTE(A) QUOTE_DEFINE(A) +#define LOOKUP(handle, name) dlsym(handle, QUOTE(name)) +#define LOAD_SYM(name) \ + name = reinterpret_cast(LOOKUP(handle_, OEMCrypto_##name)); \ + if (name == nullptr) { \ + LOGE("%s", dlerror()); \ + return false; \ + } + +// These are implementations required by OEMCrypto Reference Implementation +// and/or the Testbed, but not needed in this package. +namespace wvutil { +int64_t Clock::GetCurrentTime() { return 0; } + +class FileImpl final : public File { + public: + FileImpl() {} + ssize_t Read(char*, size_t) override { return 0; } + ssize_t Write(const char*, size_t) override { return 0; } +}; + +class FileSystem::Impl { + public: + Impl() {} +}; + +FileSystem::FileSystem() {} +FileSystem::FileSystem(const std::string&, void*) {} +FileSystem::~FileSystem() {} +std::unique_ptr FileSystem::Open(const std::string&, int) { + return std::unique_ptr(new FileImpl()); +} +bool FileSystem::Exists(const std::string&) { return false; } +bool FileSystem::Remove(const std::string&) { return false; } +ssize_t FileSystem::FileSize(const std::string&) { return false; } +bool FileSystem::List(const std::string&, std::vector*) { + return false; +} + +} // namespace wvutil + +namespace widevine { + +OEMCryptoInterface::~OEMCryptoInterface() { + if (Terminate != nullptr) { + Terminate(); + } + if (handle_ != nullptr) { + dlclose(handle_); + } +} + +bool OEMCryptoInterface::Init(const std::string& oemcrypto_path) { + dlerror(); + handle_ = dlopen(oemcrypto_path.c_str(), RTLD_LAZY | RTLD_GLOBAL); + if (handle_ == nullptr) { + LOGE("Can't open OEMCrypto library: %s", dlerror()); + return false; + } + LOGI("OEMCrypto library opened."); + + LOAD_SYM(Initialize); + LOAD_SYM(Terminate); + LOAD_SYM(GetBootCertificateChain); + LOAD_SYM(BuildInformation); + + OEMCryptoResult status = Initialize(); + if (status != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto Initialize failed: %d", status); + return false; + } + return true; +} + +OEMCryptoResult OEMCryptoInterface::GetBcc(std::vector& bcc) { + if (handle_ == nullptr) { + return OEMCrypto_ERROR_INIT_FAILED; + } + + bcc.resize(0); + size_t bcc_size = 0; + + std::vector additional_signature; // It should be empty. + size_t additional_signature_size = 0; + OEMCryptoResult result = GetBootCertificateChain(bcc.data(), &bcc_size, + additional_signature.data(), + &additional_signature_size); + LOGI("GetBootCertificateChain first attempt result %d", result); + if (additional_signature_size != 0) { + LOGW( + "The additional_signature_size required by OEMCrypto is %zu, while it " + "is expected to be zero.", + additional_signature_size); + } + + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + bcc.resize(bcc_size); + additional_signature.resize(additional_signature_size); + result = GetBootCertificateChain(bcc.data(), &bcc_size, + additional_signature.data(), + &additional_signature_size); + LOGI("GetBootCertificateChain second attempt result %d", result); + } + + return result; +} + +OEMCryptoResult OEMCryptoInterface::GetOEMCryptoBuildInfo( + std::string& build_info) { + if (handle_ == nullptr) { + return OEMCrypto_ERROR_INIT_FAILED; + } + + build_info.resize(0); + size_t build_info_size = 0; + + OEMCryptoResult result = BuildInformation(&build_info[0], &build_info_size); + LOGI("BuildInformation first attempt result %d", result); + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + build_info.resize(build_info_size); + result = BuildInformation(&build_info[0], &build_info_size); + LOGI("BuildInformation second attempt result %d", result); + } + + return result; +} + +} // namespace widevine diff --git a/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp b/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp new file mode 100644 index 00000000..af187104 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp @@ -0,0 +1,276 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "log.h" +#include "properties.h" + +namespace widevine { + +namespace { +const std::vector> 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}, +}; +} // namespace + +WidevineProvisioner::WidevineProvisioner() { + InitializeCryptoInterface(); + assert(crypto_interface_ != nullptr); +} + +std::vector WidevineProvisioner::GetBcc() { + std::vector bcc; + OEMCryptoResult result = crypto_interface_->GetBcc(bcc); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get BCC."); + } + return bcc; +} + +bool WidevineProvisioner::GenerateCertificateRequest( + bool testMode, const std::vector& endpointEncCertChain, + std::vector& deviceInfo, std::vector& protectedData) { + if (!GetDeviceInfo(deviceInfo)) { + LOGE("Failed to get device_info."); + return false; + } + + std::vector bcc; + OEMCryptoResult result = crypto_interface_->GetBcc(bcc); + if (result != OEMCrypto_SUCCESS) { + LOGE("Failed to get BCC."); + return false; + } + + if (!GenerateProtectedData(testMode, endpointEncCertChain, bcc, + protectedData)) { + LOGE("Failed to generate protected data."); + return false; + } + + return true; +} + +bool WidevineProvisioner::GetDeviceInfo(std::vector& device_info) { + auto device_info_map = cppbor::Map(); + device_info_map.add(cppbor::Tstr("type"), cppbor::Tstr("widevine")); + + 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)); + + 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)); + + 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 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)); + + 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)); + + 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."); + return false; + } + device_info_map.add(cppbor::Tstr("oemcrypto_build_info"), + cppbor::Tstr(oemcrypto_build_info)); + + device_info = device_info_map.canonicalize().encode(); + return true; +} + +bool WidevineProvisioner::GenerateProtectedData( + bool test_mode, const std::vector& endpoint_encryption_cert_chain, + std::vector bcc, std::vector& protected_data) const { + // Encrypt |signedMac| and |bcc_| with GEEK. + std::vector eek_pub; + std::vector 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 ephemeralPrivKey(X25519_PRIVATE_KEY_LEN); + std::vector 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 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& endpoint_encryption_cert_chain, + std::vector* eek_pub, std::vector* 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 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 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& pubkey, const std::vector& 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 +} + +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(); + if (!crypto_interface_->Init(oemcrypto_path)) { + LOGE("Failed to initialize OEMCrypto interface."); + crypto_interface_.reset(nullptr); + } +} + +} // namespace widevine diff --git a/libwvdrmengine/tools/factory_upload_tool/src/log.cpp b/libwvdrmengine/tools/factory_upload_tool/src/log.cpp new file mode 100644 index 00000000..5d018870 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/src/log.cpp @@ -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 +#include +#include + +#include + +/* + * 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(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 diff --git a/libwvdrmengine/tools/factory_upload_tool/src/properties_android.cpp b/libwvdrmengine/tools/factory_upload_tool/src/properties_android.cpp new file mode 100644 index 00000000..5b47b556 --- /dev/null +++ b/libwvdrmengine/tools/factory_upload_tool/src/properties_android.cpp @@ -0,0 +1,93 @@ +// 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 +#include + +#include +#include + +#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::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