From 6000f834a0287a056ad1337a2057384560653350 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Sun, 1 Jul 2018 19:21:47 -0700 Subject: [PATCH] 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 --- .../core/include/entitlement_key_session.h | 16 ++++ .../cdm/core/src/entitlement_key_session.cpp | 79 +++++++++++++------ libwvdrmengine/cdm/core/src/license.cpp | 1 - 3 files changed, 73 insertions(+), 23 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/entitlement_key_session.h b/libwvdrmengine/cdm/core/include/entitlement_key_session.h index 1877d305..da4fee41 100644 --- a/libwvdrmengine/cdm/core/include/entitlement_key_session.h +++ b/libwvdrmengine/cdm/core/include/entitlement_key_session.h @@ -5,7 +5,12 @@ #ifndef WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_ #define WVCDM_CORE_ENTITLEMENT_KEY_SESSSION_H_ +#include +#include + +#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& 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 entitled_keys_; }; } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp b/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp index 393f920f..06041886 100644 --- a/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp +++ b/libwvdrmengine/cdm/core/src/entitlement_key_session.cpp @@ -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& keys) { // The array |keys| contains new content keys, plus entitlement key ids for // those content keys. - std::vector entitlements; - entitlements.resize(keys.size()); - for (size_t i = 0; i < keys.size(); ++i) { - entitlements[i].entitlement_key_id = - reinterpret_cast(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(keys[i].key_id().data()); - entitlements[i].content_key_id_length = keys[i].key_id().size(); - - entitlements[i].content_key_data_iv = - reinterpret_cast(keys[i].key_data_iv().data()); - - entitlements[i].content_key_data = - reinterpret_cast(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(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(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(key_data_iv.data()); + + const std::string& key_data = input_key.key_data(); + output_key.content_key_data = + reinterpret_cast(key_data.data()); + output_key.content_key_data_length = key_data.size(); + + return output_key; } } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index be246f44..fc560e88 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -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::const_iterator it = wrapped_keys.begin(); it != wrapped_keys.end(); ++it) {