Add basic handling for entitlement keys in a license.
Merge from Widevine repo of http://go/wvgerrit/41834 Key rotation is not yet supported. The key statuses are updated from a license. The mechanism expects content keys tro come in a license. For entitlement licenses, the content keys come in the init_data. This code does not yet support the key rotation event. (A new pssh with wrapped keys is a passed to the cdm) The policy engine/key status mechanism needs to be updated to handle updated from the init_data. For now, the cdm builds a license with a key container with the content keys and used that to call PolicyEngine::SetLicense to setup the policy engine and key statuses. Bug: 64003606 Bug: 70334840 Test: In child CL Change-Id: Ibf46a18f5321cab4ff6f1778ba30527942c8021f
This commit is contained in:
committed by
Rahul Frias
parent
8251aab9f6
commit
9ae7489938
@@ -382,11 +382,11 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
}
|
||||
|
||||
std::vector<video_widevine::SubLicense> embedded_key_data =
|
||||
init_data.ExtractEmbeddedKeys();
|
||||
init_data.ExtractSublicenseKeys();
|
||||
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
|
||||
CdmResponseType sts = crypto_session_->AddSubSession(
|
||||
embedded_key_data[i].sub_session_key_id(),
|
||||
init_data.ExtractGroupMasterKeyId());
|
||||
embedded_key_data[i].group_id());
|
||||
if (NO_ERROR != sts) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session");
|
||||
return sts;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Generate Derived Keys for DefaultKeySession
|
||||
bool DefaultKeySession::GenerateDerivedKeys(const std::string& message) {
|
||||
// Generate Derived Keys for ContentKeySession
|
||||
bool ContentKeySession::GenerateDerivedKeys(const std::string& message) {
|
||||
std::string mac_deriv_message;
|
||||
std::string enc_deriv_message;
|
||||
GenerateMacContext(message, &mac_deriv_message);
|
||||
@@ -30,8 +30,8 @@ bool DefaultKeySession::GenerateDerivedKeys(const std::string& message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate Derived Keys (from session key) for DefaultKeySession
|
||||
bool DefaultKeySession::GenerateDerivedKeys(const std::string& message,
|
||||
// Generate Derived Keys (from session key) for ContentKeySession
|
||||
bool ContentKeySession::GenerateDerivedKeys(const std::string& message,
|
||||
const std::string& session_key) {
|
||||
std::string mac_deriv_message;
|
||||
std::string enc_deriv_message;
|
||||
@@ -58,13 +58,65 @@ bool DefaultKeySession::GenerateDerivedKeys(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load Keys for DefaultKeySession
|
||||
OEMCryptoResult DefaultKeySession::LoadKeys(
|
||||
// Load Keys for ContentKeySession
|
||||
OEMCryptoResult ContentKeySession::LoadKeys(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& keys,
|
||||
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
|
||||
const std::string& srm_requirement) {
|
||||
return LoadKeys(message, signature, mac_key_iv, mac_key, keys,
|
||||
provider_session_token, cipher_mode, srm_requirement,
|
||||
OEMCrypto_ContentLicense);
|
||||
}
|
||||
|
||||
// Select Key for ContentKeySession
|
||||
OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id,
|
||||
CdmCipherMode cipher_mode) {
|
||||
// Crypto session lock already locked.
|
||||
if (!cached_key_id_.empty() && cached_key_id_ == key_id) {
|
||||
// Already using the desired key.
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
cached_key_id_ = key_id;
|
||||
|
||||
const uint8_t* key_id_string =
|
||||
reinterpret_cast<const uint8_t*>(cached_key_id_.data());
|
||||
|
||||
OEMCryptoResult sts;
|
||||
M_TIME(sts = OEMCrypto_SelectKey(
|
||||
oec_session_id_, key_id_string, cached_key_id_.size(),
|
||||
static_cast<OEMCryptoCipherMode>(cipher_mode)),
|
||||
metrics_, oemcrypto_select_key_, sts);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
cached_key_id_.clear();
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Decrypt for ContentKeySession
|
||||
OEMCryptoResult ContentKeySession::Decrypt(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor,
|
||||
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
|
||||
OEMCryptoResult sts;
|
||||
M_TIME(sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
|
||||
metrics_, oemcrypto_decrypt_cenc_, sts,
|
||||
metrics::Pow2Bucket(params.encrypt_length));
|
||||
return sts;
|
||||
}
|
||||
|
||||
OEMCryptoResult ContentKeySession::LoadKeys(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& keys,
|
||||
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
|
||||
const std::string& srm_requirement, OEMCrypto_LicenseType license_type) {
|
||||
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
const uint8_t* enc_mac_key = NULL;
|
||||
const uint8_t* enc_mac_key_iv = NULL;
|
||||
@@ -72,7 +124,7 @@ OEMCryptoResult DefaultKeySession::LoadKeys(
|
||||
enc_mac_key = msg + GetOffset(message, mac_key);
|
||||
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
|
||||
} else {
|
||||
LOGV("DefaultKeySession::LoadKeys: enc_mac_key not set");
|
||||
LOGV("ContentKeySession::LoadKeys: enc_mac_key not set");
|
||||
}
|
||||
std::vector<OEMCrypto_KeyObject> load_keys(keys.size());
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
@@ -114,50 +166,9 @@ OEMCryptoResult DefaultKeySession::LoadKeys(
|
||||
oec_session_id_, msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
|
||||
enc_mac_key_iv, enc_mac_key, keys.size(), &load_keys[0], pst,
|
||||
provider_session_token.length(), srm_req, OEMCrypto_ContentLicense),
|
||||
provider_session_token.length(), srm_req, license_type),
|
||||
metrics_, oemcrypto_load_keys_, sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Select Key for DefaultKeySession
|
||||
OEMCryptoResult DefaultKeySession::SelectKey(const std::string& key_id,
|
||||
CdmCipherMode cipher_mode) {
|
||||
// Crypto session lock already locked.
|
||||
if (!cached_key_id_.empty() && cached_key_id_ == key_id) {
|
||||
// Already using the desired key.
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
cached_key_id_ = key_id;
|
||||
|
||||
const uint8_t* key_id_string =
|
||||
reinterpret_cast<const uint8_t*>(cached_key_id_.data());
|
||||
|
||||
OEMCryptoResult sts;
|
||||
M_TIME(sts = OEMCrypto_SelectKey(
|
||||
oec_session_id_, key_id_string, cached_key_id_.size(),
|
||||
static_cast<OEMCryptoCipherMode>(cipher_mode)),
|
||||
metrics_, oemcrypto_select_key_, sts);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
cached_key_id_.clear();
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Decrypt for DefaultKeySession
|
||||
OEMCryptoResult DefaultKeySession::Decrypt(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor,
|
||||
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
|
||||
OEMCryptoResult sts;
|
||||
M_TIME(sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
|
||||
metrics_, oemcrypto_decrypt_cenc_, sts,
|
||||
metrics::Pow2Bucket(params.encrypt_length));
|
||||
return sts;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "content_key_session.h"
|
||||
#include "crypto_key.h"
|
||||
#include "entitlement_key_session.h"
|
||||
#include "log.h"
|
||||
#include "openssl/asn1.h"
|
||||
#include "openssl/sha.h"
|
||||
@@ -119,6 +120,12 @@ void GenerateEncryptContext(const std::string& input_context,
|
||||
deriv_context->append(EncodeUint32(kEncryptionKeySizeBits));
|
||||
}
|
||||
|
||||
OEMCrypto_LicenseType OEMCryptoLicenseType(CdmLicenseKeyType cdm_license_type) {
|
||||
return cdm_license_type == kLicenseKeyTypeContent
|
||||
? OEMCrypto_ContentLicense
|
||||
: OEMCrypto_EntitlementLicense;
|
||||
}
|
||||
|
||||
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
|
||||
: metrics_(metrics),
|
||||
system_id_(-1),
|
||||
@@ -692,7 +699,7 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
|
||||
}
|
||||
|
||||
// TODO(gmorgan, jfore): resolve handling of usage records in sublicenses
|
||||
key_session_.reset(new DefaultKeySession(oec_session_id_, metrics_));
|
||||
key_session_.reset(new ContentKeySession(oec_session_id_, metrics_));
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -779,10 +786,15 @@ CdmResponseType CryptoSession::LoadKeys(
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& keys,
|
||||
const std::string& provider_session_token,
|
||||
const std::string& srm_requirement) {
|
||||
const std::string& srm_requirement, CdmLicenseKeyType key_type) {
|
||||
LOGV("CryptoSession::LoadKeys: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
if (key_type == kLicenseKeyTypeEntitlement &&
|
||||
key_session_->Type() != KeySession::kEntitlement) {
|
||||
key_session_.reset(new EntitlementKeySession(oec_session_id_, metrics_));
|
||||
}
|
||||
|
||||
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
|
||||
OEMCryptoResult sts = key_session_->LoadKeys(
|
||||
message, signature, mac_key_iv, mac_key, keys, provider_session_token,
|
||||
@@ -815,6 +827,13 @@ CdmResponseType CryptoSession::LoadKeys(
|
||||
return result;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::LoadEntitledContentKeys(
|
||||
const std::vector<CryptoKey>& key_array) {
|
||||
// TODO(jfore): Handle and return errors.
|
||||
/*OEMCryptoResult status =*/ key_session_->LoadEntitledContentKeys(key_array);
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
|
||||
LOGV("CryptoSession::LoadCertificatePrivateKey: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
54
libwvdrmengine/cdm/core/src/entitlement_key_session.cpp
Normal file
54
libwvdrmengine/cdm/core/src/entitlement_key_session.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "entitlement_key_session.h"
|
||||
|
||||
#include "crypto_key.h"
|
||||
|
||||
namespace wvcdm {
|
||||
EntitlementKeySession::EntitlementKeySession(CryptoSessionId oec_session_id,
|
||||
metrics::CryptoMetrics* metrics)
|
||||
: ContentKeySession(oec_session_id, metrics) {}
|
||||
|
||||
OEMCryptoResult EntitlementKeySession::LoadKeys(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& keys,
|
||||
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
|
||||
const std::string& srm_requirement) {
|
||||
keys_.resize(keys.size());
|
||||
return ContentKeySession::LoadKeys(
|
||||
message, signature, mac_key_iv, mac_key, keys, provider_session_token,
|
||||
cipher_mode, srm_requirement, OEMCrypto_EntitlementLicense);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
M_TIME(result = OEMCrypto_LoadEntitledContentKeys(
|
||||
oec_session_id_, entitlements.size(), &entitlements[0]),
|
||||
metrics_, oemcrypto_load_entitled_keys_, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -67,7 +67,7 @@ InitializationData::InitializationData(const std::string& type,
|
||||
|
||||
// Parse the pssh data and return the embedded key data if it exists.
|
||||
std::vector<video_widevine::SubLicense>
|
||||
InitializationData::ExtractEmbeddedKeys() const {
|
||||
InitializationData::ExtractSublicenseKeys() const {
|
||||
std::vector<video_widevine::SubLicense> keys;
|
||||
WidevinePsshData cenc_header;
|
||||
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
|
||||
@@ -81,6 +81,20 @@ InitializationData::ExtractEmbeddedKeys() const {
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::vector<video_widevine::WrappedKey> InitializationData::ExtractWrappedKeys()
|
||||
const {
|
||||
std::vector<video_widevine::WrappedKey> keys;
|
||||
WidevinePsshData cenc_header;
|
||||
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
|
||||
cenc_header.entitled_keys().size() == 0)
|
||||
return keys;
|
||||
keys.reserve(cenc_header.entitled_keys().size());
|
||||
for (int i = 0; i < cenc_header.entitled_keys().size(); ++i) {
|
||||
keys.push_back(cenc_header.entitled_keys(i));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||
// Widevine PSSH.
|
||||
bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
@@ -483,7 +497,7 @@ bool InitializationData::ConstructWidevineInitData(
|
||||
// have not yet been pushed to production. Set until then.
|
||||
cenc_header.set_algorithm(WidevinePsshData_Algorithm_AESCTR);
|
||||
for (size_t i = 0; i < key_ids.size(); ++i) {
|
||||
cenc_header.add_key_id(key_ids[i]);
|
||||
cenc_header.add_key_ids(key_ids[i]);
|
||||
}
|
||||
cenc_header.set_provider(provider);
|
||||
cenc_header.set_content_id(content_id);
|
||||
@@ -589,19 +603,4 @@ std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
|
||||
return versions;
|
||||
}
|
||||
|
||||
// Extract the key id of the group master key used to generate sublicense data.
|
||||
// Returns an empty string if not defined.
|
||||
const std::string InitializationData::ExtractGroupMasterKeyId() const {
|
||||
if (!is_cenc_) {
|
||||
return "";
|
||||
}
|
||||
|
||||
WidevinePsshData cenc_header;
|
||||
if (!cenc_header.ParseFromString(data_)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return cenc_header.group_master_key_id();
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -72,6 +72,54 @@ static std::vector<CryptoKey> ExtractSubSessionKeys(const License& license) {
|
||||
return key_array;
|
||||
}
|
||||
|
||||
static std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
|
||||
std::vector<CryptoKey> key_array;
|
||||
|
||||
// Extract sub session key(s)
|
||||
for (int i = 0; i < license.key_size(); ++i) {
|
||||
CryptoKey key;
|
||||
switch (license.key(i).type()) {
|
||||
case License_KeyContainer::ENTITLEMENT: {
|
||||
key.set_key_data(license.key(i).key());
|
||||
key.set_key_data_iv(license.key(i).iv());
|
||||
key.set_key_id(license.key(i).id());
|
||||
key.set_track_label(license.key(i).track_label());
|
||||
if (license.key(i).has_key_control()) {
|
||||
key.set_key_control(license.key(i).key_control().key_control_block());
|
||||
key.set_key_control_iv(license.key(i).key_control().iv());
|
||||
}
|
||||
uint32_t four_cc = kFourCcCenc;
|
||||
if (license.has_protection_scheme()) {
|
||||
four_cc = license.protection_scheme();
|
||||
}
|
||||
key.set_track_label(license.key(i).track_label());
|
||||
switch (four_cc) {
|
||||
// b/30713238: Android N assumed that the "protection scheme" Four
|
||||
// CC code, after being extracted from the protobuf, was host byte
|
||||
// order dependent. Later versions do not assume this, and thus,
|
||||
// for backwards compatibility, must support both byte orders.
|
||||
case kFourCcCbc1:
|
||||
case kFourCcCbcs:
|
||||
case kFourCcLittleEndianCbc1:
|
||||
case kFourCcLittleEndianCbcs:
|
||||
key.set_cipher_mode(kCipherModeCbc);
|
||||
break;
|
||||
default:
|
||||
key.set_cipher_mode(kCipherModeCtr);
|
||||
break;
|
||||
}
|
||||
key_array.push_back(key);
|
||||
} break;
|
||||
|
||||
default:
|
||||
// Ignore all but ENTITLEMENT key types.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return key_array;
|
||||
}
|
||||
|
||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
std::vector<CryptoKey> key_array;
|
||||
|
||||
@@ -239,6 +287,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
return PrepareKeyRequest(restored_init_data, license_type, app_parameters,
|
||||
signed_request, server_url);
|
||||
}
|
||||
wrapped_keys_ = init_data.ExtractWrappedKeys();
|
||||
if (!init_data.is_supported()) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
@@ -306,7 +355,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
// Prepare the request for any embedded keys that may exist in the
|
||||
// initialization data.
|
||||
std::vector<video_widevine::SubLicense> embedded_key_data =
|
||||
init_data.ExtractEmbeddedKeys();
|
||||
init_data.ExtractSublicenseKeys();
|
||||
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
|
||||
bool exists = false;
|
||||
if (!crypto_session_->GenerateSubSessionNonce(
|
||||
@@ -575,8 +624,15 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
if (!key_array.size()) {
|
||||
CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement;
|
||||
std::vector<CryptoKey> key_array = ExtractEntitlementKeys(license);
|
||||
if (key_array.empty()) {
|
||||
key_array = ExtractContentKeys(license);
|
||||
key_type = kLicenseKeyTypeContent;
|
||||
} else if (wrapped_keys_.empty()) {
|
||||
key_array.clear();
|
||||
}
|
||||
if (key_array.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
|
||||
return NO_CONTENT_KEY;
|
||||
}
|
||||
@@ -601,17 +657,15 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
renew_with_client_id_ = license.policy().always_include_client_id();
|
||||
}
|
||||
|
||||
CdmResponseType resp = crypto_session_->LoadKeys(
|
||||
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
|
||||
key_array, provider_session_token_, license.srm_requirement());
|
||||
|
||||
if (KEY_ADDED == resp) {
|
||||
loaded_keys_.clear();
|
||||
for (std::vector<CryptoKey>::iterator it = key_array.begin();
|
||||
it != key_array.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
policy_engine_->SetLicense(license);
|
||||
CdmResponseType resp = NO_CONTENT_KEY;
|
||||
if (kLicenseKeyTypeEntitlement == key_type) {
|
||||
resp = HandleEntitlementKeyResponse(signed_response.msg(),
|
||||
signed_response.signature(), mac_key_iv,
|
||||
mac_key, key_array, license);
|
||||
} else if (kLicenseKeyTypeContent == key_type) {
|
||||
resp = HandleContentKeyResponse(signed_response.msg(),
|
||||
signed_response.signature(), mac_key_iv,
|
||||
mac_key, key_array, license);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
@@ -699,7 +753,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
CdmResponseType CdmLicense::HandleSubLicense(
|
||||
const InitializationData& init_data) {
|
||||
std::vector<video_widevine::SubLicense> subkeys =
|
||||
init_data.ExtractEmbeddedKeys();
|
||||
init_data.ExtractSublicenseKeys();
|
||||
std::set<KeyId> loaded_keys;
|
||||
// Build a license with the rotated keys.
|
||||
License license;
|
||||
@@ -732,7 +786,7 @@ CdmResponseType CdmLicense::HandleSubLicense(
|
||||
//TODO: passing empty cipher_mode and srm_req params - OK?
|
||||
CdmResponseType result = crypto_session_->LoadKeys(
|
||||
sm.msg(), sm.signature(), std::string(), std::string(), keys,
|
||||
std::string(), std::string());
|
||||
std::string(), std::string(), kLicenseKeyTypeContent);
|
||||
if (result != KEY_ADDED) {
|
||||
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
|
||||
result);
|
||||
@@ -1049,6 +1103,104 @@ CdmResponseType CdmLicense::PrepareContentId(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleContentKeyResponse(
|
||||
const std::string& msg, const std::string& signature,
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& key_array,
|
||||
const video_widevine::License& license) {
|
||||
if (key_array.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
|
||||
return NO_CONTENT_KEY;
|
||||
}
|
||||
CdmResponseType resp = crypto_session_->LoadKeys(
|
||||
msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_,
|
||||
license.srm_requirement(), kLicenseKeyTypeContent);
|
||||
|
||||
if (KEY_ADDED == resp) {
|
||||
loaded_keys_.clear();
|
||||
for (std::vector<CryptoKey>::const_iterator it = key_array.begin();
|
||||
it != key_array.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
policy_engine_->SetLicense(license);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
|
||||
const std::string& msg, const std::string& signature,
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& key_array,
|
||||
const video_widevine::License& license) {
|
||||
if (key_array.empty() || wrapped_keys_.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
|
||||
return NO_CONTENT_KEY;
|
||||
}
|
||||
CdmResponseType resp = crypto_session_->LoadKeys(
|
||||
msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_,
|
||||
license.srm_requirement(), kLicenseKeyTypeEntitlement);
|
||||
if (KEY_ADDED != resp) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
std::vector<CryptoKey> entitled_key_array;
|
||||
entitled_key_array.reserve(key_array.size());
|
||||
|
||||
for (std::vector<video_widevine::WrappedKey>::iterator wk =
|
||||
wrapped_keys_.begin();
|
||||
wk != wrapped_keys_.end(); wk++) {
|
||||
for (std::vector<CryptoKey>::const_iterator key = key_array.begin();
|
||||
key != key_array.end(); key++) {
|
||||
if (wk->wrapping_key_id() == key->key_id()) {
|
||||
entitled_key_array.resize(entitled_key_array.size() + 1);
|
||||
CryptoKey& this_entry = entitled_key_array.back();
|
||||
this_entry.set_key_id(wk->key_id());
|
||||
this_entry.set_key_data(wk->wrapped_key());
|
||||
this_entry.set_key_data_iv(wk->wrapping_iv());
|
||||
this_entry.set_entitlement_key_id(wk->wrapping_key_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp = crypto_session_->LoadEntitledContentKeys(entitled_key_array);
|
||||
if (KEY_ADDED == resp) {
|
||||
loaded_keys_.clear();
|
||||
for (std::vector<video_widevine::WrappedKey>::const_iterator it =
|
||||
wrapped_keys_.begin();
|
||||
it != wrapped_keys_.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
|
||||
// TODO(jfore): Move the information to build this "license" to the
|
||||
// entitlement key session. It is used to update the policy engine and
|
||||
// key status when using entitlement licenses. It may become unnecessary
|
||||
// if policy manager ius changed to allow setting keys from the wrapped
|
||||
// keys from init_data.
|
||||
video_widevine::License entitled_license;
|
||||
entitled_license.mutable_policy()->CopyFrom(license.policy());
|
||||
entitled_license.mutable_id()->CopyFrom(license.id());
|
||||
entitled_license.mutable_key()->CopyFrom(license.key());
|
||||
entitled_license.set_license_start_time(license.license_start_time());
|
||||
for (size_t i = 0; i < wrapped_keys_.size(); ++i) {
|
||||
for (int x = 0; x < entitled_license.key().size(); ++x) {
|
||||
LOGE("Test for %s", wrapped_keys_[i].wrapping_key_id().c_str());
|
||||
if (entitled_license.key(x).id() ==
|
||||
wrapped_keys_[i].wrapping_key_id()) {
|
||||
video_widevine::License::KeyContainer* kc =
|
||||
entitled_license.mutable_key(x);
|
||||
kc->set_type(video_widevine::License::KeyContainer::CONTENT);
|
||||
kc->set_key(wrapped_keys_[i].wrapped_key());
|
||||
kc->set_id(wrapped_keys_[i].key_id());
|
||||
LOGE("Add %s", wrapped_keys_[i].wrapping_key_id().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
LOGE("%d license keys", entitled_license.key_size());
|
||||
policy_engine_->SetLicense(entitled_license);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id) {
|
||||
|
||||
@@ -102,6 +102,7 @@ message License {
|
||||
KEY_CONTROL = 3;
|
||||
OPERATOR_SESSION = 4;
|
||||
SUB_SESSION = 5;
|
||||
ENTITLEMENT = 6;
|
||||
}
|
||||
|
||||
// The SecurityLevel enumeration allows the server to communicate the level
|
||||
@@ -719,28 +720,54 @@ message SubLicense {
|
||||
// proto. Internally the message field will contain a serialized KeyContainer
|
||||
// holding a single content key.
|
||||
optional bytes key_msg = 2;
|
||||
|
||||
// TODO(jfore): There is some uncertainty about including the current group in
|
||||
// a license. This may change.
|
||||
// Byte string that identifies the group to which this this content
|
||||
// belongs.
|
||||
optional bytes group_id = 13;
|
||||
}
|
||||
|
||||
// Container for keys which are wrapped using an entitlement key from a master
|
||||
// license.
|
||||
message WrappedKey {
|
||||
// ID of the wrapped key. Required.
|
||||
optional bytes key_id = 1;
|
||||
// ID of wrapping key. Required.
|
||||
optional bytes wrapping_key_id = 2;
|
||||
// IV used to wrap the key. Required.
|
||||
optional bytes wrapping_iv = 3;
|
||||
// Encrypted entitled key. Wrapped with the entitlement key and IV, using
|
||||
// AES-256-CBC with PKCS#7 padding. Required.
|
||||
optional bytes wrapped_key = 4;
|
||||
}
|
||||
|
||||
message WidevinePsshData {
|
||||
// Superceded by protection_scheme.
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
// Replaced with protection_scheme.
|
||||
optional Algorithm algorithm = 1;
|
||||
repeated bytes key_id = 2;
|
||||
|
||||
optional Algorithm algorithm = 1 [deprecated = true];
|
||||
|
||||
// Key IDentifier(s). This field is mutually exclusive with content_id, below.
|
||||
// Only One or the other, but at least one must be present.
|
||||
repeated bytes key_ids = 2;
|
||||
|
||||
// Content provider name.
|
||||
optional string provider = 3;
|
||||
optional string provider = 3 [deprecated = true];
|
||||
|
||||
// A content identifier, specified by content provider.
|
||||
// This field is mutually exclusive with key_ids, above. Only
|
||||
// one or the other, but at least one must be present.
|
||||
optional bytes content_id = 4;
|
||||
|
||||
// Track type. Acceptable values are SD, HD and AUDIO. Used to differentiate
|
||||
// content keys used by an asset.
|
||||
// No longer adding track_type to the PSSH since the Widevine license server
|
||||
// will return keys for all allowed track types in a single license.
|
||||
optional string track_type_deprecated = 5;
|
||||
optional string track_type = 5 [deprecated = true];
|
||||
|
||||
// The name of a registered policy to be used for this asset.
|
||||
optional string policy = 6 [deprecated=true];
|
||||
@@ -750,16 +777,16 @@ message WidevinePsshData {
|
||||
|
||||
// Optional protected context for group content. The grouped_license is a
|
||||
// serialized SignedMessage.
|
||||
optional bytes grouped_license = 8;
|
||||
optional bytes grouped_license = 8 [deprecated = true];
|
||||
|
||||
// Protection scheme identifying the encryption algorithm. The protection
|
||||
// scheme is represented as a uint32 value. The uint32 contains 4 bytes each
|
||||
// representing a single ascii character in one of the 4CC protection scheme
|
||||
// values.
|
||||
// values. To be soon deprecated in favor of signaling from content.
|
||||
// 'cenc' (AES-CTR) protection_scheme = 0x63656E63,
|
||||
// 'cbc1' (AES-CBC) protection_scheme = 0x63626331,
|
||||
// 'cens' (AES-CTR subsample) protection_scheme = 0x63656E73,
|
||||
// 'cbcs' (AES-CBC subsample) protection_scheme = 0x63626373.
|
||||
// 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73,
|
||||
// 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373.
|
||||
optional uint32 protection_scheme = 9;
|
||||
|
||||
// Optional. For media using key rotation, this represents the duration
|
||||
@@ -769,9 +796,15 @@ message WidevinePsshData {
|
||||
// Required when using content keys that are embedded in content.
|
||||
repeated SubLicense sub_licenses = 11;
|
||||
|
||||
// Key ID used to identify the group master key License Server is supposed
|
||||
// to use to generate group license.
|
||||
optional string group_master_key_id = 12;
|
||||
// IDs of the groups to which the content belongs. A group is a set of
|
||||
// content IDs. A particular piece of content may belong to multiple groups.
|
||||
repeated bytes group_ids = 12;
|
||||
|
||||
// Copy/copies of the content key used to decrypt the media stream in which
|
||||
// the PSSH box is embedded, each wrapped with a different entitlement key.
|
||||
// May be repeated if using group entitlement keys. Optional, used for content
|
||||
// key rotation.
|
||||
repeated WrappedKey entitled_keys = 13;
|
||||
}
|
||||
|
||||
// Signed device certificate definition.
|
||||
|
||||
@@ -11,13 +11,13 @@ SubLicenseKeySession::SubLicenseKeySession(
|
||||
metrics::CryptoMetrics* metrics,
|
||||
const std::string& wrapped_private_device_key,
|
||||
SecurityLevel requested_security_level,
|
||||
const std::string& group_master_key_id)
|
||||
const std::string& group_id)
|
||||
: KeySession(metrics),
|
||||
state_(kInitializing),
|
||||
wrapped_private_device_key_(wrapped_private_device_key),
|
||||
sub_license_oec_sessions_(sub_license_oec_sessions),
|
||||
requested_security_level_(requested_security_level),
|
||||
group_master_key_id_(group_master_key_id) {}
|
||||
group_id_(group_id) {}
|
||||
|
||||
SubLicenseKeySession::~SubLicenseKeySession() {
|
||||
for (SubLicenseSessionMap::iterator oec_session =
|
||||
@@ -267,9 +267,9 @@ OEMCryptoResult SubLicenseKeySession::DoSubLicenseLoadKeys(
|
||||
|
||||
std::string mac_deriv_message;
|
||||
std::string enc_deriv_message;
|
||||
GenerateMacContext(group_master_key_id_ + key.track_label(),
|
||||
GenerateMacContext(group_id_ + message.c_str(),
|
||||
&mac_deriv_message);
|
||||
GenerateEncryptContext(group_master_key_id_ + key.track_label(),
|
||||
GenerateEncryptContext(group_id_ + message.c_str(),
|
||||
&enc_deriv_message);
|
||||
|
||||
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
|
||||
Reference in New Issue
Block a user