Handle key rotation

[ Merge of http://go/wvgerrit/77049 ]

Entitlement PSSHs can now be provided in follow on key generation
requests to cause keys to be rotated without needing a license
exchange.

Bug: 128462397
Test: WV unit/integration tests, Netflix and GPlay tests,
      GtsMediaDrmTests

Change-Id: I6ed0901a35c498240f42e405a522d82ea8dce2f7
This commit is contained in:
Rahul Frias
2019-04-18 11:32:58 -07:00
parent 79a271fcce
commit 2e2e92280e
5 changed files with 183 additions and 2 deletions

View File

@@ -34,6 +34,7 @@ class InitializationData {
CdmHlsMethod hls_method() const { return hls_method_; }
std::vector<video_widevine::WidevinePsshData_EntitledKey> ExtractWrappedKeys()
const;
bool contains_entitled_keys() const { return contains_entitled_keys_; }
private:
bool SelectWidevinePssh(const CdmInitData& init_data,
@@ -86,6 +87,7 @@ class InitializationData {
bool is_hls_;
bool is_webm_;
bool is_audio_;
bool contains_entitled_keys_;
std::vector<uint8_t> hls_iv_;
CdmHlsMethod hls_method_;

View File

@@ -439,8 +439,13 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal(
if (is_release_) {
return GenerateReleaseRequest(key_request);
} else if (license_received_) { // renewal
return GenerateRenewalRequest(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
if (init_data.contains_entitled_keys())
return license_parser_->HandleEmbeddedKeyData(init_data);
else
return GenerateRenewalRequest(key_request);
} else {
key_request->type = kKeyRequestTypeInitial;

View File

@@ -56,6 +56,7 @@ InitializationData::InitializationData(const std::string& type,
is_hls_(false),
is_webm_(false),
is_audio_(false),
contains_entitled_keys_(false),
hls_method_(kHlsMethodNone) {
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
type == CENC_INIT_DATA_FORMAT) {
@@ -138,12 +139,19 @@ bool InitializationData::SelectWidevinePssh(const CdmInitData& init_data,
continue;
}
if (pssh.type() == WidevinePsshData_Type_ENTITLED_KEY) {
contains_entitled_keys_ = true;
*output = pssh_payloads[i];
return true;
}
}
}
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.

View File

@@ -524,12 +524,14 @@ TEST_F(InitializationDataTest, HandlesMultipleWidevinePsshs) {
kOemCryptoWithoutEntitlements);
EXPECT_FALSE(single_init_data.IsEmpty());
EXPECT_EQ(kSingleKeyWidevinePsshBoxData, single_init_data.data());
EXPECT_FALSE(single_init_data.contains_entitled_keys());
InitializationData entitled_init_data(ISO_BMFF_VIDEO_MIME_TYPE,
kMultipleWidevinePsshBox,
kOemCryptoWithEntitlements);
EXPECT_FALSE(entitled_init_data.IsEmpty());
EXPECT_EQ(kEntitledKeysWidevinePsshBoxData, entitled_init_data.data());
EXPECT_TRUE(entitled_init_data.contains_entitled_keys());
}
TEST_P(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions) {

View File

@@ -446,6 +446,54 @@ SubSampleInfo usage_info_sub_samples_icp[] = {
wvcdm::a2b_hex("964c2dfda920357c668308d52d33c652"), 0,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}};
SubSampleInfo entitlement_with_key_rotation_sub_sample[] = {
{
true, 1, true, true, false,
wvcdm::a2bs_hex("1D9B8E13B59951169348FF8D5B9394C0"),
wvcdm::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
wvcdm::a2b_hex(
"4bb2ff540e12e4c97248b63abdf30c4474df11ae8f22ba587e5aa9b64d51f8ce"
"209b13cb24f436ac192060690d13d5a1230fe5207287678a3acbaf59b5381186"
"92dcdec42c770afc0545407c243a452214d0497f1a044adc56ac1dba5530d5a5"
"482f9fc67a5e1d1314e864ad85fec9f78657e10f68ae8720b218339c96e878c1"
"c0f09015172d8a52a85b6f09526b98aad6d7326d3799a418581efadd16f9ba3e"
"454945428a36959a296aa14fe05cb8ae7b44ae68d82950f0742d38d86f167c36"
"75e75390d3cc6cd6db267729b2aa81a7e7c4db186e82d4300c4123c0a5de73e9"
"a6bb238bd351769359d1b46c9702270b756038fd54ef609d985eecde58e9a58e"),
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample},
{
true, 1, true, true, false,
wvcdm::a2bs_hex("A0396729B3795B46A5EA7F9919B96A67"),
wvcdm::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"),
wvcdm::a2b_hex(
"49067453f9fe1b36fb2b37a2bba927bfe7f5f81ce715047beb99675da809b502"
"a5638f891cfad95c9fefdb32b6e8614ce3d5528032d51644a6cfaaccea2ad0b6"
"dd8bd36a0fb751bf4b70e1cb02266f373be467d3167aed4f3820eb4af884cc39"
"f60f83e060c8674b7e53d7ec8934ec07750d4677ed14ad6f6bacf46f46cf3ea8"
"560f704220bc3e32b9ad21c74aff6dbdbd64f49f38717ab7a05042dfe6fdc56f"
"47ddc384822b9a3fab3445653fa51f2405fcbcfd6d39a7fb8a99777c41960b94"
"74f1deb4b9b242bef609c625af791cba63e8c184b0312d624daba3889307b48b"
"00c362f246c3bc0b36cd41f9ec8eb72eab603f9517c7948f5e317a93ac1a5631"),
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}};
// License duration and uncertainty window
const uint32_t kSingleEncryptedSubSampleIcpLicenseDurationExpiration = 5;
const uint32_t kSingleEncryptedSubSampleIcpLicenseExpirationWindow = 2;
@@ -530,6 +578,83 @@ std::string kPsshStreamingClip21 = wvcdm::a2bs_hex(
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
"08011a0d7769646576696e655f746573" // pssh data
"74221073747265616d696e675f636c69703231");
const std::string kPsshEntitlementWithKeyRotation[] = {
wvcdm::a2bs_hex(
"0000003e7073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed0000001e" // Widevine system id
"2210426f6f5261646c6579666137613664393800" // pssh data
"48e3dc959b0650126a00000002027073"
"736800000000edef8ba979d64acea3c8"
"27dcd51d21ed000001e22210426f6f52"
"61646c657966613761366439380048e3"
"dc959b06501258026a0072580a109ab3"
"fb7eaffc5ee087568b9581ff0e721210"
"4a2da018480c51358c660c4b1a198721"
"1a20a00425ef5fe538adba59a6c4ce9a"
"fbff394351b79552643e08ed3a0cde2c"
"3ca42210931a2ef95b898d804b9a25ef"
"6d2805f072580a100fba9bd0f8e055d5"
"900bbfaabf5033141210f9bfca8cd3b8"
"593897f0b9283cd768f61a209c1d9a96"
"573a4dbba68327f03e598adfde1bd29f"
"1e709f206c2424d82ca14c1c22108428"
"46cffba58c12742418a702138c3a7258"
"0a109fec6ad1d36e56cdaf5c232480f6"
"3f8812101d9b8e13b59951169348ff8d"
"5b9394c01a2058ee6164ae759802e0b1"
"f9f7eeeaa3606faf21182777fe716c01"
"4c2412621543221039c8a6caab12515e"
"3c744a5447e9b72372580a10cd2e9ebf"
"89c257e2a3196c82ac4c76ba1210d48f"
"8f11c2d853289e981f5775db60441a20"
"3b491980b27587592c86e73da27caa12"
"d95acda2295f768746090b55b81c9d61"
"2210dd7f29944aaeb08826ba4fc0dec6"
"c88d72580a10b3fd79204b9d5cbf8f67"
"2eca27255e9e121023feaaf517585fde"
"bdf0d4693f32c1091a20d18a1c0e54b8"
"fbc189b58ccc0dc5a0c541b628b8fe23"
"34c862944b5555ad7f7f221091269127"
"e4feb6a8fa878c6e9781a55f"),
wvcdm::a2bs_hex(
"0000003e7073736800000000ed" // blob size and pssh
"ef8ba979d64acea3c827dcd51d21ed0000001e" // Widevine system id
"2210426f6f5261646c65796661" // pssh data
"3761366439380448e3dc959b0650126a"
"00000002027073736800000000edef8b"
"a979d64acea3c827dcd51d21ed000001"
"e22210426f6f5261646c657966613761"
"366439380448e3dc959b06501258026a"
"0072580a109ab3fb7eaffc5ee087568b"
"9581ff0e721210777bc13c94c4568b84"
"ddaf99de3f03311a2042949649205d0d"
"f4f4a4eec94263561931066a9fc18b61"
"8b3929a168b4c2404222101e7aad142f"
"59edb962816e8d0702356b72580a100f"
"ba9bd0f8e055d5900bbfaabf50331412"
"10537cb8f017ec59e7be3c309e6d7f4b"
"5d1a2089c49c0f0e214c3765cbb37ab5"
"41dc0625c0087fa94317528ee8265431"
"71cabe2210dddba401d4eaaa76437638"
"a29dbdc38472580a109fec6ad1d36e56"
"cdaf5c232480f63f881210a0396729b3"
"795b46a5ea7f9919b96a671a209245a1"
"c821de9c65fb1086d9af8aaca4b7da0e"
"bbd0850a56ab36c23bb71b507e2210b0"
"531d2235575ff9ba5af4545bb43fdd72"
"580a10cd2e9ebf89c257e2a3196c82ac"
"4c76ba121011647820b33352349942cd"
"31ce352e571a20277b4392c76a04335d"
"3eadf22705184eb1adc057a61e372e78"
"30b7a20361d2472210d954557dc853a7"
"42283e6dfe16677a6a72580a10b3fd79"
"204b9d5cbf8f672eca27255e9e1210c4"
"78fb09c0c6531b92c571972c36098b1a"
"20bf17c678ac01685e258192eb4d2d49"
"157c3a07a95342d8be8b2f9f121f596b"
"8622100d9cfe972bc17003b49ecd5f45"
"f3bb28")
};
std::string kProviderSessionTokenStreamingClip3 = wvcdm::a2bs_hex(
"4851305A4A4156485A554936444E4931");
@@ -1513,6 +1638,22 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
EXPECT_NE(0u, key_request.url.size());
}
bool KeyRotationRequest(CdmLicenseType license_type,
const std::string& init_data) {
wvcdm::CdmAppParameterMap app_parameters;
CdmKeyRequest key_request;
CdmResponseType status =
decryptor_.GenerateKeyRequest(
session_id_, key_set_id_, "video/mp4", init_data,
license_type, app_parameters, NULL, kDefaultCdmIdentifier,
&key_request);
EXPECT_EQ(wvcdm::KEY_ADDED, status);
EXPECT_TRUE(key_request.message.empty());
EXPECT_TRUE(key_request.url.empty());
return wvcdm::KEY_ADDED == status && key_request.message.empty()
&& key_request.url.empty();
}
void GenerateKeyRelease(CdmKeySetId key_set_id) {
GenerateKeyRelease(key_set_id, NULL, NULL);
}
@@ -2739,6 +2880,29 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewalAndRelease) {
VerifyKeyRequestResponse(config_.license_server(), client_auth);
}
TEST_F(WvCdmRequestLicenseTest, EntitlementWithKeyRotation) {
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
NULL, &session_id_);
// Fetch entitlement license
GenerateKeyRequest(kPsshEntitlementWithKeyRotation[0], 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]));
// Key rotation
ASSERT_TRUE(KeyRotationRequest(kLicenseTypeStreaming,
kPsshEntitlementWithKeyRotation[1]));
// Verify that we can decrypt a subsample
ASSERT_TRUE(VerifyDecryption(session_id_,
entitlement_with_key_rotation_sub_sample[1]));
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, RemoveKeys) {
ASSERT_EQ(NO_ERROR, decryptor_.OpenSession(config_.key_system(), NULL,
kDefaultCdmIdentifier, NULL,