From 135d6c608dac7fb0aa6c0db201c96d9ac0c5aadf Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Wed, 15 May 2024 17:27:13 -0700 Subject: [PATCH 1/4] Update test data for entitled license test A new set of license data was created on UAT so that we could have keys that match those in the license returned by a License SDK and by those generated by UAT. It should be more clear now which data is just made up, and which data has to match some golden values based on the made up data. Bug: 338323091 Test: WVTS Change-Id: Ic112b4594afb99c6f43e011f59ee7592d4809189 --- .../cdm/core/src/initialization_data.cpp | 5 +++- libwvdrmengine/cdm/core/test/test_base.cpp | 30 +++++++++++++++++++ libwvdrmengine/cdm/core/test/test_base.h | 4 +++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index 373e5a96..cf87d24a 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -680,7 +680,7 @@ void InitializationData::DumpToLogs() const { if (!is_supported()) { LOGD("InitData: Not supported"); } - if (!IsEmpty()) { + if (IsEmpty()) { LOGD("InitData: Empty"); } std::string type_info = type(); @@ -736,6 +736,9 @@ void InitializationData::DumpToLogs() const { LOGD("InitData: entitlement_key_id %d: %s -> %s", i, wvutil::b2a_hex(key.entitlement_key_id()).c_str(), wvutil::b2a_hex(key.key_id()).c_str()); + LOGD("InitData: entitled_key %d: %s", i, + wvutil::b2a_hex(key.key()).c_str()); + LOGD("InitData: iv %d: %s", i, wvutil::b2a_hex(key.iv()).c_str()); } } diff --git a/libwvdrmengine/cdm/core/test/test_base.cpp b/libwvdrmengine/cdm/core/test/test_base.cpp index a72c92d8..e3e4e28c 100644 --- a/libwvdrmengine/cdm/core/test/test_base.cpp +++ b/libwvdrmengine/cdm/core/test/test_base.cpp @@ -189,6 +189,16 @@ enum OptionalBool { bool UnwrapOptionalBool(OptionalBool value, bool default_value) { return (value == kBoolUnset) ? default_value : (value == kBoolTrue); } + +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. This +// is different from the BoringSSL implementation, so we implement the CTR loop +// ourselves. +void ctr128_inc64(int64_t increaseBy, std::vector& iv) { + uint64_t* counterBuffer = reinterpret_cast(&(iv[8])); + (*counterBuffer) = + wvutil::htonll64(wvutil::ntohll64(*counterBuffer) + increaseBy); +} } // namespace // Static WvCdmTestBase variables. @@ -208,6 +218,26 @@ void WvCdmTestBase::StripeBuffer(std::vector* buffer, size_t size, } } +// Encrypt a block of data using CTR mode. +std::vector WvCdmTestBase::Aes128CtrEncrypt( + const std::vector& key, const std::vector& starting_iv, + const std::vector& in_buffer) { + AES_KEY aes_key; + AES_set_encrypt_key(key.data(), AES_BLOCK_SIZE * 8, &aes_key); + std::vector out_buffer(in_buffer.size()); + std::vector iv = starting_iv; + size_t l = 0; // byte index into encrypted subsample. + while (l < in_buffer.size()) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv.data(), aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { + out_buffer[l] = aes_output[n] ^ in_buffer[l]; + } + ctr128_inc64(1, iv); + } + return out_buffer; +} + std::string WvCdmTestBase::Aes128CbcEncrypt(std::vector key, const std::vector& clear, std::vector iv) { diff --git a/libwvdrmengine/cdm/core/test/test_base.h b/libwvdrmengine/cdm/core/test/test_base.h index d95cef40..646b9409 100644 --- a/libwvdrmengine/cdm/core/test/test_base.h +++ b/libwvdrmengine/cdm/core/test/test_base.h @@ -76,6 +76,10 @@ class WvCdmTestBase : public ::testing::Test { const std::vector& clear, std::vector iv); // Helper method for doing cryptography. + static std::vector Aes128CtrEncrypt( + const std::vector& key, const std::vector& starting_iv, + const std::vector& in_buffer); + // Helper method for doing cryptography. static std::string SignHMAC(const std::string& message, const std::vector& key); From b0bf7187f09c688e0b8ec775453bfb73ad8b520b Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Mon, 15 Jul 2024 16:38:19 -0700 Subject: [PATCH 2/4] Clang format long line Error not found in go/wvgerrit/199931 Change-Id: I58806276ab0a710444f6632d4c824607b8ebd17c --- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index ed4041c5..32d705e7 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -1510,7 +1510,8 @@ static WvStatus getDeviceSignedCsrPayload( } else { return toNdkScopedAStatus(Status::BAD_VALUE); } - } else if (name == "certificateSigningRequestChallenge" && isCsrAccessAllowed()) { + } else if (name == "certificateSigningRequestChallenge" && + isCsrAccessAllowed()) { mCertificateSigningRequestChallenge = std::string(_value.begin(), _value.end()); } else if (name == "deviceInfo" && isCsrAccessAllowed()) { From e642847b819ade7ef0d0f9ceb4266760b570014d Mon Sep 17 00:00:00 2001 From: Vicky Min Date: Wed, 14 Aug 2024 21:59:54 +0000 Subject: [PATCH 3/4] Update license holder to handle Android license releases Since the CDM engine handles license releases for CE CDM and Android differently, this changes the license release test to accomodate for that. Bug: 348712053 Change-Id: Ibc768e5d5c31ef8c2226b63dc622ffabfc0591fe --- .../cdm/core/test/license_holder.cpp | 24 ++++++++++++++--- .../cdm/core/test/policy_integration_test.cpp | 27 +++++++++++++++++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/libwvdrmengine/cdm/core/test/license_holder.cpp b/libwvdrmengine/cdm/core/test/license_holder.cpp index 7535b14b..de131c62 100644 --- a/libwvdrmengine/cdm/core/test/license_holder.cpp +++ b/libwvdrmengine/cdm/core/test/license_holder.cpp @@ -7,6 +7,7 @@ #include "license_request.h" #include "message_dumper.h" #include "oec_device_features.h" +#include "properties.h" #include "test_base.h" namespace wvcdm { @@ -126,10 +127,27 @@ void LicenseHolder::GenerateAndPostReleaseRequest( const std::string init_data_string = MakePSSH(pssh); const InitializationData init_data(kCencMimeType, init_data_string); init_data.DumpToLogs(); - const CdmResponseType result = cdm_engine_->GenerateKeyRequest( - session_id_, key_set_id_, init_data, kLicenseTypeRelease, - empty_app_parameters, &request); + + CdmSessionId session_id; + CdmKeySetId key_set_id; + CdmResponseType result; + // For Android when key set IDs are used, the key set ID passed in should have + // a value and the session ID should be empty. + if (!Properties::AlwaysUseKeySetIds()) { + key_set_id = key_set_id_; + result = cdm_engine_->OpenKeySetSession(key_set_id_, nullptr, nullptr); + ASSERT_EQ(NO_ERROR, result) << "Failed for " << content_id(); + // For CE CDM, we only need the session ID to be valid. + } else { + session_id = session_id_; + } + result = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, init_data, + kLicenseTypeRelease, + empty_app_parameters, &request); ASSERT_EQ(KEY_MESSAGE, result) << "Failed for " << content_id(); + if (!Properties::AlwaysUseKeySetIds()) { + cdm_engine_->CloseKeySetSession(key_set_id_); + } if (config_.dump_golden_data()) { // TODO (b/295956275) vickymin: write DumpReleaseRequest function // MessageDumper::DumpReleaseRequest(request); diff --git a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp index 814b6d07..0249aafe 100644 --- a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp +++ b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp @@ -19,6 +19,7 @@ #include "license_holder.h" #include "log.h" #include "oec_device_features.h" +#include "properties.h" #include "provisioning_holder.h" #include "test_base.h" #include "test_printers.h" @@ -193,13 +194,24 @@ TEST_F(CorePIGTest, LicenseRelease1) { ASSERT_NO_FATAL_FAILURE(holder.FetchLicense()); ASSERT_NO_FATAL_FAILURE(holder.LoadLicense()); EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id)); + // For Android where AlwaysUseKeySetIds() is false, the CDM engine generates + // a session separately. Thus, we close the session and only for CE CDM reopen + // it for the license release. + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); + if (Properties::AlwaysUseKeySetIds()) { + ASSERT_NO_FATAL_FAILURE(holder.OpenSession()); + ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense()); + } ASSERT_NO_FATAL_FAILURE(holder.GenerateAndPostReleaseRequest( "CDM_UnlimitedStreaming_can_persist")); EXPECT_NE(NO_ERROR, holder.Decrypt(key_id)); ASSERT_NO_FATAL_FAILURE(holder.FetchRelease()); ASSERT_NO_FATAL_FAILURE(holder.LoadRelease()); EXPECT_NE(NO_ERROR, holder.Decrypt(key_id)); - ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); + // For CE CDM, we can close the session after we have gotten the release. + if (Properties::AlwaysUseKeySetIds()) { + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); + } } /** @@ -219,11 +231,22 @@ TEST_F(CorePIGTest, LicenseRelease2) { ASSERT_NO_FATAL_FAILURE(holder.FetchLicense()); ASSERT_NO_FATAL_FAILURE(holder.LoadLicense()); wvutil::TestSleep::Sleep(10); + // For Android where AlwaysUseKeySetIds() is false, the CDM engine generates + // a session separately. Thus, we close the session and only for CE CDM reopen + // it for the license release. + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); + if (Properties::AlwaysUseKeySetIds()) { + ASSERT_NO_FATAL_FAILURE(holder.OpenSession()); + ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense()); + } ASSERT_NO_FATAL_FAILURE(holder.GenerateAndPostReleaseRequest( "CDM_UnlimitedStreaming_can_persist")); ASSERT_NO_FATAL_FAILURE(holder.FetchRelease()); ASSERT_NO_FATAL_FAILURE(holder.LoadRelease()); - ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); + // For CE CDM, we can close the session after we have gotten the release. + if (Properties::AlwaysUseKeySetIds()) { + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); + } } TEST_F(CorePIGTest, CastReceiverProvisioningUsingCdm) { From 51944dc3313f333069079bf591e74d5eaa2e6551 Mon Sep 17 00:00:00 2001 From: Vicky Min Date: Wed, 28 Aug 2024 17:22:40 +0000 Subject: [PATCH 4/4] Allow key_session to be set to oec_session when creating entitled key session Bug: 358042250 Change-Id: If636f252aec6c95e23a91ee410f9cadf0ebad5d9 (cherry picked from commit 232d01b83abb6fe9ff9f4148bf79dc5dab8ee946) --- libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index fba2ee09..5cfb47ac 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -1285,6 +1285,10 @@ class Adapter { OEMCryptoResult result = pair.fcn->CreateEntitledKeySession(pair.session, key_session); if (result == OEMCrypto_SUCCESS) { + if (pair.session == *key_session) { + *key_session = oec_session; + return result; + } // Copy everything from |pair| except session field. LevelSession new_session; new_session.fcn = pair.fcn;