CDM Core: Removed support for v15 licenses.

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

OEMCrypto v15 licenses made use of several now-obsolete API functions
of OEMCrypto (mainly LoadKeys and RefreshKeys).  All license handled
by the CDM must be v16 or newer.  The CDM can now rely on all license
requests/responses containing a core message, using v16 policy timers,
and requires loading using LoadLicense() / LoadRenewal().

Bug: 252670759
Test: run_x86_64_tests and policy_engine_unittest
Change-Id: I3f65a6ec0326b4c89d1919b8911e065079cb90d2
This commit is contained in:
Alex Dale
2022-10-27 17:05:20 -07:00
parent 4a4ee80cad
commit b26126d3aa
8 changed files with 200 additions and 2301 deletions

View File

@@ -199,7 +199,6 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
supports_core_messages_(true),
use_privacy_mode_(false),
clock_(new wvutil::Clock()),
license_key_type_(kLicenseKeyTypeContent) {}
@@ -211,7 +210,6 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock)
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
supports_core_messages_(true),
use_privacy_mode_(false),
license_key_type_(kLicenseKeyTypeContent) {
clock_.reset(clock);
@@ -368,9 +366,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
signed_message.set_msg(serialized_license_req);
if (core_message.size() > 0) {
signed_message.set_oemcrypto_core_message(core_message);
}
signed_message.set_oemcrypto_core_message(core_message);
signed_message.SerializeToString(signed_request);
@@ -383,7 +379,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateReload(CdmSession* cdm_session) {
uint32_t api_version = 0;
if (!crypto_session_->GetApiVersion(&api_version)) {
LOGW("Unknown API Version");
api_version = 15;
api_version = 16;
}
if (api_version != 16) return NO_ERROR;
// To work around b/166010609, we ask OEMCrypto to prepare an unused renewal
@@ -484,27 +480,6 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
current_license->set_seconds_since_last_played(seconds_since_last_played);
}
uint32_t api_version = 0;
if (!crypto_session_->GetApiVersion(&api_version)) {
LOGW("Unknown API Version");
api_version = 15;
}
if (api_version < 16) {
// For a pre-v16 license, get/set the nonce. This value will be reflected
// in the Key Control Block of the license response.
const CdmResponseType status =
crypto_session_->GenerateNonce(&license_nonce_);
switch (status) {
case NO_ERROR:
break;
case SESSION_LOST_STATE_ERROR:
case SYSTEM_INVALIDATED_ERROR:
return status;
default:
return LICENSE_RENEWAL_NONCE_GENERATION_ERROR;
}
}
license_request.set_key_control_nonce(license_nonce_);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
@@ -528,11 +503,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
signed_message.set_msg(serialized_license_req);
if (supports_core_messages()) {
// Only include the |core_message| in renewal requests if it is
// already known that the license is v16.
signed_message.set_oemcrypto_core_message(core_message);
}
signed_message.set_oemcrypto_core_message(core_message);
signed_message.SerializeToString(signed_request);
*server_url = server_url_;
@@ -586,20 +557,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return LICENSE_RESPONSE_NOT_SIGNED;
}
// Check that the server returned a |core_message|. If missing, then
// the server is assumed to operate as V15. This will imply that the
// |signature| field in the response does not include a core message
// either.
if (!signed_response.has_oemcrypto_core_message() ||
signed_response.oemcrypto_core_message().empty()) {
supports_core_messages_ = false;
}
const std::string& signed_message = signed_response.msg();
const std::string core_message =
signed_response.has_oemcrypto_core_message()
? signed_response.oemcrypto_core_message()
: std::string();
const std::string& core_message = signed_response.oemcrypto_core_message();
const std::string& signature = signed_response.signature();
License license;
@@ -631,7 +590,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
}
if (license.policy().can_renew() ||
(mac_key_iv.size() != 0 || mac_keys.size() != 0)) {
(!mac_key_iv.empty() || !mac_keys.empty())) {
if (mac_key_iv.size() != KEY_IV_SIZE ||
mac_keys.size() != kLicenseMacKeySize) {
LOGE(
@@ -688,13 +647,12 @@ CdmResponseType CdmLicense::HandleKeyResponse(
CdmResponseType resp = NO_CONTENT_KEY;
if (kLicenseKeyTypeEntitlement == key_type) {
resp = HandleEntitlementKeyResponse(is_restore, signed_message,
core_message, signature, mac_key_iv,
mac_keys, key_array, license);
resp =
HandleEntitlementKeyResponse(is_restore, signed_message, core_message,
signature, key_array, license);
} else if (kLicenseKeyTypeContent == key_type) {
resp = HandleContentKeyResponse(is_restore, signed_message, core_message,
signature, mac_key_iv, mac_keys, key_array,
license);
signature, key_array, license);
}
return resp;
}
@@ -727,29 +685,19 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
return INVALID_LICENSE_TYPE;
}
// At this point of the license life-cycle (handling a renewal), we should
// already know if the license is v15 or not. If license is v16, then a
// renewal should have a |core_message| present; otherwise there might have
// been some tampering with the request or response. On the other hand, a
// release is processed without loading the license, so OEMCrypto does not
// know if it is v15 or v16, and will not add a core message.
if (is_renewal && supports_core_messages() &&
(!signed_response.has_oemcrypto_core_message() ||
signed_response.oemcrypto_core_message().empty())) {
const std::string& signed_message = signed_response.msg();
const std::string& core_message = signed_response.oemcrypto_core_message();
const std::string& signature = signed_response.signature();
if (is_renewal && core_message.empty()) {
LOGE("Renewal response is missing |core_message| field");
return CORE_MESSAGE_NOT_FOUND;
}
if (!signed_response.has_signature()) {
if (signature.empty()) {
LOGE("Update key response is missing signature");
return SIGNATURE_NOT_FOUND;
}
const std::string& signed_message = signed_response.msg();
const std::string core_message =
signed_response.has_oemcrypto_core_message()
? signed_response.oemcrypto_core_message()
: std::string();
const std::string& signature = signed_response.signature();
License license;
if (!license.ParseFromString(signed_message)) {
@@ -777,19 +725,13 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
server_url_ = license.policy().renewal_server_url();
}
CdmResponseType status;
// If the field is not set, it will default to false.
status =
CdmResponseType status =
crypto_session_->UseSecondaryKey(signed_response.using_secondary_key());
if (status != NO_ERROR) return status;
if (supports_core_messages()) {
status =
crypto_session_->LoadRenewal(signed_message, core_message, signature);
} else {
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
status = crypto_session_->RefreshKeys(signed_message, signature, key_array);
}
status =
crypto_session_->LoadRenewal(signed_message, core_message, signature);
if (status == KEY_ADDED) {
policy_engine_->UpdateLicense(license, is_restore);
@@ -834,13 +776,6 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
return INVALID_LICENSE_REQUEST_TYPE_1;
}
if (!signed_request.has_oemcrypto_core_message() ||
signed_request.oemcrypto_core_message().empty()) {
// Pre V16 license did not include |core_message| components.
// The license response is checked by HandleKeyResponse().
supports_core_messages_ = false;
}
key_request_ = signed_request.msg();
LicenseRequest original_license_request;
if (!original_license_request.ParseFromString(key_request_)) {
@@ -928,11 +863,6 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
return INVALID_LICENSE_REQUEST_TYPE_2;
}
if (!signed_request.has_oemcrypto_core_message()) {
// Pre V16 license did not include |core_message| components.
supports_core_messages_ = false;
}
key_request_ = signed_request.msg();
SignedMessage signed_response;
@@ -953,12 +883,9 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
return SIGNATURE_NOT_FOUND_2;
}
if (!signed_response.has_oemcrypto_core_message() ||
signed_response.oemcrypto_core_message().empty()) {
// Possible that the request contains a |core_message|, but the
// response does not. This would occur if the licensing server
// is v15.
supports_core_messages_ = false;
if (!signed_response.has_oemcrypto_core_message()) {
LOGE("License response is missing core message");
return CORE_MESSAGE_NOT_FOUND;
}
License license;
@@ -991,7 +918,7 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
// If the policy engine already has keys, they will now expire.
// If the policy engine does not already have keys, this will not add any.
policy_engine_->SetLicenseForRelease(license, supports_core_messages());
policy_engine_->SetLicenseForRelease(license);
return NO_ERROR;
}
@@ -1137,52 +1064,34 @@ CdmResponseType CdmLicense::PrepareContentId(
CdmResponseType CdmLicense::HandleContentKeyResponse(
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const std::string& signature, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("No content keys provided");
return NO_CONTENT_KEY;
}
CdmResponseType resp;
if (supports_core_messages()) {
resp = crypto_session_->LoadLicense(msg, core_message, signature,
kLicenseKeyTypeContent);
} else {
resp = crypto_session_->LoadKeys(
msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_,
license.srm_requirement(), kLicenseKeyTypeContent);
}
const CdmResponseType resp = crypto_session_->LoadLicense(
msg, core_message, signature, 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());
for (const CryptoKey& key : key_array) {
loaded_keys_.insert(key.key_id());
}
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
policy_engine_->SetLicense(license, is_restore);
}
return resp;
}
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const std::string& signature, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("No entitlement keys provided");
return NO_CONTENT_KEY;
}
CdmResponseType resp;
if (supports_core_messages()) {
resp = crypto_session_->LoadLicense(msg, core_message, signature,
kLicenseKeyTypeEntitlement);
} else {
resp = crypto_session_->LoadKeys(
msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_,
license.srm_requirement(), kLicenseKeyTypeEntitlement);
}
const CdmResponseType resp = crypto_session_->LoadLicense(
msg, core_message, signature, kLicenseKeyTypeEntitlement);
if (KEY_ADDED != resp) {
return resp;
@@ -1190,8 +1099,7 @@ CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
// Save the entitlement keys for future use to handle key changes.
entitlement_keys_.CopyFrom(license.key());
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
policy_engine_->SetLicense(license, is_restore);
return HandleNewEntitledKeys(wrapped_keys_);
}
@@ -1199,53 +1107,42 @@ CdmResponseType CdmLicense::HandleNewEntitledKeys(
const std::vector<WidevinePsshData_EntitledKey>& wrapped_keys) {
std::vector<CryptoKey> entitled_key_array;
entitled_key_array.reserve(wrapped_keys.size());
for (RepeatedPtrField<License_KeyContainer>::const_iterator kc =
entitlement_keys_.begin();
kc != entitlement_keys_.end(); ++kc) {
if (kc->type() != video_widevine::License::KeyContainer::ENTITLEMENT) {
for (const auto& kc : entitlement_keys_) {
if (kc.type() != video_widevine::License::KeyContainer::ENTITLEMENT) {
continue;
}
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator wk =
wrapped_keys.begin();
wk != wrapped_keys.end(); wk++) {
if (wk->entitlement_key_id() == kc->id()) {
// Add a new entry to the key array to load oemcrypto.
entitled_key_array.resize(entitled_key_array.size() + 1);
// Strip PKCS#5 padding from entitled content keys.
std::string content_key = wk->key();
if (content_key.size() < CONTENT_KEY_SIZE) {
LOGE(
"Entitled content key too small: "
"expected = %zu, actual = %zu (bytes)",
CONTENT_KEY_SIZE, content_key.size());
return KEY_SIZE_ERROR_2;
} else if (content_key.size() > CONTENT_KEY_SIZE) {
content_key.resize(CONTENT_KEY_SIZE);
}
CryptoKey& this_entry = entitled_key_array.back();
this_entry.set_key_id(wk->key_id());
this_entry.set_key_data_iv(wk->iv());
this_entry.set_entitlement_key_id(wk->entitlement_key_id());
this_entry.set_key_data(content_key);
for (const auto& wk : wrapped_keys) {
if (wk.entitlement_key_id() != kc.id()) continue;
// Strip PKCS#5 padding from entitled content keys.
std::string content_key = wk.key();
if (content_key.size() < CONTENT_KEY_SIZE) {
LOGE(
"Entitled content key too small: "
"expected = %zu, actual = %zu (bytes)",
CONTENT_KEY_SIZE, content_key.size());
return KEY_SIZE_ERROR_2;
} else if (content_key.size() > CONTENT_KEY_SIZE) {
content_key.resize(CONTENT_KEY_SIZE);
}
CryptoKey this_entry;
this_entry.set_key_id(wk.key_id());
this_entry.set_key_data_iv(wk.iv());
this_entry.set_entitlement_key_id(wk.entitlement_key_id());
this_entry.set_key_data(content_key);
// Add a new entry to the key array to load oemcrypto.
entitled_key_array.push_back(std::move(this_entry));
}
}
CdmResponseType resp =
const CdmResponseType resp =
crypto_session_->LoadEntitledContentKeys(entitled_key_array);
if (KEY_ADDED == resp) {
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator it =
wrapped_keys.begin();
it != wrapped_keys.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetEntitledLicenseKeys(wrapped_keys);
if (resp != KEY_ADDED) return resp;
for (const auto& wk : wrapped_keys) {
loaded_keys_.insert(wk.key_id());
}
return resp;
policy_engine_->SetEntitledLicenseKeys(wrapped_keys);
return KEY_ADDED;
}
template <typename T>