Source release 16.4.0
This commit is contained in:
@@ -617,17 +617,22 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
} else if (query_token == QUERY_KEY_CURRENT_SRM_VERSION) {
|
||||
uint16_t current_srm_version;
|
||||
status = crypto_session->GetSrmVersion(¤t_srm_version);
|
||||
if (status == NOT_IMPLEMENTED_ERROR) {
|
||||
*query_response = QUERY_VALUE_NONE;
|
||||
return NO_ERROR;
|
||||
} else if (status != NO_ERROR) {
|
||||
LOGW("GetCurrentSRMVersion failed: status = %d",
|
||||
static_cast<int>(status));
|
||||
return status;
|
||||
switch (status) {
|
||||
case NO_ERROR: {
|
||||
*query_response = std::to_string(current_srm_version);
|
||||
return NO_ERROR;
|
||||
}
|
||||
case NO_SRM_VERSION: {
|
||||
// SRM is not supported or not applicable (ex. local display only).
|
||||
*query_response = QUERY_VALUE_NONE;
|
||||
return NO_ERROR;
|
||||
}
|
||||
default: {
|
||||
LOGW("GetCurrentSRMVersion failed: status = %d",
|
||||
static_cast<int>(status));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
*query_response = std::to_string(current_srm_version);
|
||||
return NO_ERROR;
|
||||
} else if (query_token == QUERY_KEY_SRM_UPDATE_SUPPORT) {
|
||||
bool is_srm_update_supported = crypto_session->IsSrmUpdateSupported();
|
||||
*query_response =
|
||||
|
||||
@@ -111,6 +111,18 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
return REINIT_ERROR;
|
||||
}
|
||||
|
||||
// Save parameters in case Init needs to be called again (load and restore
|
||||
// offline license)
|
||||
if (cdm_client_property_set)
|
||||
cdm_client_property_set_ = cdm_client_property_set;
|
||||
|
||||
if (forced_session_id) {
|
||||
forced_session_id_value_ = *forced_session_id;
|
||||
forced_session_id_ = &forced_session_id_value_;
|
||||
}
|
||||
|
||||
if (event_listener) event_listener_ = event_listener;
|
||||
|
||||
if (cdm_client_property_set && cdm_client_property_set->security_level() ==
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
requested_security_level_ = kLevel3;
|
||||
@@ -229,6 +241,24 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::ReleaseOfflineResources() {
|
||||
// |license_parser_| and |policy_engine_| are reset in Init. No need to
|
||||
// deallocate here.
|
||||
if (usage_support_type_ == kUsageEntrySupport &&
|
||||
has_provider_session_token() && usage_table_header_ != nullptr &&
|
||||
!is_release_) {
|
||||
UpdateUsageEntryInformation();
|
||||
}
|
||||
|
||||
if (!key_set_id_.empty()) {
|
||||
// Unreserve the license ID.
|
||||
file_handle_->UnreserveLicenseId(key_set_id_);
|
||||
}
|
||||
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
|
||||
initialized_ = false;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
CdmLicenseType license_type,
|
||||
int* error_detail) {
|
||||
@@ -239,6 +269,31 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
if (!key_set_id_.empty()) {
|
||||
file_handle_->UnreserveLicenseId(key_set_id_);
|
||||
}
|
||||
|
||||
// On android, we previously permitted an offline license to be loaded and
|
||||
// restored in the same session. OEMCrypto v16+ disallows it so we need to
|
||||
// release and initialize an OEMCrypto session. We will still prohibit
|
||||
// multiple restore attempts on the same session.
|
||||
// TODO(b/161865160): reevalute this scenario. Should we also
|
||||
// (a) only allow a restore for the same key set ID that was loaded
|
||||
// (b) if (a) is true, indicate success and do nothing else rather than
|
||||
// release resources and reinitialize.
|
||||
// We need to investigate the conditions that caused an app failure and
|
||||
// led us to add a test to support this use case as there were multiple
|
||||
// related issues.
|
||||
if (!has_license_been_loaded_ && has_license_been_restored_) {
|
||||
LOGE("Disallow multiple offline license restores");
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_3;
|
||||
}
|
||||
|
||||
if (has_license_been_loaded_) {
|
||||
CdmResponseType status = ReleaseOfflineResources();
|
||||
if (status != NO_ERROR) return status;
|
||||
status =
|
||||
Init(cdm_client_property_set_, forced_session_id_, event_listener_);
|
||||
if (status != NO_ERROR) return status;
|
||||
}
|
||||
has_license_been_restored_ = true;
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
DeviceFiles::CdmLicenseData license_data;
|
||||
@@ -281,19 +336,13 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
}
|
||||
|
||||
std::string provider_session_token;
|
||||
bool sign_fake_request = false; // TODO(b/169483174): remove this variable.
|
||||
if (usage_support_type_ == kUsageEntrySupport) {
|
||||
if (!license_parser_->ExtractProviderSessionToken(
|
||||
key_response_, &provider_session_token) ||
|
||||
usage_table_header_ == nullptr) {
|
||||
provider_session_token.clear();
|
||||
std::string fake_message("empty message");
|
||||
std::string core_message;
|
||||
std::string license_request_signature;
|
||||
// Sign a fake message so that OEMCrypto will start the rental clock. The
|
||||
// signature and generated core message are ignored.
|
||||
CdmResponseType status = crypto_session_->PrepareAndSignLicenseRequest(
|
||||
fake_message, &core_message, &license_request_signature);
|
||||
if (status != NO_ERROR) return status;
|
||||
sign_fake_request = true; // TODO(b/169483174): remove this line.
|
||||
} else if (!VerifyOfflineUsageEntry()) {
|
||||
LOGE("License usage entry is invalid, cannot restore");
|
||||
return LICENSE_USAGE_ENTRY_MISSING;
|
||||
@@ -306,6 +355,24 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sign_fake_request = true; // TODO(b/169483174): remove this block.
|
||||
}
|
||||
// TODO(b/169483174): remove this code in v17. For OEMCrypto v16, an offline
|
||||
// license would not work because the rental clock in OEMCrypto is only
|
||||
// started when the license request is signed. We will sign a fake license
|
||||
// request if the device does not support usage tables, or if the license does
|
||||
// not have a usage entry.
|
||||
if (sign_fake_request) {
|
||||
std::string fake_message("empty message");
|
||||
std::string core_message;
|
||||
std::string license_request_signature;
|
||||
// Sign a fake message so that OEMCrypto will start the rental clock. The
|
||||
// signature and generated core message are ignored.
|
||||
const CdmResponseType status =
|
||||
crypto_session_->PrepareAndSignLicenseRequest(
|
||||
fake_message, &core_message, &license_request_signature);
|
||||
if (status != NO_ERROR) return status;
|
||||
}
|
||||
|
||||
CdmResponseType result;
|
||||
@@ -429,6 +496,7 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal(
|
||||
switch (license_type) {
|
||||
case kLicenseTypeTemporary:
|
||||
is_temporary_ = true;
|
||||
is_offline_ = false;
|
||||
break;
|
||||
case kLicenseTypeStreaming:
|
||||
is_offline_ = false;
|
||||
@@ -536,7 +604,8 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
}
|
||||
sts = license_parser_->HandleKeyResponse(key_response);
|
||||
sts = license_parser_->HandleKeyResponse(/* is restore */ false,
|
||||
key_response);
|
||||
|
||||
// Update the license sdk and service versions.
|
||||
const VersionInfo& version_info = license_parser_->GetServiceVersion();
|
||||
@@ -569,7 +638,7 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
license_parser_->provider_session_token().c_str(),
|
||||
license_parser_->provider_session_token().size());
|
||||
|
||||
if (is_offline_ || has_provider_session_token()) {
|
||||
if ((is_offline_ || has_provider_session_token()) && !is_temporary_) {
|
||||
if (has_provider_session_token() &&
|
||||
usage_support_type_ == kUsageEntrySupport &&
|
||||
usage_table_header_ != nullptr) {
|
||||
@@ -584,6 +653,7 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
sts = StoreLicense();
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
has_license_been_loaded_ = true;
|
||||
|
||||
return KEY_ADDED;
|
||||
}
|
||||
@@ -732,8 +802,9 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
LOGE("CDM session not initialized");
|
||||
return NOT_INITIALIZED_ERROR;
|
||||
}
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(true, key_response);
|
||||
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(
|
||||
/* is renewal */ true,
|
||||
/* is restore */ false, key_response);
|
||||
|
||||
// Record the timing on success.
|
||||
UpdateRequestLatencyTiming(sts);
|
||||
@@ -797,8 +868,9 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
LOGE("CDM session not initialized");
|
||||
return NOT_INITIALIZED_ERROR;
|
||||
}
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(false, key_response);
|
||||
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(
|
||||
/* is renewal */ false,
|
||||
/* is restore */ false, key_response);
|
||||
// Record the timing on success.
|
||||
UpdateRequestLatencyTiming(sts);
|
||||
|
||||
|
||||
@@ -852,7 +852,22 @@ void CryptoSession::Close() {
|
||||
WithOecWriteLock(
|
||||
"Close", [&] { close_sts = OEMCrypto_CloseSession(oec_session_id_); });
|
||||
metrics_->oemcrypto_close_session_.Increment(close_sts);
|
||||
if (OEMCrypto_SUCCESS == close_sts) open_ = false;
|
||||
|
||||
if (close_sts != OEMCrypto_SUCCESS) {
|
||||
LOGW("OEMCrypto_CloseSession failed: status = %d",
|
||||
static_cast<int>(close_sts));
|
||||
}
|
||||
switch (close_sts) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
case OEMCrypto_ERROR_INVALID_SESSION:
|
||||
case OEMCrypto_ERROR_SYSTEM_INVALIDATED:
|
||||
open_ = false;
|
||||
break;
|
||||
case OEMCrypto_ERROR_CLOSE_SESSION_FAILED:
|
||||
default:
|
||||
// empty case
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::PrepareAndSignLicenseRequest(
|
||||
@@ -1898,25 +1913,33 @@ CdmResponseType CryptoSession::GetSrmVersion(uint16_t* srm_version) {
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
RETURN_IF_NULL(srm_version, PARAMETER_NULL);
|
||||
|
||||
OEMCryptoResult status;
|
||||
WithOecReadLock("GetSrmVersion", [&] {
|
||||
status = OEMCrypto_GetCurrentSRMVersion(srm_version);
|
||||
const OEMCryptoResult status = WithOecReadLock("GetSrmVersion", [&] {
|
||||
return OEMCrypto_GetCurrentSRMVersion(srm_version);
|
||||
});
|
||||
|
||||
// SRM is an optional feature. Whether it is implemented is up to the
|
||||
// discretion of OEMs
|
||||
if (status == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
LOGV("OEMCrypto_GetCurrentSRMVersion not implemented");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
// discretion of OEMs. OEMs may implement this method, but SRM is not
|
||||
// required if there is only a local display, as such no SRM version
|
||||
// is available/reportable. |srm_version| is only set if SUCCESS is
|
||||
// returned.
|
||||
switch (status) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
return NO_ERROR;
|
||||
case OEMCrypto_LOCAL_DISPLAY_ONLY:
|
||||
LOGD("No SRM: Local display only");
|
||||
return NO_SRM_VERSION;
|
||||
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
|
||||
LOGD("No SRM: Not implemented");
|
||||
return NO_SRM_VERSION;
|
||||
default:
|
||||
return MapOEMCryptoResult(status, GET_SRM_VERSION_ERROR,
|
||||
"GetCurrentSRMVersion");
|
||||
}
|
||||
|
||||
return MapOEMCryptoResult(status, GET_SRM_VERSION_ERROR,
|
||||
"GetCurrentSRMVersion");
|
||||
}
|
||||
|
||||
bool CryptoSession::IsSrmUpdateSupported() {
|
||||
LOGV("Checking if SRM update is supported");
|
||||
if (!IsInitialized()) return false;
|
||||
RETURN_IF_UNINITIALIZED(false);
|
||||
return WithOecReadLock("IsSrmUpdateSupported",
|
||||
[&] { return OEMCrypto_IsSRMUpdateSupported(); });
|
||||
}
|
||||
@@ -1929,10 +1952,9 @@ CdmResponseType CryptoSession::LoadSrm(const std::string& srm) {
|
||||
return INVALID_SRM_LIST;
|
||||
}
|
||||
|
||||
OEMCryptoResult status;
|
||||
WithOecWriteLock("LoadSrm", [&] {
|
||||
status = OEMCrypto_LoadSRM(reinterpret_cast<const uint8_t*>(srm.data()),
|
||||
srm.size());
|
||||
const OEMCryptoResult status = WithOecWriteLock("LoadSrm", [&] {
|
||||
return OEMCrypto_LoadSRM(reinterpret_cast<const uint8_t*>(srm.data()),
|
||||
srm.size());
|
||||
});
|
||||
|
||||
return MapOEMCryptoResult(status, LOAD_SRM_ERROR, "LoadSRM");
|
||||
@@ -2030,6 +2052,9 @@ bool CryptoSession::GetDecryptHashSupport(SecurityLevel security_level,
|
||||
case OEMCrypto_CRC_Clear_Buffer:
|
||||
case OEMCrypto_Partner_Defined_Hash:
|
||||
break;
|
||||
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
|
||||
*decrypt_hash_support = OEMCrypto_Hash_Not_Supported;
|
||||
break;
|
||||
default:
|
||||
// Not flagging an error since it is only used in test
|
||||
LOGW("OEMCrypto_SupportsDecryptHash unrecognized result = %d",
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kFourCcCbc1 = 0x63626331;
|
||||
const uint32_t kFourCcCbcs = 0x63626373;
|
||||
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
|
||||
@@ -32,10 +31,11 @@ const uint32_t kFourCcCenc = 0x63656e63;
|
||||
|
||||
const std::string kEmptyString;
|
||||
|
||||
// MAC key in the license are two separate MAC keys (client and server).
|
||||
constexpr size_t kLicenseMacKeySize = wvcdm::MAC_KEY_SIZE * 2;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::License;
|
||||
@@ -367,6 +367,25 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
// TODO(b/166007195): Remove this.
|
||||
CdmResponseType CdmLicense::PrepareKeyUpdateReload(CdmSession* cdm_session) {
|
||||
uint32_t api_version = 0;
|
||||
if (!crypto_session_->GetApiVersion(&api_version)) {
|
||||
LOGW("Unknown API Version");
|
||||
api_version = 15;
|
||||
}
|
||||
if (api_version != 16) return NO_ERROR;
|
||||
// To work around b/166010609, we ask OEMCrypto to prepare an unused renewal
|
||||
// request. This lets the ODK library update its clock saying when the renewal
|
||||
// was signed.
|
||||
constexpr bool is_renewal = true;
|
||||
const CdmAppParameterMap unused_app_parameters;
|
||||
CdmKeyMessage unused_request;
|
||||
std::string unused_url;
|
||||
return PrepareKeyUpdateRequest(is_renewal, unused_app_parameters, cdm_session,
|
||||
&unused_request, &unused_url);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
bool is_renewal, const CdmAppParameterMap& app_parameters,
|
||||
CdmSession* cdm_session, CdmKeyMessage* signed_request,
|
||||
@@ -511,7 +530,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
const CdmKeyResponse& license_response) {
|
||||
bool is_restore, const CdmKeyResponse& license_response) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmLicense not initialized");
|
||||
return LICENSE_PARSER_NOT_INITIALIZED_2;
|
||||
@@ -596,17 +615,17 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
mac_key_iv.assign(license.key(i).iv());
|
||||
|
||||
// Strip off PKCS#5 padding
|
||||
mac_keys.assign(license.key(i).key().data(), 2 * MAC_KEY_SIZE);
|
||||
mac_keys.assign(license.key(i).key().data(), kLicenseMacKeySize);
|
||||
}
|
||||
}
|
||||
if (license.policy().can_renew() ||
|
||||
(mac_key_iv.size() != 0 || mac_keys.size() != 0)) {
|
||||
if (mac_key_iv.size() != KEY_IV_SIZE ||
|
||||
mac_keys.size() != 2 * MAC_KEY_SIZE) {
|
||||
mac_keys.size() != kLicenseMacKeySize) {
|
||||
LOGE(
|
||||
"MAC key/IV size error: expected = %lu/%lu, "
|
||||
"MAC key/IV size error: expected = %zu/%zu, "
|
||||
"actual = %zu/%zu (key/iv)",
|
||||
2 * MAC_KEY_SIZE, KEY_IV_SIZE, mac_keys.size(), mac_key_iv.size());
|
||||
kLicenseMacKeySize, KEY_IV_SIZE, mac_keys.size(), mac_key_iv.size());
|
||||
return KEY_SIZE_ERROR_1;
|
||||
}
|
||||
}
|
||||
@@ -664,18 +683,19 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
|
||||
CdmResponseType resp = NO_CONTENT_KEY;
|
||||
if (kLicenseKeyTypeEntitlement == key_type) {
|
||||
resp =
|
||||
HandleEntitlementKeyResponse(signed_message, core_message, signature,
|
||||
mac_key_iv, mac_keys, key_array, license);
|
||||
resp = HandleEntitlementKeyResponse(is_restore, signed_message,
|
||||
core_message, signature, mac_key_iv,
|
||||
mac_keys, key_array, license);
|
||||
} else if (kLicenseKeyTypeContent == key_type) {
|
||||
resp = HandleContentKeyResponse(signed_message, core_message, signature,
|
||||
mac_key_iv, mac_keys, key_array, license);
|
||||
resp = HandleContentKeyResponse(is_restore, signed_message, core_message,
|
||||
signature, mac_key_iv, mac_keys, key_array,
|
||||
license);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
bool is_renewal, const CdmKeyResponse& license_response) {
|
||||
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmLicense not initialized");
|
||||
return LICENSE_PARSER_NOT_INITIALIZED_3;
|
||||
@@ -762,7 +782,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
}
|
||||
|
||||
if (status == KEY_ADDED) {
|
||||
policy_engine_->UpdateLicense(license);
|
||||
policy_engine_->UpdateLicense(license, is_restore);
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -817,12 +837,14 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
|
||||
license_nonce_ = original_license_request.key_control_nonce();
|
||||
}
|
||||
|
||||
CdmResponseType sts = HandleKeyResponse(license_response);
|
||||
CdmResponseType sts = HandleKeyResponse(true, license_response);
|
||||
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
|
||||
if (!license_renewal_response.empty()) {
|
||||
sts = HandleKeyUpdateResponse(true, license_renewal_response);
|
||||
sts = PrepareKeyUpdateReload(cdm_session);
|
||||
if (sts != KEY_MESSAGE) return sts;
|
||||
sts = HandleKeyUpdateResponse(true, true, license_renewal_response);
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
}
|
||||
|
||||
@@ -947,7 +969,7 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
|
||||
}
|
||||
|
||||
if (!license.id().has_provider_session_token()) {
|
||||
CdmResponseType result = HandleKeyResponse(license_response);
|
||||
CdmResponseType result = HandleKeyResponse(false, license_response);
|
||||
return result == KEY_ADDED ? NO_ERROR : result;
|
||||
}
|
||||
|
||||
@@ -1095,7 +1117,7 @@ CdmResponseType CdmLicense::PrepareContentId(
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleContentKeyResponse(
|
||||
const std::string& msg, const std::string& core_message,
|
||||
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 video_widevine::License& license) {
|
||||
@@ -1119,13 +1141,13 @@ CdmResponseType CdmLicense::HandleContentKeyResponse(
|
||||
it != key_array.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
policy_engine_->SetLicense(license, supports_core_messages());
|
||||
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
|
||||
const std::string& msg, const std::string& core_message,
|
||||
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 video_widevine::License& license) {
|
||||
@@ -1149,7 +1171,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());
|
||||
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
|
||||
|
||||
return HandleNewEntitledKeys(wrapped_keys_);
|
||||
}
|
||||
@@ -1177,7 +1199,7 @@ CdmResponseType CdmLicense::HandleNewEntitledKeys(
|
||||
if (content_key.size() < CONTENT_KEY_SIZE) {
|
||||
LOGE(
|
||||
"Entitled content key too small: "
|
||||
"expected = %lu, actual = %zu (bytes)",
|
||||
"expected = %zu, actual = %zu (bytes)",
|
||||
CONTENT_KEY_SIZE, content_key.size());
|
||||
return KEY_SIZE_ERROR_2;
|
||||
} else if (content_key.size() > CONTENT_KEY_SIZE) {
|
||||
|
||||
@@ -20,7 +20,8 @@ using video_widevine::License;
|
||||
namespace {
|
||||
|
||||
const int kCdmPolicyTimerDurationSeconds = 1;
|
||||
const int kClockSkewDelta = 5; // seconds
|
||||
const int kClockSkewDelta = 5; // seconds
|
||||
const int64_t kLicenseStateUpdateDelay = 20; // seconds
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -30,6 +31,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
WvCdmEventListener* event_listener,
|
||||
CryptoSession* crypto_session)
|
||||
: license_state_(kLicenseStateInitial),
|
||||
license_state_update_deadline_(0),
|
||||
last_recorded_current_time_(0),
|
||||
session_id_(session_id),
|
||||
event_listener_(event_listener),
|
||||
@@ -90,6 +92,17 @@ void PolicyEngine::OnTimerEvent() {
|
||||
last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds;
|
||||
const int64_t current_time = GetCurrentTime();
|
||||
|
||||
// This conditional should not succeed but being cautious in case license
|
||||
// state was not updated after the license was processed. Licenses and
|
||||
// renewals from the license service should update state on
|
||||
// |Set/UpdateLicense|. Offline licenses, when restored, should update license
|
||||
// state when |RestorePlaybackTimes| is called.
|
||||
if (license_state_update_deadline_ != 0 &&
|
||||
license_state_update_deadline_ < current_time) {
|
||||
LOGW("License state was not updated after a license was loaded/renewed");
|
||||
UpdateLicenseState(current_time);
|
||||
}
|
||||
|
||||
// If we have passed the grace period, the expiration will update.
|
||||
if (policy_timers_->HasPassedGracePeriod(current_time)) {
|
||||
NotifyExpirationUpdate(current_time);
|
||||
@@ -159,12 +172,13 @@ void PolicyEngine::OnTimerEvent() {
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicense(const License& license,
|
||||
bool supports_core_messages) {
|
||||
bool supports_core_messages,
|
||||
bool defer_license_state_update) {
|
||||
if (supports_core_messages) policy_timers_.reset(new PolicyTimersV16());
|
||||
license_id_.CopyFrom(license.id());
|
||||
license_keys_->SetFromLicense(license);
|
||||
policy_timers_->SetLicense(license);
|
||||
UpdateLicense(license);
|
||||
UpdateLicense(license, defer_license_state_update);
|
||||
}
|
||||
|
||||
void PolicyEngine::SetEntitledLicenseKeys(
|
||||
@@ -180,10 +194,11 @@ void PolicyEngine::SetLicenseForRelease(const License& license,
|
||||
// Expire any old keys.
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
policy_timers_->SetLicense(license);
|
||||
UpdateLicense(license);
|
||||
UpdateLicense(license, false);
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateLicense(const License& license) {
|
||||
void PolicyEngine::UpdateLicense(const License& license,
|
||||
bool defer_license_state_update) {
|
||||
if (!license.has_policy()) return;
|
||||
|
||||
if (kLicenseStateExpired == license_state_) {
|
||||
@@ -204,8 +219,15 @@ void PolicyEngine::UpdateLicense(const License& license) {
|
||||
|
||||
const int64_t current_time = GetCurrentTime();
|
||||
policy_timers_->UpdateLicense(current_time, license);
|
||||
if (defer_license_state_update)
|
||||
license_state_update_deadline_ = current_time + kLicenseStateUpdateDelay;
|
||||
else
|
||||
UpdateLicenseState(current_time);
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateLicenseState(int64_t current_time) {
|
||||
// Update time information
|
||||
license_state_update_deadline_ = 0;
|
||||
if (!policy_timers_->get_policy().can_play() ||
|
||||
policy_timers_->HasLicenseOrRentalOrPlaybackDurationExpired(
|
||||
current_time)) {
|
||||
@@ -349,7 +371,7 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
|
||||
last_playback_time,
|
||||
grace_period_end_time);
|
||||
|
||||
NotifyExpirationUpdate(current_time);
|
||||
UpdateLicenseState(current_time);
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
|
||||
@@ -77,6 +77,12 @@ int64_t PolicyTimersV16::GetRentalDurationRemaining(int64_t current_time) {
|
||||
return rental_expiry_time - current_time;
|
||||
}
|
||||
|
||||
bool PolicyTimersV16::HasRenewalDelayExpired(int64_t current_time) {
|
||||
return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) &&
|
||||
(renewal_start_time_ + policy_.renewal_delay_seconds() <=
|
||||
current_time);
|
||||
}
|
||||
|
||||
// For the policy time fields checked in the following methods, a value of 0
|
||||
// (UNLIMITED_DURATION) indicates that there is no limit to the duration.
|
||||
// If the fields are UNLIMITED_DURATION then these methods will return
|
||||
|
||||
@@ -336,11 +336,12 @@ bool ExtractExtensionValueFromCertificate(const std::string& cert,
|
||||
STACK_OF(X509)* certs = pkcs7->d.sign->cert;
|
||||
|
||||
// Find the desired certificate from the stack.
|
||||
if (cert_index >= static_cast<size_t>(sk_X509_num(certs))) {
|
||||
const size_t num_certs = static_cast<size_t>(sk_X509_num(certs));
|
||||
if (cert_index >= num_certs) {
|
||||
LOGE(
|
||||
"Unexpected number of certificates in chain: "
|
||||
"count = %zu, minimum = %zu",
|
||||
sk_X509_num(certs), cert_index + 1);
|
||||
num_certs, cert_index + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -229,8 +229,15 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
// entries. If not, clear files/data and recreate usage header
|
||||
// table.
|
||||
if (status == NO_ERROR && lru_success) {
|
||||
if (HasUnlimitedTableCapacity() ||
|
||||
usage_entry_info_.size() > potential_table_capacity()) {
|
||||
if ((HasUnlimitedTableCapacity() &&
|
||||
usage_entry_info_.size() > kMinimumUsageTableEntriesSupported) ||
|
||||
(!HasUnlimitedTableCapacity() &&
|
||||
usage_entry_info_.size() > potential_table_capacity())) {
|
||||
LOGD("Checking if new entry can be added: size = %zu, capacity = %s",
|
||||
usage_entry_info_.size(),
|
||||
HasUnlimitedTableCapacity()
|
||||
? "unlimited"
|
||||
: std::to_string(potential_table_capacity()).c_str());
|
||||
uint32_t temporary_usage_entry_number;
|
||||
|
||||
// Create a new temporary usage entry, close the session and then
|
||||
@@ -1048,7 +1055,7 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
||||
bad_license_file_entries.push_back(usage_entry_number);
|
||||
continue;
|
||||
default: {
|
||||
LOGW("Unknown usage entry storage type: %d, usage_entry_number = %u",
|
||||
LOGW("Unknown usage entry storage type: %d, usage_entry_number = %zu",
|
||||
static_cast<int>(usage_entry_info.storage_type),
|
||||
usage_entry_number);
|
||||
bad_license_file_entries.push_back(usage_entry_number);
|
||||
@@ -1056,7 +1063,7 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
||||
}
|
||||
}
|
||||
if (!retrieve_response) {
|
||||
LOGW("Could not retrieve license message: usage_entry_number = %u",
|
||||
LOGW("Could not retrieve license message: usage_entry_number = %zu",
|
||||
usage_entry_number);
|
||||
bad_license_file_entries.push_back(usage_entry_number);
|
||||
continue;
|
||||
@@ -1064,7 +1071,7 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
||||
|
||||
if (retrieved_entry_number != usage_entry_number) {
|
||||
LOGW(
|
||||
"Usage entry number mismatched: usage_entry_number = %u, "
|
||||
"Usage entry number mismatched: usage_entry_number = %zu, "
|
||||
"retrieved_entry_number = %u",
|
||||
usage_entry_number, retrieved_entry_number);
|
||||
bad_license_file_entries.push_back(usage_entry_number);
|
||||
@@ -1073,7 +1080,7 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
||||
|
||||
video_widevine::License license;
|
||||
if (!ParseLicenseFromLicenseMessage(license_message, &license)) {
|
||||
LOGW("Could not parse license: usage_entry_number = %u",
|
||||
LOGW("Could not parse license: usage_entry_number = %zu",
|
||||
usage_entry_number);
|
||||
bad_license_file_entries.push_back(usage_entry_number);
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user