diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 58c2c792..13264b65 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -112,6 +112,9 @@ class CryptoSession { // Only valid for OEM certificate-based based devices. virtual CdmResponseType GetTokenFromOemCert( RequestedSecurityLevel requested_security_level, std::string* oem_cert); + // Retrieves the embedded public certificate from OEMCrypto. + // Only valid for L3 devices with embedded (baked-in) certificates. + virtual CdmResponseType GetTokenFromEmbeddedCertificate(std::string* token); // The overloaded methods with |requested_level| may be called // without a preceding call to Open. The other method must call Open first. diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 15fd82c7..a5b90fd7 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -463,6 +463,7 @@ enum CdmResponseEnum : int32_t { SESSION_NOT_FOUND_24 = 397, GET_DEVICE_INFORMATION_ERROR = 398, GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR = 399, + GET_TOKEN_FROM_EMBEDDED_CERT_ERROR = 400, // Don't forget to add new values to // * core/src/wv_cdm_types.cpp // * android/include/mapErrors-inl.h diff --git a/libwvdrmengine/cdm/core/src/client_identification.cpp b/libwvdrmengine/cdm/core/src/client_identification.cpp index 212d5115..86aa9fff 100644 --- a/libwvdrmengine/cdm/core/src/client_identification.cpp +++ b/libwvdrmengine/cdm/core/src/client_identification.cpp @@ -104,7 +104,8 @@ CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning( /* * Return the ClientIdentification message token type for provisioning request. - * NOTE: a DRM Cert should never be presented to the provisioning server. + * NOTE: a DRM Cert should never be presented to the provisioning server unless + * DRM re-provisioning is being used. */ CdmResponseType ClientIdentification::Prepare( const CdmAppParameterMap& app_parameters, @@ -403,9 +404,11 @@ bool ClientIdentification::GetProvisioningTokenType( } return true; } - case kClientTokenDrmCert: - // TODO: b/305093063 - Add token for DRM reprovisioning requests. case kClientTokenDrmReprovisioning: + *token_type = + video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE; + return true; + case kClientTokenDrmCert: default: // shouldn't happen LOGE("Unexpected provisioning type: %d", static_cast(token)); diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index eb1efb39..77cc1ab8 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -81,6 +81,7 @@ extern "C" OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION session, #endif namespace wvcdm { + namespace { using UsageTableLock = std::unique_lock; @@ -346,6 +347,9 @@ CdmResponseType CryptoSession::GetProvisioningMethod( case OEMCrypto_BootCertificateChain: type = kClientTokenBootCertChain; break; + case OEMCrypto_DrmReprovisioning: + type = kClientTokenDrmReprovisioning; + break; case OEMCrypto_ProvisioningError: default: if (static_cast(method) == 0 && needs_keybox_provisioning_) { @@ -662,6 +666,8 @@ CdmResponseType CryptoSession::GetProvisioningToken( } else if (pre_provision_token_type_ == kClientTokenBootCertChain) { status = GetBootCertificateChain(requested_security_level, token, additional_token); + } else if (pre_provision_token_type_ == kClientTokenDrmReprovisioning) { + status = GetTokenFromEmbeddedCertificate(token); } metrics_->crypto_session_get_token_.Increment(status); return status; @@ -1253,6 +1259,7 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest( } OEMCryptoResult sts; + // TODO: b/305093063 - Refactor to a switch statement to improve readability if (pre_provision_token_type_ == kClientTokenKeybox) { should_specify_algorithm = false; } else if (pre_provision_token_type_ == kClientTokenOemCert) { @@ -1268,6 +1275,10 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest( should_specify_algorithm = true; // Do nothing here. The key to signing the provisioning 4.0 request for each // stage has been loaded already when it was generated by OEMCrypto. + } else if (pre_provision_token_type_ == kClientTokenDrmReprovisioning) { + should_specify_algorithm = false; + // Do nothing here. The baked-in certificate used as the token has already + // been loaded when the EncryptedClientId was filled in. } else { LOGE("Unknown method %d", pre_provision_token_type_); return CdmResponseType(UNKNOWN_CLIENT_TOKEN_TYPE); @@ -1439,6 +1450,59 @@ CdmResponseType CryptoSession::GetBootCertificateChain( return CdmResponseType(NO_ERROR); } +CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate( + std::string* token) { + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); + RETURN_IF_NULL(token, PARAMETER_NULL); + + CdmClientTokenType token_type = kClientTokenUninitialized; + const CdmResponseType sts = + GetProvisioningMethod(requested_security_level_, &token_type); + if (sts != NO_ERROR) { + LOGE("Failed to get token type"); + return sts; + } + if (token_type != kClientTokenDrmReprovisioning) { + token->clear(); + return CdmResponseType(NO_ERROR); + } + + size_t certificate_length = CERTIFICATE_DATA_SIZE; + std::string embedded_certificate(certificate_length, '\0'); + OEMCryptoResult status = + WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 1", [&] { + return OEMCrypto_GetEmbeddedDrmCertificate( + reinterpret_cast(&embedded_certificate.front()), + &certificate_length); + }); + + if (status == OEMCrypto_ERROR_SHORT_BUFFER) { + embedded_certificate.assign(certificate_length, '\0'); + status = + WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 2", [&] { + return OEMCrypto_GetEmbeddedDrmCertificate( + reinterpret_cast(&embedded_certificate.front()), + &certificate_length); + }); + } + + if (status == OEMCrypto_SUCCESS) { + // TODO: b/312782308 - Store embedded certificate as SignedDrmCertificate + video_widevine_client::sdk::HashedFile hashed_file; + video_widevine_client::sdk::File file; + embedded_certificate.resize(certificate_length); + if (hashed_file.ParseFromString(embedded_certificate) && + file.ParseFromString(hashed_file.file())) { + *token = file.device_certificate().certificate(); + return CdmResponseType(NO_ERROR); + } + } + + token->clear(); + return MapOEMCryptoResult(status, GET_TOKEN_FROM_EMBEDDED_CERT_ERROR, + "GetTokenFromEmbeddedCertificate"); +} + CdmResponseType CryptoSession::GetDeviceInformation( RequestedSecurityLevel requested_security_level, std::string* device_info) { RETURN_IF_NULL(device_info, PARAMETER_NULL); diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index c661f0cd..fba2ee09 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -3191,3 +3191,16 @@ extern "C" OEMCrypto_WatermarkingSupport OEMCrypto_GetWatermarkingSupport( extern "C" OEMCryptoResult OEMCrypto_ProductionReady(void) { return wvcdm::OEMCrypto_ProductionReady(kLevelDefault); } + +extern "C" OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate( + uint8_t* public_cert, size_t* public_cert_length) { + if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault); + if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + // Only supported for internal L3. Ignore L1 calls. + if (fcn->security_level != wvcdm::kSecurityLevelL3) { + if (public_cert_length) *public_cert_length = 0; + return OEMCrypto_SUCCESS; + } + return Level3_GetEmbeddedDrmCertificate(public_cert, public_cert_length); +} diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_embedded_cert_stubs.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_embedded_cert_stubs.cpp new file mode 100644 index 00000000..8f6362bb --- /dev/null +++ b/libwvdrmengine/cdm/core/src/oemcrypto_embedded_cert_stubs.cpp @@ -0,0 +1,12 @@ +// Copyright 2024 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" + +OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate( + uint8_t* public_cert, size_t* public_cert_length) { + (void)public_cert; + (void)public_cert_length; + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/libwvdrmengine/cdm/core/src/system_id_extractor.cpp b/libwvdrmengine/cdm/core/src/system_id_extractor.cpp index b3cc7473..791ace5f 100644 --- a/libwvdrmengine/cdm/core/src/system_id_extractor.cpp +++ b/libwvdrmengine/cdm/core/src/system_id_extractor.cpp @@ -59,7 +59,7 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) { bool success = false; switch (type) { case kClientTokenDrmCert: - // TODO: b/305093063 - Extract system id when handling DRM reprovisioning. + // TODO: b/309675153 - Extract system id when using DRM reprovisioning. case kClientTokenDrmReprovisioning: LOGW( "Cannot get a system ID from a DRM certificate, " diff --git a/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp b/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp index ad5b61e7..b31cb1b6 100644 --- a/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp +++ b/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp @@ -749,6 +749,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) { return "GET_TOKEN_FROM_KEYBOX_ERROR"; case KEYBOX_TOKEN_TOO_SHORT: return "KEYBOX_TOKEN_TOO_SHORT"; + case GET_TOKEN_FROM_EMBEDDED_CERT_ERROR: + return "GET_TOKEN_FROM_EMBEDDED_CERT_ERROR"; case EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR: return "EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR"; case RSA_SIGNATURE_GENERATION_ERROR: diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 9ab7598b..274015ec 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -21,6 +21,8 @@ #include "license_holder.h" #include "license_request.h" #include "log.h" +// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. +#include "oec_device_features.h" #include "properties.h" #include "string_conversions.h" #include "test_base.h" @@ -152,6 +154,12 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { WvCdmEngineTest() {} void SetUp() override { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } WvCdmEnginePreProvTest::SetUp(); session_opened_ = false; WvCdmEnginePreProvTest::OpenSession(); diff --git a/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp b/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp index 6f0bbb2c..cf5ccbd9 100644 --- a/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp @@ -481,8 +481,8 @@ TEST_P(CertificateProvisioningTest, ProvisioningResponseSuccess) { INSTANTIATE_TEST_SUITE_P( CertificateProvisioningTests, CertificateProvisioningTest, - // TODO: b/305093063 - Add Drm Reprovisioning to Values once implemented. - testing::Values(kClientTokenKeybox, kClientTokenOemCert), + testing::Values(kClientTokenKeybox, kClientTokenOemCert, + kClientTokenDrmReprovisioning), [](const testing::TestParamInfo& param_type) { return CdmClientTokenTypeToString(param_type.param); diff --git a/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp b/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp index 0a5545a2..138cbff2 100644 --- a/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp @@ -99,6 +99,9 @@ TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) { } else if (token_type == kClientTokenBootCertChain) { EXPECT_EQ(OEMCrypto_BootCertificateChain, metrics_proto.oemcrypto_provisioning_method().int_value()); + } else if (token_type == kClientTokenDrmReprovisioning) { + EXPECT_EQ(OEMCrypto_DrmReprovisioning, + metrics_proto.oemcrypto_provisioning_method().int_value()); } else { FAIL() << "Unexpected token type: " << token_type; } @@ -140,6 +143,9 @@ TEST_F(CryptoSessionMetricsTest, GetProvisioningTokenValidMetrics) { } else if (token_type == kClientTokenBootCertChain) { EXPECT_EQ(OEMCrypto_BootCertificateChain, metrics_proto.oemcrypto_provisioning_method().int_value()); + } else if (token_type == kClientTokenDrmReprovisioning) { + EXPECT_EQ(OEMCrypto_DrmReprovisioning, + metrics_proto.oemcrypto_provisioning_method().int_value()); } else { ASSERT_EQ(0, metrics_proto.crypto_session_get_token().size()); } diff --git a/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp b/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp index 90e2de18..94fe365c 100644 --- a/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/generic_crypto_unittest.cpp @@ -40,6 +40,12 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine { if (!wvoec::global_features.generic_crypto) { GTEST_SKIP() << "Test for devices with generic crypto API only"; } + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } EnsureProvisioned(); ASSERT_NO_FATAL_FAILURE(holder_.OpenSession()); ASSERT_NO_FATAL_FAILURE(holder_.FetchLicense()); @@ -68,7 +74,11 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine { iv_ = std::string(iv_vector_.begin(), iv_vector_.end()); } - void TearDown() override { holder_.CloseSession(); } + void TearDown() override { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (IsSkipped()) return; + holder_.CloseSession(); + } protected: LicenseHolder holder_; diff --git a/libwvdrmengine/cdm/core/test/test_base.cpp b/libwvdrmengine/cdm/core/test/test_base.cpp index 1464029b..a72c92d8 100644 --- a/libwvdrmengine/cdm/core/test/test_base.cpp +++ b/libwvdrmengine/cdm/core/test/test_base.cpp @@ -374,6 +374,12 @@ void WvCdmTestBase::Provision() { } void WvCdmTestBase::EnsureProvisioned() { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } CdmSessionId session_id; std::unique_ptr file_system(CreateTestFileSystem()); // OpenSession will check if a DRM certificate exists, while diff --git a/libwvdrmengine/level3/src/level3_adapter.cpp b/libwvdrmengine/level3/src/level3_adapter.cpp index b8934ba6..faedf43e 100644 --- a/libwvdrmengine/level3/src/level3_adapter.cpp +++ b/libwvdrmengine/level3/src/level3_adapter.cpp @@ -70,6 +70,11 @@ OEMCryptoResult Level3_QueryKeyControl(OEMCrypto_SESSION session, key_control_block_length); } +OEMCryptoResult Level3_GetEmbeddedDrmCertificate( + uint8_t* public_cert, size_t* public_cert_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + OEMCryptoResult Level3_DecryptCENC_V17( OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples, size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 14853bc0..a1f25831 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -521,6 +521,8 @@ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_OEMCertificate = 3, // Device has Boot Certificate Chain (BCC). OEMCrypto_BootCertificateChain = 4, + // Device has baked in DRM certificate with reprovisioning (level 3 only). + OEMCrypto_DrmReprovisioning = 5 } OEMCrypto_ProvisioningMethod; /** @@ -740,6 +742,7 @@ typedef enum OEMCrypto_SignatureHashAlgorithm { #define OEMCrypto_GetUsageEntryInfo _oecc148 #define OEMCrypto_GetBCCType _oecc149 #define OEMCrypto_LoadRelease _oecc150 +#define OEMCrypto_GetEmbeddedDrmCertificate _oecc151 // clang-format on /// @addtogroup initcontrol @@ -6108,6 +6111,22 @@ OEMCryptoResult OEMCrypto_LoadProvisioning_V18( /****************************************************************************/ /****************************************************************************/ +/* The following functions are used by internal L3 CDMs and are not required by + * other CDM implementations. + */ + +/** + * Get the embedded Drm Certificate used by internal L3 CDMs. + * + * @param[out] public_cert where the certificate is stored. + * @param[in,out] public_cert_length the length, in bytes, of the certificate. + * + * @retval OEMCrypto_SUCCESS on success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if public_cert_length is too small. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + */ +OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate(uint8_t* public_cert, + size_t* public_cert_length); #ifdef __cplusplus } diff --git a/libwvdrmengine/oemcrypto/include/level3.h b/libwvdrmengine/oemcrypto/include/level3.h index 8b65a270..1ee7ac1d 100644 --- a/libwvdrmengine/oemcrypto/include/level3.h +++ b/libwvdrmengine/oemcrypto/include/level3.h @@ -125,6 +125,7 @@ #define Level3_GetUsageEntryInfo _lcc148 #define Level3_GetBCCType _lcc149 #define Level3_LoadRelease _lcc150 +#define Level3_GetEmbeddedDrmCertificate _lcc151 #else #define Level3_Initialize _oecc01 #define Level3_Terminate _oecc02 @@ -233,6 +234,8 @@ #define Level3_GetUsageEntryInfo _oecc148 #define Level3_GetBCCType _oecc149 #define Level3_LoadRelease _oecc150 +// Internal-only. +#define Level3_GetEmbeddedDrmCertificate _oecc151 #endif #define Level3_GetInitializationState _oecl3o01 @@ -527,6 +530,8 @@ OEMCryptoResult Level3_LoadProvisioningCast( size_t signature_length, uint8_t* wrapped_private_key, size_t* wrapped_private_key_length); OEMCryptoResult Level3_GetBCCType(OEMCrypto_BCCType* bcc_type); +OEMCryptoResult Level3_GetEmbeddedDrmCertificate(uint8_t* public_cert, + size_t* public_cert_length); // The following are specific to Google's Level 3 implementation and are not // required.