From bac33dbc6e182674ac35002d48cba0541ca02aaa Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Tue, 25 May 2021 01:33:04 +0000 Subject: [PATCH] Use local provisioning server Merge from Widevine repo of http://go/wvgerrit/133703 and http://ag/14707867 [ Cherry-pick of http://ag/15835345 ] In order to use a local provisioning server, we need to use a different test keybox system id that is in the dev device database instead of the production database. We also need to use a local license server that uses the dev license server. Bug: 187646550 Test: GtsMediaTestCases Change-Id: Ice89143dd26de22757375a770c6bac716fcbc057 Add Keybox OTA Provisioning functions to OEMCrypto header Merge from Widevine repo of http://go/wvgerrit/133704 and http://go/ag/14707868 Bug: 188228998 Change-Id: Iff54bc2870e87bf7239e179e1d02fbcc8df6198f Stub build changes to support OTA Keybox Merge from Widevine repo of http://go/wvgerrit/133725 and http://go/ag/14781459 This CL adds a new unit test file for testing OTA keybox reprovisioning functionality. This new test is built when running the dynamic adapter in the linux build, and in the Android build. Bug: 187646550 Change-Id: I625513840188f95e74831ef2ea399e827e837439 Add OTA Keybox functions to dynamic adapter Merge from Widevine repo of http://go/wvgerrit/125843 and http://go/ag/14781460 Bug: 187646550 Change-Id: Ief78ed10599c091690e0d7dc488ea71674c763b5 Refactor dynamic adapter keybox verification Merge from Widevine repo of http://go/wvgerrit/133727 http://go/ag/14812524 The keybox validation needs to be done separately from initializing the library so that we can support Keybox OTA Reprovisioning. If L1 loads, but the keybox is missing, the initialization should succeed. When the keybox is validated, the adapter should try to look for a keybox on the filesystem. if none is found, it should either return NEEDS PROVISIONING or an error. Bug: 187646550 Change-Id: I34a8c365a5a5ca35c379bea827c85c749964744c Update crypto session to use new OTA keybox functionality Merge from Widevine repo of http://go/wvgerrit/133728 and http://go/ag/14812525 This CL stubs out two new CryptoSession functions that call the new OEMCrypto functions for OTA Keybox Provisioning. It builds! Yay! It also adds a boolean needs_keybox_provisioning that is set to true when OEMCrypto reports that it needs a keybox. This should only happen if there is no keybox installed and oemcrypto supports provisioning. Bug: 187646550 Change-Id: Ide9533943125aa13b8899b652b118a0b410c882c --- .../cdm/core/include/crypto_session.h | 18 ++ .../cdm/core/include/oemcrypto_adapter.h | 6 +- .../cdm/core/src/crypto_session.cpp | 43 ++- .../core/src/oemcrypto_adapter_dynamic.cpp | 262 ++++++++++++------ .../cdm/core/src/oemcrypto_ota_stubs.cpp | 17 ++ .../cdm/core/test/config_test_env.cpp | 30 ++ .../cdm/core/test/config_test_env.h | 2 + .../cdm/core/test/keybox_ota_test.cpp | 11 + libwvdrmengine/cdm/core/test/test_base.cpp | 23 +- libwvdrmengine/cdm/core/test/test_base.h | 3 + .../cdm/core/test/test_printers.cpp | 3 + libwvdrmengine/cdm/test/Android.mk | 4 + .../oemcrypto/include/OEMCryptoCENC.h | 109 +++++++- .../odk/include/OEMCryptoCENCCommon.h | 1 + libwvdrmengine/oemcrypto/test/common.mk | 1 + libwvdrmengine/oemcrypto/test/oec_test_data.h | 38 +++ .../oemcrypto/test/ota_keybox_test.cpp | 53 ++++ 17 files changed, 526 insertions(+), 98 deletions(-) create mode 100644 libwvdrmengine/cdm/core/src/oemcrypto_ota_stubs.cpp create mode 100644 libwvdrmengine/cdm/core/test/keybox_ota_test.cpp create mode 100644 libwvdrmengine/oemcrypto/test/ota_keybox_test.cpp diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 4d2e9371..e516a1b3 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -287,6 +287,19 @@ class CryptoSession { virtual CdmResponseType GetProvisioningMethod( SecurityLevel requested_security_level, CdmClientTokenType* token_type); + // OTA Provisioning + // TODO(sigquit): include rest of http://go/wvgerrit/126004 + + // Generates an OTA provisioning request. + // This should only be called by an instance of OtaKeyboxProvisioner. + virtual CdmResponseType PrepareOtaProvisioningRequest(bool use_test_key, + std::string* request); + + // Loads an OTA provisioning response. + // This should only be called by an instance of OtaKeyboxProvisioner. + virtual CdmResponseType LoadOtaProvisioning(bool use_test_key, + const std::string& response); + protected: // Creates an instance of CryptoSession with the given |crypto_metrics|. // |crypto_metrics| is owned by the caller, must NOT be null, and must @@ -294,6 +307,10 @@ class CryptoSession { explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics); int session_count() const { return session_count_; } + bool initialized() const { return initialized_; } + void OverrideInitializedForTesting(bool initialized) { + initialized_ = initialized; + } private: friend class CryptoSessionForTest; @@ -434,6 +451,7 @@ class CryptoSession { static bool initialized_; static int session_count_; static int termination_counter_; + static bool needs_keybox_provisioning_; enum CachedBooleanProperty { // Property has not yet been checked/cached. diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h index e52c519d..e12a2b99 100644 --- a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -9,6 +9,11 @@ #include "wv_cdm_types.h" namespace wvcdm { +// Initialize OEMCrypto, then check the keybox and see if it is valid. If not, +// and OTA provisioning is supported, set needs_keybox_provisioning to true. +// If the keybox is not valid, and +OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( + bool* needs_keybox_provisioning); // This attempts to open a session at the desired security level. // If one level is not available, the other will be used instead. @@ -17,7 +22,6 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, size_t keyBoxLength, SecurityLevel level); -OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(SecurityLevel level); OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength, SecurityLevel level); OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength, diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 051554f1..7967b49b 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -85,7 +85,7 @@ constexpr size_t kMaxSubsampleRegionSizes[] = { static_assert(ArraySize(kMaxSubsampleRegionSizes) == RESOURCE_RATING_TIER_MAX - RESOURCE_RATING_TIER_MIN + 1, "The kMaxSubsampleRegionSizes table needs to be updated to " - "reflect the supported range of resource rating tiers."); + "reflect the supported range of resource rating tiers"); constexpr size_t kDefaultMaxSubsampleRegionSize = kMaxSubsampleRegionSizes[0]; @@ -171,6 +171,7 @@ size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm) { shared_mutex CryptoSession::static_field_mutex_; shared_mutex CryptoSession::oem_crypto_mutex_; bool CryptoSession::initialized_ = false; +bool CryptoSession::needs_keybox_provisioning_ = false; int CryptoSession::session_count_ = 0; int CryptoSession::termination_counter_ = 0; std::unique_ptr CryptoSession::usage_table_header_l1_; @@ -321,8 +322,9 @@ void CryptoSession::Init() { sandbox_id.length()); metrics_->oemcrypto_set_sandbox_.Record(sandbox_id); } - M_TIME(sts = OEMCrypto_Initialize(), metrics_, oemcrypto_initialize_, - sts); + M_TIME(sts = OEMCrypto_InitializeAndCheckKeybox( + &needs_keybox_provisioning_), + metrics_, oemcrypto_initialize_, sts); }); if (OEMCrypto_SUCCESS != sts) { LOGE("OEMCrypto_Initialize failed: status = %d", static_cast(sts)); @@ -357,6 +359,10 @@ void CryptoSession::Init() { : kStringNotAvailable; LOGD("OEMCrypto version (L3 security level): %s.%s", api_version.c_str(), api_minor_version.c_str()); + if (needs_keybox_provisioning_) { + LOGE("OEMCrypto needs provisioning"); + // TODO(fredgc,sigquit,rfrias): handle provisioning. + } } } @@ -1440,7 +1446,7 @@ size_t CryptoSession::GetMaxSubsampleRegionSize() { // If something went wrong, use the default. if (max_subsample_region_size_ == 0) { - LOGW("Unable to get maximum subsample region size. Defaulting to %zu.", + LOGW("Unable to get maximum subsample region size. Defaulting to %zu", kDefaultMaxSubsampleRegionSize); max_subsample_region_size_ = kDefaultMaxSubsampleRegionSize; } @@ -2994,7 +3000,7 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks( static_assert(sizeof(fake_sample.iv) == kAes128BlockSize, "The size of an AES-128 block and the size of an AES-128 " - "IV have become misaligned."); + "IV have become misaligned"); memcpy(fake_sample.iv, block_end - kAes128BlockSize, kAes128BlockSize); } } @@ -3009,6 +3015,33 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks( return sts; } +// TODO(sigquit): include rest of http://go/wvgerrit/126004 + +CdmResponseType CryptoSession::PrepareOtaProvisioningRequest( + bool use_test_key, std::string* request) { + RETURN_IF_NULL(request, PARAMETER_NULL); + size_t buffer_length = 0; + OEMCryptoResult status = + OEMCrypto_GenerateOTARequest(nullptr, &buffer_length, use_test_key); + if (status != OEMCrypto_ERROR_SHORT_BUFFER) + return MapOEMCryptoResult(status, UNKNOWN_ERROR, + "PrepareOtaProvisioningRequest"); + std::string temp_buffer(buffer_length, '\0'); + uint8_t* buf = reinterpret_cast(&temp_buffer[0]); + status = OEMCrypto_GenerateOTARequest(buf, &buffer_length, use_test_key); + if (OEMCrypto_SUCCESS == status) request->assign(temp_buffer); + return MapOEMCryptoResult(status, UNKNOWN_ERROR, + "PrepareOtaProvisioningRequest"); +} + +CdmResponseType CryptoSession::LoadOtaProvisioning( + bool use_test_key, const std::string& response) { + OEMCryptoResult status = OEMCrypto_ProcessOTAKeybox( + reinterpret_cast(response.data()), response.size(), + use_test_key); + return MapOEMCryptoResult(status, UNKNOWN_ERROR, "LoadOtaProvisioning"); +} + template auto CryptoSession::WithStaticFieldWriteLock(const char* tag, Func body) -> decltype(body()) { diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 51e2c119..759c280d 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -318,7 +318,12 @@ typedef OEMCryptoResult (*L1_LoadProvisioning_t)( size_t signature_length, uint8_t* wrapped_private_key, size_t* wrapped_private_key_length); typedef uint32_t (*L1_MinorAPIVersion_t)(); - +typedef OEMCryptoResult (*L1_GenerateOTARequest_t)(uint8_t* buffer, + size_t* buffer_length, + bool use_test_key); +typedef OEMCryptoResult (*L1_ProcessOTAKeybox_t)(const uint8_t* buffer, + size_t buffer_length, + bool use_test_key); struct FunctionPointers { wvcdm::CdmSecurityLevel security_level; uint32_t version; @@ -415,6 +420,8 @@ struct FunctionPointers { L1_SelectKey_V13_t SelectKey_V13; L1_LoadTestKeybox_V13_t LoadTestKeybox_V13; L1_RefreshKeys_V14_t RefreshKeys_V14; + L1_GenerateOTARequest_t GenerateOTARequest; + L1_ProcessOTAKeybox_t ProcessOTAKeybox; }; size_t GetOffset(const std::string& message, const std::string& field) { @@ -643,7 +650,7 @@ struct LevelSession { if ((level1_.version >= min) && (level1_.version <= max)) { \ level1_.Name = (L1_##Name##_t)dlsym(level1_library_, QUOTE(Function)); \ if (!level1_.Name) { \ - LOGW("Could not load L1 %s. Falling Back to L3.", QUOTE(Function)); \ + LOGW("Could not load L1 %s. Falling back to L3.", QUOTE(Function)); \ if (level1_.Terminate) level1_.Terminate(); \ return false; \ } \ @@ -778,7 +785,7 @@ class Adapter { } OEMCryptoResult st = level1_.Initialize(); if (st != OEMCrypto_SUCCESS) { - LOGW("Could not initialize L1. Falling Back to L3."); + LOGW("Could not initialize L1. Falling back to L3."); metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( wvcdm::metrics:: OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INITIALIZE_L1); @@ -790,7 +797,7 @@ class Adapter { metrics->SetL1MinApiVersion(kMinimumVersion); if (level1_.version < kMinimumVersion) { - LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.", + LOGW("liboemcrypto.so is version %d, not %d. Falling back to L3.", level1_.version, kMinimumVersion); metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_WRONG_L1_VERSION); @@ -887,6 +894,8 @@ class Adapter { LOOKUP_ALL(16, MaximumUsageTableHeaderSize, OEMCrypto_MaximumUsageTableHeaderSize); LOOKUP_ALL(16, LoadProvisioning, OEMCrypto_LoadProvisioning); LOOKUP_ALL(16, MinorAPIVersion, OEMCrypto_MinorAPIVersion); + LOOKUP_ALL(16, GenerateOTARequest, OEMCrypto_GenerateOTARequest); + LOOKUP_ALL(16, ProcessOTAKeybox, OEMCrypto_ProcessOTAKeybox); // clang-format on // There was a mistake in version 16.3 of the header that did not rename @@ -915,81 +924,6 @@ class Adapter { } } - // TODO(119830252): make the code below available to a static adapter. - // Check if the keybox or oem certificate is valid, if so, we are finished - // with initialization. - OEMCryptoResult root_valid = level1_.IsKeyboxOrOEMCertValid(); - OEMCrypto_ProvisioningMethod provisioning_method = - level1_.GetProvisioningMethod(); - if (root_valid == OEMCrypto_SUCCESS) { - // The keybox or certificate is valid -- that means initialization is done - // and we only have save some metrics and return. - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - (provisioning_method == OEMCrypto_Keybox) - ? wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX - : wvcdm::metrics:: - OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0); - return true; - } - // At this point, the keybox or cert is not valid. We look on the file - // system for one. If it is there we try to install it. - wvcdm::FileSystem file_system; - std::string filename; - if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) { - // No keybox or cert file found. Give up. - LOGW("Bad Level 1 Root of Trust. Falling Back to L3."); - level1_.Terminate(); - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX); - return false; - } - ssize_t size = file_system.FileSize(filename); - if (size <= 0) { - // A keybox or cert file was found, but it has size 0. Give up. - LOGW("Could not find %s. Falling Back to L3.", filename.c_str()); - level1_.Terminate(); - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - wvcdm::metrics:: - OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX); - return false; - } - auto file = file_system.Open(filename, file_system.kReadOnly); - if (!file) { - // A keybox or cert file was found, but can't open it. Give up. - LOGW("Could not open %s. Falling Back to L3.", filename.c_str()); - level1_.Terminate(); - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - wvcdm::metrics:: - OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX); - return false; - } - std::vector root_key(size); - ssize_t size_read = file->Read(reinterpret_cast(&root_key[0]), size); - if (level1_.InstallKeyboxOrOEMCert(&root_key[0], size_read) != - OEMCrypto_SUCCESS) { - // A keybox or cert file was read, but I could not install it. Give up. - LOGE("Could NOT install root key from %s. Falling Back to L3.", - filename.c_str()); - level1_.Terminate(); - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - wvcdm::metrics:: - OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX); - return false; - } - if (level1_.IsKeyboxOrOEMCertValid() != OEMCrypto_SUCCESS) { - // A keybox or cert file was read and installed, but it is still not - // valid. Give up. - LOGE("Installed bad key from %s. Falling Back to L3.", filename.c_str()); - level1_.Terminate(); - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - wvcdm::metrics:: - OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX); - return false; - } - // A valid keybox or cert file was read and installed. Yay! return success. - LOGI("Installed root key from %s", filename.c_str()); - metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( - wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX); return true; } @@ -1133,6 +1067,77 @@ class Adapter { return result; } + // Check the L1 keybox or cert. If it is valid, return success. If not, try to + // install one. If one is not available, but OTA provisioning is supported, + // return OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING. If none of these work, + // then return the status of the L3 keybox or cert. + OEMCryptoResult ValidateOrInstallKeyboxOrCert() { + if (!level1_valid_) { + // TODO(b/189989043): add metrics. + // If level 1 not initialized, then return level 3's answer. + return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid() + : OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!level1_.IsKeyboxOrOEMCertValid) { + // TODO(b/189989043): add metrics. + LOGE("L1 invalid function pointers. Falling back to L3"); + if (level1_.Terminate) level1_.Terminate(); + level1_valid_ = false; + return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid() + : OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + // Check if the keybox or oem certificate is valid, if so, we are finished + // with initialization. Record some metrics and return success. + const OEMCryptoResult rot_valid = level1_.IsKeyboxOrOEMCertValid(); + wvcdm::metrics::OemCryptoDynamicAdapterMetrics& metrics = + wvcdm::metrics::GetDynamicAdapterMetricsInstance(); + // Figure out provisioning method. Defaults to keybox. + const OEMCrypto_ProvisioningMethod provisioning_method = + level1_.GetProvisioningMethod ? level1_.GetProvisioningMethod() + : OEMCrypto_Keybox; + if (rot_valid == OEMCrypto_SUCCESS) { + // The keybox or certificate is valid -- that means initialization is done + // and we only have save some metrics and return. + metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode( + (provisioning_method == OEMCrypto_Keybox) + ? wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX + : wvcdm::metrics:: + OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0); + return OEMCrypto_SUCCESS; + } + // At this point, the L1 keybox or cert is not valid. If are able to + // install one, then we look on the file system for one. If it is there we + // try to install it. + OEMCryptoResult file_attempt = TryToInstallKeybox(); + if (file_attempt == OEMCrypto_SUCCESS) { + // If loading the keybox succeeded, we are done. good job. + metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode( + wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX); + return OEMCrypto_SUCCESS; + } + if (rot_valid == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING) { + // TODO(b/189989043): add metrics. + return OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING; + } + if (file_attempt == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + // No keybox to load, and none installed, so give up. + LOGW("Bad Level 1 Root of Trust. Falling back to L3"); + metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode( + wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX); + } else { + // There was a keybox to load, but there was an error loading it. So give + // up. + LOGW("Error installing Level 1 Root of Trust. Falling back to L3"); + metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode( + wvcdm::metrics:: + OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX); + } + if (level1_.Terminate) level1_.Terminate(); + level1_valid_ = false; + return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid() + : OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + private: bool level1_valid_; void* level1_library_; @@ -1149,6 +1154,51 @@ class Adapter { if (!var) return false; return !strcmp(var, "yes"); } + + // Try to install a keybox from the file system. + OEMCryptoResult TryToInstallKeybox() { + if (!level1_.InstallKeyboxOrOEMCert) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + wvcdm::FileSystem file_system; + std::string filename; + if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) { + // No keybox or cert file found. Give up. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + const ssize_t size = file_system.FileSize(filename); + if (size <= 0) { + // The keybox file does not exit or has size 0. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + // After this, any error will be logged differently to metrics + // because we found a keybox on the filesystem, so we expect it to work. + auto file = file_system.Open(filename, file_system.kReadOnly); + if (!file) { + // A keybox or cert file was found, but can't open it. Give up, but + // log it as a different error because the file did exist. + LOGW("Could not open %s", filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::vector root_key(size); + ssize_t size_read = + file->Read(reinterpret_cast(root_key.data()), size); + if (level1_.InstallKeyboxOrOEMCert(root_key.data(), size_read) != + OEMCrypto_SUCCESS) { + // A keybox or cert file was read, but I could not install it. Give up. + LOGE("Could NOT install root key from %s", filename.c_str()); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + if (!level1_.IsKeyboxOrOEMCertValid) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + OEMCryptoResult rot_valid = level1_.IsKeyboxOrOEMCertValid(); + if (rot_valid == OEMCrypto_SUCCESS) { + // A valid keybox or cert file was read and installed. Yay! return + // success. + LOGI("Installed root key from %s", filename.c_str()); + } else { + LOGW("Installed root key from %s, but invalid(%d)", filename.c_str(), + rot_valid); + } + return rot_valid; + } }; static std::unique_ptr gAdapter; @@ -1156,6 +1206,23 @@ static std::unique_ptr gAdapter; namespace wvcdm { +OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( + bool* needs_keybox_provisioning) { + if (!needs_keybox_provisioning) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (!gAdapter) { + gAdapter.reset(new Adapter()); + } + const OEMCryptoResult status = gAdapter->Initialize(); + if (status != OEMCrypto_SUCCESS) return status; + const OEMCryptoResult keybox_status = + gAdapter->ValidateOrInstallKeyboxOrCert(); + if (keybox_status == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING) { + *needs_keybox_provisioning = true; + return OEMCrypto_SUCCESS; + } + return keybox_status; +} + OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) { if (!gAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED; @@ -1183,15 +1250,6 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod( return fcn->GetProvisioningMethod(); } -OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(SecurityLevel level) { - if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; - const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level); - if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; - if (fcn->IsKeyboxOrOEMCertValid == nullptr) - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - return fcn->IsKeyboxOrOEMCertValid(); -} - OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength, SecurityLevel level) { if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -2155,7 +2213,12 @@ extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, } extern "C" OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid() { - return OEMCrypto_IsKeyboxOrOEMCertValid(kLevelDefault); + if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault); + if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION; + if (fcn->IsKeyboxOrOEMCertValid == nullptr) + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + return fcn->IsKeyboxOrOEMCertValid(); } extern "C" OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() { @@ -2733,3 +2796,24 @@ extern "C" OEMCryptoResult OEMCrypto_FreeSecureBuffer( } return pair.fcn->FreeSecureBuffer(pair.session, output_descriptor, secure_fd); } + +extern "C" OEMCryptoResult OEMCrypto_GenerateOTARequest(uint8_t* buffer, + size_t* buffer_length, + bool use_test_key) { + if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault); + if (!fcn) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (fcn->GenerateOTARequest == nullptr) + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + return fcn->GenerateOTARequest(buffer, buffer_length, use_test_key); +} + +extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(const uint8_t* buffer, + size_t buffer_length, + bool use_test_key) { + if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault); + if (!fcn) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (fcn->ProcessOTAKeybox == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + return fcn->ProcessOTAKeybox(buffer, buffer_length, use_test_key); +} diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_ota_stubs.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_ota_stubs.cpp new file mode 100644 index 00000000..74bfb3c1 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/oemcrypto_ota_stubs.cpp @@ -0,0 +1,17 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "OEMCryptoCENC.h" + +extern "C" OEMCryptoResult OEMCrypto_GenerateOTARequest(uint8_t* buffer, + size_t* buffer_length, + bool use_test_key) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(const uint8_t* buffer, + size_t buffer_length, + bool use_test_key) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp index 10dc6b6c..40b2cf47 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -82,6 +82,32 @@ const std::string kCpStagingProvisioningServiceCertificate = "8598ed5751b38694419242a875d9e00d5a5832933024b934859ec8be78adccbb" "1ec7127ae9afeef9c5cd2e15bd3048e8ce652f7d8c5d595a0323238c598a28"; +// Service certificate for qa.widevine.com +const std::string kCpQAProvisioningServiceCertificate = + "0abc02080312100ec8164669cc2fdfc253b3b5e763276e18abd8cdcf05228e02" + "3082010a0282010100b24d497c0cc6ab5072f97623daa49b8d5564360654d8e5" + "8df8db7a23158f1afdd04724cbadbe87001532d9d6dec3b06973666da7759ec3" + "bf3083e2d9b85e7a47c340db796b085493a460eef31d56e3f15d857713c55cdb" + "164fe09e2a06be7fb979ad55e33a59ade3712aed2445b89fc145556a9e0093fa" + "36fc3ff4d05291a9633d20c80a13cd0d924ed9078395714c30b49019f4d6f5ba" + "093ad958aee9a164ba73ec298f905662de5859d3e6fae41c063f262d29dae75e" + "8654ac9d68f3e3fccc809573d0f90704a77f9bce391d0a5f265f438119e4fb0b" + "ec27706f5c7fc888f665730b691a0431e30cb3b57dfd078838c44550c3b79b35" + "0552a92a760f90c6cf02030100013a0f71612e7769646576696e652e636f6d12" + "800331ca1662fdc97e02debdca5b6de35dce5da87b5f61b15745ccf83e66197c" + "e31bc6379ae4f6a5e4fd8a0e76f979701c5a715c06d70908563626d0dd3986b5" + "e623a7b6336789c67f0fc68f9ec68e045f85d9a06942f4af0fe47d801cf035af" + "27924f1c4cd395d15ce2f92f48044254fdefe37320471d7009160f5293183ca4" + "a09bca71f76f1457a80eebcf12706bd79256f1b02e67dc002fc81e18c00d880f" + "04b0187e6ef59ae75eaaf6b16672a887b9657f1796607d1585d1998283af1650" + "9bd9a170c262056aad69e222a4c3180d104a76d76da29082e4f2e5297d1ec44d" + "ed98c999981688089d8ff2d62f0f13b96ce5e89a4c215d60f025fa29811fcdc1" + "848fe0581f612f45733da4b4c8803ae8088dcb3b811ea9c691daccfbe9cbf603" + "13e8f85eb68f2f1d8cdf9e4fc91a46157a90fffafbd9d408b319307ea4d3d4a9" + "d2f177355ad361f5284057dec1b186beb85dcbda64bf00a164cecc66c1878961" + "7748618d069c39f365e8347acdae777cc4e3c3c3c3fe9698c4f5ee1285b0e6a9" + "675e"; + // ----------------------------------------------------------------------------- // Below are several choices for licenseing servers: production, UAT, staging // and staging and maybe Google Play. We haven't tested with Google Play in a @@ -354,4 +380,8 @@ const std::string& ConfigTestEnv::GetLicenseServerUrl( } } +std::string ConfigTestEnv::QAProvisioningServiceCertificate() { + return a2bs_hex(kCpQAProvisioningServiceCertificate); +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h index 5e40446b..50900bf3 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.h +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -116,6 +116,8 @@ class ConfigTestEnv { const std::string& provisioning_service_certificate) { provisioning_service_certificate_.assign(provisioning_service_certificate); } + // The QA service certificate, used for a local provisioning server. + static std::string QAProvisioningServiceCertificate(); private: void Init(ServerConfigurationId server_id); diff --git a/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp b/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp new file mode 100644 index 00000000..16dd0836 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp @@ -0,0 +1,11 @@ +// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include + +#include "crypto_session.h" + +namespace wvcdm { +TEST(OTAKeyboxTest, TestThatTheBuildFilesWork) { ASSERT_TRUE(true); } +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/test_base.cpp b/libwvdrmengine/cdm/core/test/test_base.cpp index a256c42e..87c14bd3 100644 --- a/libwvdrmengine/cdm/core/test/test_base.cpp +++ b/libwvdrmengine/cdm/core/test/test_base.cpp @@ -112,6 +112,11 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) { << " in the url" << std::endl << std::endl; + std::cout << " --qa_provisioning" << std::endl; + std::cout << " use the QA provisioning cert and QA test keybox" + << std::endl + << std::endl; + std::cout << " --fake_sleep" << std::endl; std::cout << " Use a fake clock to sleep for duration tests. This cannot" << " be used with a real OEMCrypto." << std::endl @@ -162,6 +167,7 @@ bool ExtractSignedMessage(const std::string& response, } // namespace ConfigTestEnv WvCdmTestBase::default_config_(kContentProtectionUatServer); +bool WvCdmTestBase::use_qa_test_keybox_ = false; void WvCdmTestBase::StripeBuffer(std::vector* buffer, size_t size, uint8_t init) { @@ -211,6 +217,13 @@ TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics) // The first CryptoSession should have initialized OEMCrypto. This is right // after that, so should tell oemcrypto to use a test keybox. if (session_count() == 1) { + if (!initialized()) { + // If not initialized, try again and see if we are just missing a keybox. + // Since we plan to install a test keybox, we can ignore keybox errors. + const OEMCryptoResult status = ::OEMCrypto_Initialize(); + if (status != OEMCrypto_SUCCESS) return; + OverrideInitializedForTesting(true); + } WvCdmTestBase::InstallTestRootOfTrust(); } } @@ -262,12 +275,14 @@ void WvCdmTestBase::SetUp() { } void WvCdmTestBase::InstallTestRootOfTrust() { + const wvoec::WidevineKeybox& test_keybox = + use_qa_test_keybox_ ? wvoec::kQATestKeybox : wvoec::kTestKeybox; switch (wvoec::global_features.derive_key_method) { case wvoec::DeviceFeatures::LOAD_TEST_KEYBOX: ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox( - reinterpret_cast(&wvoec::kTestKeybox), - sizeof(wvoec::kTestKeybox))); + reinterpret_cast(&test_keybox), + sizeof(test_keybox))); break; case wvoec::DeviceFeatures::LOAD_TEST_RSA_KEY: // Rare case: used by devices with baked in DRM cert. @@ -451,6 +466,10 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[], is_cast_receiver = true; } else if (arg == "--fake_sleep") { wvcdm::TestSleep::set_real_sleep(false); + } else if (arg == "--qa_provisioning") { + use_qa_test_keybox_ = true; + default_config_.set_provisioning_service_certificate( + default_config_.QAProvisioningServiceCertificate()); } else if (arg.find("--gtest") == 0) { // gtest arguments will be passed to gtest by the main program. continue; diff --git a/libwvdrmengine/cdm/core/test/test_base.h b/libwvdrmengine/cdm/core/test/test_base.h index 4293bd97..58cea6c9 100644 --- a/libwvdrmengine/cdm/core/test/test_base.h +++ b/libwvdrmengine/cdm/core/test/test_base.h @@ -67,6 +67,9 @@ class WvCdmTestBase : public ::testing::Test { // arguments before any tests are created. static ConfigTestEnv default_config_; + // If the tests should use the QA test keybox. + static bool use_qa_test_keybox_; + // Configuration for an individual test. This is initialized to be the // default configuration, but can be modified by the test itself. ConfigTestEnv config_; diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 5364eb2c..d57de31e 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -1186,6 +1186,9 @@ void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os) { case OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION: *os << "OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION"; break; + case OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING: + *os << "ERROR_NEEDS_KEYBOX_PROVISIONING"; + break; // ODK Values. case ODK_ERROR_CORE_MESSAGE: *os << "CORE_MESSAGE"; diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index d37ee0f6..2f12234b 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -97,6 +97,10 @@ test_name := initialization_data_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := keybox_ota_test +test_src_dir := ../core/test +include $(LOCAL_PATH)/unit-test.mk + test_name := license_keys_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 4b8dc03b..6efc97ac 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -624,6 +624,9 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_MinorAPIVersion _oecc108 #define OEMCrypto_AllocateSecureBuffer _oecc109 #define OEMCrypto_FreeSecureBuffer _oecc110 +// Reserved 111-112. +#define OEMCrypto_GenerateOTARequest _oecc113 +#define OEMCrypto_ProcessOTAKeybox _oecc114 // clang-format on /// @addtogroup initcontrol @@ -2963,7 +2966,24 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void); * @retval OEMCrypto_ERROR_INVALID_RSA_KEY * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * @threading + * On devices that support OEMCrypto_GenerateOTARequest and + * OEMCrypto_ProcessOTAKeybox, this function may return + * OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING when a valid keybox is not + * present. + * + * Parameters: + * none + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC + * OEMCrypto_ERROR_KEYBOX_INVALID + * OEMCrypto_ERROR_INVALID_RSA_KEY + * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING + * + * Threading: * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto @@ -4719,6 +4739,93 @@ OEMCryptoResult OEMCrypto_FreeSecureBuffer( /// @} +/****************************************************************************/ +/****************************************************************************/ +/* The following functions are optional. They are only used if the device + * supports OTA keybox provisioning. Widevine does not allow all devices to + * support OTA provisioning. Using an OTA provisioned keybox usually lowers a + * device's security profile in the DCSL. Please work with your Widevine Partner + * Engineer before implementing these functions to make sure you understand the + * security implications of using Keybox OTA Provisioning. + */ + +/* + * OEMCrypto_GenerateOTARequest + * + * Description: + * Generate an OTA Keybox provisioning request. The format of the + * message is specified in the document Keybox OTA Reprovisioning. If + * use_test_key is true, then the debug model key and id should be + * used. Widevine does not allow all devices to support OTA + * provisioning. Using an OTA provisioned keybox usually lowers a device's + * security profile in the DCSL. + * + * Parameters: + * [out] buffer: where the provisioning request is stored. + * [in/out] buffer_length: length of the request, in bytes. + * [in] use_test_key: If true, use the debug model key. This is used for + * testing the workflow. + * + * Returns: + * OEMCrypto_SUCCESS on success + * OEMCrypto_ERROR_SHORT_BUFFER - if buffer_length is too small. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * Any other error will be logged. + * + * Threading: + * This is an "Initialization and Termination Function" and will not be called + * simultaneously with any other function, as if the CDM holds a write lock on + * the OEMCrypto system. It will be called only after + * OEMCrypto_IsKeyboxOrOEMCertValid() returns + * OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING immediately after initialization, + * and before any session is opened. + * + * Version: + * This method is new in API version 16. + */ +OEMCryptoResult OEMCrypto_GenerateOTARequest(uint8_t* buffer, + size_t* buffer_length, + bool use_test_key); +/* + * OEMCrypto_ProcessOTAKeybox + * + * Description: + * The buffer will be parsed as an OTA Keybox provisioning message, as + * described in the document OTA Keybox Reprovisioning. The + * signature will be verified. The keybox will be decrypted and verified. If + * use_test_key is false, the keybox will be installed permanently. + * + * If use_test_keybox is true, do not use the real model key, use the debug + * model key specified in OTA Keybox Reprovisioning. + * + * Parameters: + * [in] buffer: pointer to provisioning response. + * [in] buffer_length: length of the buffer, in bytes. + * [in] use_test_key: If true, use the debug model key. This is used for + * testing the workflow. + * + * Returns: + * OEMCrypto_SUCCESS on success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_SIGNATURE_FAILURE - signature of message was wrong. + * OEMCrypto_ERROR_KEYBOX_INVALID - if the keybox was unpacked, but is + * invalid. + * OEMCrypto_ERROR_WRITE_KEYBOX - could not save keybox. + * Any other error will be logged. + * + * Threading: + * This is an "Initialization and Termination Function" and will not be called + * simultaneously with any other function, as if the CDM holds a write lock on + * the OEMCrypto system. It will only be called after + * OEMCrypto_GenerateOTARequest. + * + * Version: + * This method is new in API version 16. + */ +OEMCryptoResult OEMCrypto_ProcessOTAKeybox(const uint8_t* buffer, + size_t buffer_length, + bool use_test_key); + /****************************************************************************/ /****************************************************************************/ /* The following functions are deprecated. They are not required for the diff --git a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h index d279c5c4..b1ad6e97 100644 --- a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -88,6 +88,7 @@ typedef enum OEMCryptoResult { OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES = 58, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION = 59, OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION = 60, + OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING = 61, /* ODK return values */ ODK_ERROR_BASE = 1000, ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE, diff --git a/libwvdrmengine/oemcrypto/test/common.mk b/libwvdrmengine/oemcrypto/test/common.mk index daf7c0e4..698a7d77 100644 --- a/libwvdrmengine/oemcrypto/test/common.mk +++ b/libwvdrmengine/oemcrypto/test/common.mk @@ -18,6 +18,7 @@ LOCAL_SRC_FILES:= \ oemcrypto_test.cpp \ oemcrypto_test_android.cpp \ oemcrypto_test_main.cpp \ + ota_keybox_test.cpp \ wvcrc.cpp \ ../../cdm/util/test/test_sleep.cpp \ diff --git a/libwvdrmengine/oemcrypto/test/oec_test_data.h b/libwvdrmengine/oemcrypto/test/oec_test_data.h index fbd202da..453847b5 100644 --- a/libwvdrmengine/oemcrypto/test/oec_test_data.h +++ b/libwvdrmengine/oemcrypto/test/oec_test_data.h @@ -18,6 +18,7 @@ namespace wvoec { // This is a test keybox. It will not be accepted by production systems. By // using a known keybox for these tests, the results for a given set of inputs // to a test are predictable and can be compared to the actual results. +// clang-format off static const WidevineKeybox kTestKeybox = { // Sample keybox used for test vectors { @@ -49,6 +50,43 @@ static const WidevineKeybox kTestKeybox = { 0x39, 0xf2, 0x94, 0xa7, } }; +// clang-format on + +// This test keybox is only accepted by the QA provisioning server. +// It is not valid with the production provisioning server. +// clang-format off +static const WidevineKeybox kQATestKeybox = { + // Sample keybox used for test vectors + { + // deviceID = WidevineQATestOnlyKeybox000 + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, + 0x51, 0x41, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x6e, + 0x6c, 0x79, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x78, + 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + }, { + // key + 0x03, 0x77, 0x0f, 0x4e, 0x29, 0x77, 0x4b, 0x43, + 0x9e, 0xd2, 0x8a, 0x94, 0x73, 0xb3, 0x26, 0x65, + }, { + // data (system ID 2000000 = 0x1E8480). + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x84, 0x80, + 0x90, 0x46, 0x8a, 0x1d, 0x27, 0x52, 0xca, 0xdb, + 0x5b, 0xf4, 0x67, 0xcb, 0xd3, 0x5e, 0x9e, 0xe9, + 0xb1, 0xcf, 0x89, 0x74, 0x08, 0x26, 0x96, 0x5b, + 0x43, 0x02, 0x7c, 0xb6, 0x4a, 0x9d, 0xf6, 0x7e, + 0x24, 0x82, 0x1d, 0xe2, 0x89, 0x52, 0x8e, 0xac, + 0xf2, 0x98, 0xac, 0x92, 0xa9, 0x40, 0x11, 0x9f, + 0x9f, 0xf8, 0x55, 0x84, 0x42, 0x04, 0x34, 0xbc, + 0x53, 0x14, 0x3d, 0x44, 0x97, 0x5c, 0xd9, 0xb4, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x43, 0xa2, 0x67, 0x63, + } +}; +// clang-format on // A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format // Used to verify the functions that manipulate RSA keys. diff --git a/libwvdrmengine/oemcrypto/test/ota_keybox_test.cpp b/libwvdrmengine/oemcrypto/test/ota_keybox_test.cpp new file mode 100644 index 00000000..1ddaf655 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/ota_keybox_test.cpp @@ -0,0 +1,53 @@ +// 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 "OEMCryptoCENC.h" +#include "clock.h" +#include "log.h" +#include "oec_decrypt_fallback_chain.h" +#include "oec_device_features.h" +#include "oec_session_util.h" +#include "oec_test_data.h" +#include "oemcrypto_session_tests_helper.h" +#include "oemcrypto_types.h" +#include "platform.h" +#include "string_conversions.h" +#include "test_sleep.h" + +using namespace std; + +namespace wvoec { +class OTAKeyboxProvisioningTest : public ::testing::Test, public SessionUtil { + protected: + void SetUp() override { + ::testing::Test::SetUp(); + wvcdm::TestSleep::SyncFakeClock(); + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); + OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + } + + void TearDown() override { + OEMCrypto_Terminate(); + ::testing::Test::TearDown(); + } +}; + +TEST_F(OTAKeyboxProvisioningTest, BasicTest) { + OEMCryptoResult result = OEMCrypto_IsKeyboxValid(); + if (result == OEMCrypto_SUCCESS) { + cout << " " + << "Keybox valid after initialization. Skipping rest of test." << endl; + return; + } + ASSERT_EQ(result, OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING); + cout << " " + << "OTA Keybox functions supported. Device needs provisioning." << endl; +} +} // namespace wvoec