diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index da643010..f431ccde 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -425,10 +425,12 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal( case kLicenseTypeRelease: is_release_ = true; break; - // TODO(jfore): CdmSession assumes a call to this method once a license has - // been received is a call to generate a license renewal message. Use of - // this enum differentiates the two calls. See "else if (license_received_)" - // below. + // TODO(b/132071885): Once a license has been received, CdmSession assumes + // that a call to this method is to generate a license renewal/release + // or key rotation. Key rotation can be indicated by specifing a license + // type kLicenseTypeEmbeddedKeyData or interrogating the PSSH + // (See "else if (license_received_)" below). b/132071885 is to evaluate + // whether both mechanisms are needed. case kLicenseTypeEmbeddedKeyData: return license_parser_->HandleEmbeddedKeyData(init_data); default: @@ -441,7 +443,7 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal( return GenerateReleaseRequest(key_request); } else if (license_received_) { // A call to GenerateKeyRequest after the initial license has been received - // is either a renewal request or a key rotation event + // is either a renewal/release request or a key rotation event if (init_data.contains_entitled_keys()) return license_parser_->HandleEmbeddedKeyData(init_data); else diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index 1cf0edb5..e8ebeb4d 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -126,10 +126,9 @@ bool InitializationData::SelectWidevinePssh(const CdmInitData& init_data, return false; } - // If there are multiple PSSHs to choose from and this device prefers - // entitlements, find the first |ENTITLED_KEY| PSSH, if present, and - // select it. - if (prefer_entitlements && pssh_payloads.size() > 1) { + // If this device prefers entitlements, search through available PSSHs. + // If present, select the first |ENTITLED_KEY| PSSH. + if (prefer_entitlements && !pssh_payloads.empty()) { for (size_t i = 0; i < pssh_payloads.size(); ++i) { WidevinePsshData pssh; if (!pssh.ParseFromString(pssh_payloads[i])) { @@ -146,12 +145,6 @@ bool InitializationData::SelectWidevinePssh(const CdmInitData& init_data, } } - WidevinePsshData pssh; - if (prefer_entitlements && pssh.ParseFromString(pssh_payloads[0])) { - if (pssh.type() == WidevinePsshData_Type_ENTITLED_KEY) - contains_entitled_keys_ = true; - } - // Either there is only one PSSH, this device does not prefer entitlements, // or no entitlement PSSH was found. Regardless of how we got here, select the // first PSSH, which should be a |SINGLE| PSSH. diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 9c0f3897..dcfee958 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -446,7 +446,55 @@ SubSampleInfo usage_info_sub_samples_icp[] = { wvcdm::a2b_hex("964c2dfda920357c668308d52d33c652"), 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}}; -SubSampleInfo entitlement_with_key_rotation_sub_sample[] = { +SubSampleInfo kEntitlementWithKeyRotationSubSampleSinglePssh[] = { + { + true, 1, true, true, false, + wvcdm::a2bs_hex("c8326486bb5d5c4a958f00b1111afc81"), + wvcdm::a2b_hex( + "64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36" + "17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d" + "7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab" + "ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c" + "2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4" + "9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4" + "6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012" + "c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"), + wvcdm::a2b_hex( + "cb468f067baa5634f83947b921fdb37e66bc5925edc4bdc2424db4c33cfd55a1" + "cd0a96b6634796ddcf5ba8820b83f80d318ff49b7e614779f5e87dd6dfadd8d6" + "cf95c7b0f21e4174e2e91371ebdb69866340a37a581a7c0713eda5168bcfc003" + "12a341a83defa754c4601772ed9171526720b6a2b2b084030f21ef13f2a35dec" + "e93f5c394c56d9ce108c2f5b0e5edb857d322fae24ec22f3ad726496b382306b" + "4fdf5a0a99efc2db2a9458f0bfd6b21869c9acf6ea222fe942af6cd9b38d9a50" + "a96db14ad27d368c5753aa5da8d8507603ed08086e2492bdff267ee64862f159" + "b19d2c72b2f5a39520d5ae2aacae1a192d375d45f3f9ba86d5026cbcacdfecbe"), + wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}, + { + true, 1, true, true, false, + wvcdm::a2bs_hex("f8488775a99855ff94b93ec5bd499356"), + wvcdm::a2b_hex( + "64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36" + "17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d" + "7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab" + "ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c" + "2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4" + "9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4" + "6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012" + "c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"), + wvcdm::a2b_hex( + "51b0133e2e5f40c22d10ec0562799e5e4118aacaa6a820be976acee4b7689280" + "541b56836c454414fbaebf9a3142baa2c8009a4f19ac033665bd3495f2ad19f3" + "3b850d1e8e6957172571e82d3c812d03588c95a1ef49f08b33f21ea4f2d382c9" + "28704e329847e5fe98966949f39e272ec30126f5d7a4ae21d3a0d25aa8ccf637" + "d5f880a6733b07bdd33cfdc3c36dece2bffb6049f218162b024df4f800557568" + "a792e0add16fc388ee13595313c3fbeef28f69737523e449dc2cf893f0566a79" + "8a83110c8d3aaf5c1f7e8e8fe355a294a9a77b5494704b18e27f1315cb19c104" + "3ad2061a2a414d40cc768fc4c8f49a3905e3a82095aa4eef6a1ad8af1029fced"), + wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}}; + +SubSampleInfo kEntitlementWithKeyRotationSubSampleDualPssh[] = { { true, 1, true, true, false, wvcdm::a2bs_hex("1D9B8E13B59951169348FF8D5B9394C0"), @@ -578,7 +626,75 @@ std::string kPsshStreamingClip21 = wvcdm::a2bs_hex( "EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id "08011a0d7769646576696e655f746573" // pssh data "74221073747265616d696e675f636c69703231"); -const std::string kPsshEntitlementWithKeyRotation[] = { +const std::string kSinglePsshEntitlementWithKeyRotation[] = { + wvcdm::a2bs_hex( + "000001fb7073736800000000" // blob size and pssh + "edef8ba979d64acea3c827dcd51d21ed000001db" // Widevine system id + "220b47726f7570563254657374381448" // pssh data + "e3dc959b065002580272580a10668093" + "381a8c5be48a0168ce372726ac1210c8" + "326486bb5d5c4a958f00b1111afc811a" + "20082cd9d3aed3ebe6239d30fbcf0b22" + "1d28cbb0360ea1295c2363973346ec00" + "512210914781334e864c8eb7f768cf26" + "49073872580a10f872d11d5b1052f2bd" + "a94e60a0e383021210450897c987a85c" + "2e9579f968554a12991a2097e603ceea" + "f35ed8cef1029eae7a0a54701e3d6db6" + "80e7da1de3b22a8db347fb2210b41c34" + "29b7bb96972bbaf6587bc0ddf172580a" + "10bac58b9fce9e5929a42a180e529f19" + "4712103f11f22988d25659b145ce4854" + "3e6b141a20416e22768e5a57b08d155e" + "5210d00658056947ff06d626668bceb3" + "5eb01c6b57221081fb2ff3fef79d332f" + "f98be46233596972580a101261c8036d" + "ae5c8caa968858aa0ca9cc12106d583c" + "b37c1456519843a81cf49912221a20c2" + "1116bb54a226e8d879a4cd41d8879920" + "2ae85b80d83b1b4447e5d7fcad6f6a22" + "100b27a4c3f44771d2b0c7c34c66af35" + "b572580a10ab1c8c259c6b5967991389" + "65bff5ac0c1210b5b4473658565d3786" + "efaf4b85d8e6e21a203ce6a9085285c2" + "ece0b650dc83dd7aa8ac849611a8e3f8" + "3c8f389223c0f3621522101946f0c2a3" + "d543101cc842bbec2d0b30"), + wvcdm::a2bs_hex( + "000001fb7073736800000000" // blob size and pssh + "edef8ba979d64acea3c827dcd51d21ed000001db" // Widevine system id + "220b47726f7570563254657374381548" // pssh data + "e3dc959b065002580272580a10668093" + "381a8c5be48a0168ce372726ac1210f8" + "488775a99855ff94b93ec5bd4993561a" + "20d15ba631c20e95da0d4857f6a1d25a" + "a3bccbd3fde18b3fdc1dd8c4f0ede76f" + "402210d6dd3675f0d1150052e81b9107" + "6d7fc172580a10f872d11d5b1052f2bd" + "a94e60a0e383021210ad1f93ad921e53" + "b097c415b2bf1ef1c61a20b2087b60a2" + "d253ac2158a1bfa789b150b79701b29e" + "c852a2662560f8b8977a4c2210051ed3" + "2628671fbda58f506ba5ea713972580a" + "10bac58b9fce9e5929a42a180e529f19" + "47121027cdda7bfe5e5fd4bff2ebc9c7" + "c020701a20f2cb1184d648a2404517e6" + "7a39d698332aae6bb890a69bf7ddb536" + "75b8ac41c62210a80ed7f9b728fdd566" + "0b01b173ace26372580a101261c8036d" + "ae5c8caa968858aa0ca9cc1210769a70" + "0442a25bf5ae17174c70f4cb8e1a206c" + "7b2012723fc47c83b003ea214204915f" + "9a63dc373bf219f36ccf5697589aa422" + "10bcc3c16e836cca264d5493a0c334d3" + "4872580a10ab1c8c259c6b5967991389" + "65bff5ac0c1210894b04aef78557c6a7" + "e6e8855febbcc91a2025cc545ee3cd0c" + "c323586610ff6a8f8f22a78f5fade2f2" + "1083f152c52208f16d2210257aacacec" + "512a2e769396b10e6d9dfa") +}; +const std::string kDualPsshEntitlementWithKeyRotation[] = { wvcdm::a2bs_hex( "0000003e7073736800000000" // blob size and pssh "edef8ba979d64acea3c827dcd51d21ed0000001e" // Widevine system id @@ -735,6 +851,26 @@ RenewWithClientIdTestConfiguration {true, false, false, false, "Test: Usage reporting with client Id"}}; +struct EntitlementTestConfiguration { + std::string entitlement_pssh; + std::string key_rotation_pssh; + SubSampleInfo* sub_sample_with_initial_keys; + SubSampleInfo* sub_sample_with_rotated_keys; +}; + +EntitlementTestConfiguration kEntitlementTestConfiguration[] = { + {// Single Widevine PSSH containing PSSH data of type ENTITLED_KEY + kSinglePsshEntitlementWithKeyRotation[0], + kSinglePsshEntitlementWithKeyRotation[1], + &kEntitlementWithKeyRotationSubSampleSinglePssh[0], + &kEntitlementWithKeyRotationSubSampleSinglePssh[1]}, + {// Two Widevine PSSHs containing PSSH data of type SINGLE, ENTITLED_KEY + kDualPsshEntitlementWithKeyRotation[0], + kDualPsshEntitlementWithKeyRotation[1], + &kEntitlementWithKeyRotationSubSampleDualPssh[0], + &kEntitlementWithKeyRotationSubSampleDualPssh[1]}, +}; + // provider:"widevine_test", // content_id":"aGxzX3NhbXBsZV9hZXNfc3RyZWFtaW5n" (hls_sample_aes_streaming) // key_id:613db35603320eb8e7ea24bdeea3fdb8 @@ -2880,29 +3016,39 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewalAndRelease) { VerifyKeyRequestResponse(config_.license_server(), client_auth); } -TEST_F(WvCdmRequestLicenseTest, EntitlementWithKeyRotation) { +class WvCdmEntitlementTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmEntitlementTest, EntitlementWithKeyRotation) { + EntitlementTestConfiguration* config = GetParam(); decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier, NULL, &session_id_); // Fetch entitlement license - GenerateKeyRequest(kPsshEntitlementWithKeyRotation[0], kLicenseTypeStreaming); + GenerateKeyRequest(config->entitlement_pssh, kLicenseTypeStreaming); VerifyKeyRequestResponse(config_.license_server(), config_.client_auth()); // Verify that we can decrypt a subsample ASSERT_TRUE(VerifyDecryption(session_id_, - entitlement_with_key_rotation_sub_sample[0])); + *(config->sub_sample_with_initial_keys))); // Key rotation ASSERT_TRUE(KeyRotationRequest(kLicenseTypeStreaming, - kPsshEntitlementWithKeyRotation[1])); + config->key_rotation_pssh)); // Verify that we can decrypt a subsample ASSERT_TRUE(VerifyDecryption(session_id_, - entitlement_with_key_rotation_sub_sample[1])); + *(config->sub_sample_with_rotated_keys))); decryptor_.CloseSession(session_id_); } +INSTANTIATE_TEST_CASE_P( + Cdm, WvCdmEntitlementTest, + ::testing::Range(&kEntitlementTestConfiguration[0], + &kEntitlementTestConfiguration[2])); + TEST_F(WvCdmRequestLicenseTest, RemoveKeys) { ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier, NULL,