diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 883370a6..f1ed6d15 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -309,6 +309,12 @@ class CdmEngine { return CryptoSession::SetDebugIgnoreKeyboxCount(count); } + // This tells the OEMCrypto adapter to allow the device to continue with a + // test keybox. Otherwise, the keybox is reported as invalid. + static CdmResponseType SetAllowTestKeybox(bool allow) { + return CryptoSession::SetAllowTestKeybox(allow); + } + static CdmResponseType ParseDecryptHashString(const std::string& hash_string, CdmSessionId* id, uint32_t* frame_number, diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index ef9e6fa3..a0e87231 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -299,6 +299,10 @@ class CryptoSession { // report that it needs provisioning instead. static CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count); + // This tells the OEMCrypto adapter to allow the device to continue with a + // test keybox. Otherwise, the keybox is reported as invalid. + static CdmResponseType SetAllowTestKeybox(bool allow); + // Returns a system-wide singleton instance of SystemFallbackPolicy // to be used for communicating OTA keybox provisioning state between // apps. Returns a null pointer if OTA provisioning is not supported, @@ -322,13 +326,10 @@ class CryptoSession { explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics); int session_count() const { return session_count_; } - bool initialized() const { return initialized_; } - void set_initialized(bool initialized) { initialized_ = initialized; } // Cache api version and fallback policy. Call this once at initialization. void CacheVersion(); - void OverrideNeedKeyboxForTesting(bool needs_keybox_provisioning) { - needs_keybox_provisioning_ = needs_keybox_provisioning; - } + // Re-initialize for running tests with a test keybox. + void ReinitializeForTest(); private: friend class CryptoSessionForTest; diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h index 1687fd11..0c44ac5c 100644 --- a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -19,6 +19,10 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( // report that it needs provisioning instead. OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count); +// This tells the OEMCrypto adapter to allow the device to continue with a +// test keybox. Otherwise, the keybox is reported as invalid. +OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow); + // This attempts to open a session at the desired security level. // If one level is not available, the other will be used instead. OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 839371d7..5c1d5fe7 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -358,6 +358,21 @@ void CryptoSession::Init() { } } +void CryptoSession::ReinitializeForTest() { + if (initialized_) { + initialized_ = false; + if (OEMCrypto_SUCCESS != OEMCrypto_Terminate()) return; + } + // Give up if we cannot initialize at all. + if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) return; + initialized_ = true; + // For integration and unit tests we will install a test keybox and do not + // need to do keybox provisioning. + needs_keybox_provisioning_ = false; + // This was skipped in Init because initialization failed. + CacheVersion(); +} + void CryptoSession::CacheVersion() { uint32_t version; std::string api_version = @@ -3061,6 +3076,11 @@ CdmResponseType CryptoSession::SetDebugIgnoreKeyboxCount(uint32_t count) { return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetDebugIgnoreKeyboxCount"); } +CdmResponseType CryptoSession::SetAllowTestKeybox(bool allow) { + OEMCryptoResult status = OEMCrypto_SetAllowTestKeybox(allow); + return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetAllowTestKeybox"); +} + okp::SystemFallbackPolicy* CryptoSession::GetOkpFallbackPolicy() { const auto getter = [&]() -> okp::SystemFallbackPolicy* { // If not set, then OTA keybox provisioning is not supported or diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index ec7e7522..d693c1d0 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -646,6 +646,17 @@ std::string GetIgnoreCountFile() { return path; } +std::string GetAllowTestKeyboxFile() { + std::string path; + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL1, + &path)) { + LOGW("GetAllowTestKeyboxFile: Unable to get base path"); + path = "/data/"; + } + path += "debug_allow_test_keybox.txt"; + return path; +} + uint32_t GetDebugIgnoreKeyboxCount() { const std::string filename = GetIgnoreCountFile(); wvcdm::FileSystem file_system; @@ -698,6 +709,49 @@ OEMCryptoResult SetDebugIgnoreKeyboxCount(uint32_t count) { return OEMCrypto_SUCCESS; } +bool GetAllowTestKeybox() { + const std::string filename = GetAllowTestKeyboxFile(); + wvcdm::FileSystem file_system; + if (!file_system.Exists(filename)) { + return 0; + } + auto file = file_system.Open(filename, file_system.kReadOnly); + if (!file) { + LOGE("Error opening %s", filename.c_str()); + return 0; + } + ssize_t size = file_system.FileSize(filename); + std::string contents(size, ' '); + ssize_t size_read = file->Read(const_cast(contents.data()), size); + if (size != size_read) { + LOGE("Short allow_test_keybox = %zu", size_read); + return 0; + } + // skip whitespace or any extra garbage. + return (std::string::npos != contents.find("true")); +} + +OEMCryptoResult SetAllowTestKeybox(bool allow) { + const std::string filename = GetAllowTestKeyboxFile(); + wvcdm::FileSystem file_system; + auto file = + file_system.Open(filename, file_system.kCreate | file_system.kTruncate); + if (!file) { + LOGE("Could not create file %s", filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::string contents = allow ? "true\n" : "false\n"; + const size_t size = contents.size(); + ssize_t size_written = file->Write(contents.data(), size); + if (static_cast(size) != size_written) { + LOGE("Wrote %zd bytes of %s, not %zd, to file %s", size_written, + contents.c_str(), size, filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + LOGD("Wrote %s to %s", contents.c_str(), filename.c_str()); + return OEMCrypto_SUCCESS; +} + struct LevelSession { FunctionPointers* fcn; OEMCrypto_SESSION session; @@ -821,6 +875,7 @@ class Adapter { level1_.BuildInformation()); } } else { + level1_failed_ = true; FallBackToLevel3(); } return result; @@ -842,7 +897,6 @@ class Adapter { Level1Terminate(); level1_ = FunctionPointers(); // revert to all null pointers. level1_valid_ = false; - level1_failed_ = true; // Note: if the function pointers are bad, we do not close the library and // try again later. Instead, we permanently fall back to L3. This is a // debatable choice: I decided the risk of a dlclose resource leak out @@ -1168,23 +1222,32 @@ class Adapter { return result; } + // Check the system ID of the keybox. This should only be called if the device + // uses provisioning 2.0. + bool UsingTestKeybox() { + uint8_t key_data[256]; + size_t key_data_len = sizeof(key_data); + OEMCryptoResult sts = OEMCrypto_GetKeyData(key_data, &key_data_len); + if (sts != OEMCrypto_SUCCESS) return true; + uint32_t* data = reinterpret_cast(key_data); + uint32_t system_id = htonl(data[1]); + return system_id == 7912; + } + // 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() { + // then return an error code. The caller should fall back to L3. + OEMCryptoResult ValidateOrInstallL1KeyboxOrCert() { 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; + return OEMCrypto_ERROR_NOT_IMPLEMENTED; } if (!level1_.IsKeyboxOrOEMCertValid) { // TODO(b/189989043): add metrics. LOGE("L1 invalid function pointers. Falling back to L3"); - FallBackToLevel3(); - return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid() - : OEMCrypto_ERROR_NOT_IMPLEMENTED; + return 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. @@ -1195,6 +1258,19 @@ class Adapter { const OEMCrypto_ProvisioningMethod provisioning_method = level1_.GetProvisioningMethod ? level1_.GetProvisioningMethod() : OEMCrypto_Keybox; + // For production systems, we do wish to use a test keybox. We do not force + // a fallback to L3 at this point, because this can be overridden by test + // code that requires a test keybox. + if ((rot_valid == OEMCrypto_SUCCESS) && + (provisioning_method == OEMCrypto_Keybox) && UsingTestKeybox()) { + if (GetAllowTestKeybox()) { + LOGW("Allowing device with test keybox installed."); + } else { + LOGW("Device has test keybox installed."); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + } + 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. @@ -1232,9 +1308,7 @@ class Adapter { wvcdm::metrics:: OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX); } - FallBackToLevel3(); - return level3_.IsKeyboxOrOEMCertValid ? level3_.IsKeyboxOrOEMCertValid() - : OEMCrypto_ERROR_NOT_IMPLEMENTED; + return file_attempt; } bool IsOTAKeyboxSupported() { @@ -1348,7 +1422,7 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( // continue on. if (status != OEMCrypto_SUCCESS) return status; const OEMCryptoResult keybox_status = - gAdapter->ValidateOrInstallKeyboxOrCert(); + gAdapter->ValidateOrInstallL1KeyboxOrCert(); uint32_t ignore_count = GetDebugIgnoreKeyboxCount(); if (keybox_status == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING || ignore_count > 0) { @@ -1365,7 +1439,13 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( return OEMCrypto_SUCCESS; } } - return keybox_status; + if (keybox_status == OEMCrypto_SUCCESS) { + return OEMCrypto_SUCCESS; + } + LOGW("Keybox error: %d. Falling back to L3.", keybox_status); + gAdapter->FallBackToLevel3(); + // Return success if the L3 keybox or cert is valid. + return OEMCrypto_IsKeyboxOrOEMCertValid(); } OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, @@ -1649,6 +1729,9 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert, OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) { return SetDebugIgnoreKeyboxCount(count); } +OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow) { + return SetAllowTestKeybox(allow); +} } // namespace wvcdm extern "C" OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id, diff --git a/libwvdrmengine/cdm/core/test/test_base.cpp b/libwvdrmengine/cdm/core/test/test_base.cpp index 38f401b5..5f775393 100644 --- a/libwvdrmengine/cdm/core/test/test_base.cpp +++ b/libwvdrmengine/cdm/core/test/test_base.cpp @@ -225,16 +225,8 @@ TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics) // The first CryptoSession should have initialized OEMCrypto. This is right // after that, so we should tell oemcrypto to use a test keybox. if (session_count() == 1) { - OverrideNeedKeyboxForTesting(false); - // However, if the device does not have a keybox, initialization would have - // failed. In that case we should try again. - if (!initialized()) { - // Give up if we cannot initialize at all. - if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) return; - set_initialized(true); - // This was skipped in Init because initialization failed. - CacheVersion(); - } + CryptoSession::SetAllowTestKeybox(true); + ReinitializeForTest(); WvCdmTestBase::InstallTestRootOfTrust(); } } diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index e87d7890..0d91b3bb 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -506,8 +506,7 @@ CdmResponseType WvContentDecryptionModule::SetDebugIgnoreKeyboxCount( } CdmResponseType WvContentDecryptionModule::SetAllowTestKeybox(bool allow) { - // TODO(210807585) add functionality in next CL. - return NO_ERROR; + return CdmEngine::SetAllowTestKeybox(allow); } CdmResponseType WvContentDecryptionModule::SetDecryptHash(