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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user