Entitled Key Management

Merge from Widevine repo of http://go/wvgerrit/53883

Note: this CL does not modify license_key_status.cpp because the
previous CL already included those changes.

OEMCrypto v14 only supports one entitled key per entitlement key at a
time. Unfortunately, some partners have use cases that require using
old entitlement keys after the new keys have been loaded. Most
notably, when a key rotation occurs, the new PSSH will often be loaded
before the playback position catches up to the PSSH in the stream,
meaning that decryption will need to continue using the old keys for a
bit.

To fix this, EntitlementKeySession now caches the entitled keys when
they are loaded and only loads them under their matching entitlement
key when SelectKey() is called. This ensures that the right entitled
key is loaded for a given entitlement key before decryption.

The entitlement key integration tests have been updated to verify that
the old entitled keys still work even after loading new entitled keys.
Also, several places in the code that assumed loading new entitled
keys would wipe out the old keys have had to be modified.

Bug: 78652567
Test: CE CDM Unit Tests
Test: tested as part of http://go/ag/4674759
Change-Id: I6fac9dfe2b170ad68fb7cdb5bc8d6a2f35a20c2c
This commit is contained in:
Fred Gylys-Colwell
2018-07-01 19:21:47 -07:00
parent d5bb64a60c
commit 6000f834a0
3 changed files with 73 additions and 23 deletions

View File

@@ -5,7 +5,12 @@
#ifndef WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_
#define WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_
#include <map>
#include <string>
#include "OEMCryptoCENC.h"
#include "content_key_session.h"
#include "crypto_key.h"
#include "metrics_collections.h"
#include "override.h"
@@ -30,6 +35,17 @@ class EntitlementKeySession : public ContentKeySession {
const std::string& srm_requirement) OVERRIDE;
virtual OEMCryptoResult LoadEntitledContentKeys(
const std::vector<CryptoKey>& keys) OVERRIDE;
virtual OEMCryptoResult SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) OVERRIDE;
private:
// The object returned by this function contains raw pointers to the passed-in
// CryptoKey object. Care should be taken that it does not outlive the
// CryptoKey.
OEMCrypto_EntitledContentKeyObject MakeOecEntitledKey(
const CryptoKey& input_key);
std::map<std::string, CryptoKey> entitled_keys_;
};
} // namespace wvcdm

View File

@@ -9,7 +9,7 @@
namespace wvcdm {
EntitlementKeySession::EntitlementKeySession(CryptoSessionId oec_session_id,
metrics::CryptoMetrics* metrics)
: ContentKeySession(oec_session_id, metrics) {}
: ContentKeySession(oec_session_id, metrics), entitled_keys_() {}
OEMCryptoResult EntitlementKeySession::LoadKeys(
const std::string& message, const std::string& signature,
@@ -28,32 +28,67 @@ OEMCryptoResult EntitlementKeySession::LoadEntitledContentKeys(
const std::vector<CryptoKey>& keys) {
// The array |keys| contains new content keys, plus entitlement key ids for
// those content keys.
std::vector<OEMCrypto_EntitledContentKeyObject> entitlements;
entitlements.resize(keys.size());
for (size_t i = 0; i < keys.size(); ++i) {
entitlements[i].entitlement_key_id =
reinterpret_cast<const uint8_t*>(keys[i].entitlement_key_id().data());
entitlements[i].entitlement_key_id_length =
keys[i].entitlement_key_id().size();
entitlements[i].content_key_id =
reinterpret_cast<const uint8_t*>(keys[i].key_id().data());
entitlements[i].content_key_id_length = keys[i].key_id().size();
entitlements[i].content_key_data_iv =
reinterpret_cast<const uint8_t*>(keys[i].key_data_iv().data());
entitlements[i].content_key_data =
reinterpret_cast<const uint8_t*>(keys[i].key_data().data());
entitlements[i].content_key_data_length = keys[i].key_data().size();
// Since OEMCrypto only supports loading one entitled key per entitlement
// key at a time, (b/110266851) we defer loading until SelectKey() tells us
// which entitled key we actually need. For fast lookup later, we index the
// entitled keys by their ID.
const CryptoKey& input_key = keys[i];
entitled_keys_[input_key.key_id()] = input_key;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult EntitlementKeySession::SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
// Before the key can be selected, it must be loaded under its associated
// entitlement key. This could, in theory, be done ahead of time during
// LoadEntitledContentKeys(), but OEMCrypto v14 only supports one content key
// per entitlement key at a time, (b/110266851) so we must swap out for the
// correct key every time SelectKey() is called.
if (entitled_keys_.find(key_id) == entitled_keys_.end()) {
LOGE("Unknown entitled key ID selected.");
return OEMCrypto_KEY_NOT_LOADED;
}
OEMCrypto_EntitledContentKeyObject entitled_key =
MakeOecEntitledKey(entitled_keys_[key_id]);
OEMCryptoResult result = OEMCrypto_SUCCESS;
M_TIME(result = OEMCrypto_LoadEntitledContentKeys(
oec_session_id_, entitlements.size(), &entitlements[0]),
M_TIME(result = OEMCrypto_LoadEntitledContentKeys(oec_session_id_, 1,
&entitled_key),
metrics_, oemcrypto_load_entitled_keys_, result);
return result;
if (result != OEMCrypto_SUCCESS) {
return result;
}
return ContentKeySession::SelectKey(key_id, cipher_mode);
}
OEMCrypto_EntitledContentKeyObject EntitlementKeySession::MakeOecEntitledKey(
const CryptoKey& input_key) {
OEMCrypto_EntitledContentKeyObject output_key;
const std::string& entitlement_key_id = input_key.entitlement_key_id();
output_key.entitlement_key_id =
reinterpret_cast<const uint8_t*>(entitlement_key_id.data());
output_key.entitlement_key_id_length = entitlement_key_id.size();
const std::string& key_id = input_key.key_id();
output_key.content_key_id = reinterpret_cast<const uint8_t*>(key_id.data());
output_key.content_key_id_length = key_id.size();
const std::string& key_data_iv = input_key.key_data_iv();
output_key.content_key_data_iv =
reinterpret_cast<const uint8_t*>(key_data_iv.data());
const std::string& key_data = input_key.key_data();
output_key.content_key_data =
reinterpret_cast<const uint8_t*>(key_data.data());
output_key.content_key_data_length = key_data.size();
return output_key;
}
} // namespace wvcdm

View File

@@ -1154,7 +1154,6 @@ CdmResponseType CdmLicense::HandleNewEntitledKeys(
CdmResponseType resp =
crypto_session_->LoadEntitledContentKeys(entitled_key_array);
if (KEY_ADDED == resp) {
loaded_keys_.clear();
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator it =
wrapped_keys.begin();
it != wrapped_keys.end(); ++it) {