Source release 16.3.0

This commit is contained in:
John W. Bruce
2020-07-24 14:30:03 -07:00
parent b830b1d1fb
commit 160df9f57a
74 changed files with 4632 additions and 2561 deletions

View File

@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <iostream>
#include <limits>
#include <list>
#include <memory>
#include <sstream>
@@ -62,6 +63,7 @@ class UsagePropertySet : public CdmClientPropertySet {
void set_session_sharing_id(uint32_t /* id */) override {}
const std::string& app_id() const override { return app_id_; }
void set_app_id(const std::string& appId) { app_id_ = appId; }
bool use_atsc_mode() const override { return false; }
private:
std::string app_id_;
@@ -73,7 +75,6 @@ CdmEngine::CdmEngine(FileSystem* file_system,
std::shared_ptr<metrics::EngineMetrics> metrics)
: metrics_(metrics),
cert_provisioning_(),
cert_provisioning_requested_security_level_(kLevelDefault),
file_system_(file_system),
spoid_(EMPTY_SPOID),
usage_session_(),
@@ -136,8 +137,6 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
new_session->Init(property_set, forced_session_id, event_listener);
if (sts != NO_ERROR) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
new_session->GetRequestedSecurityLevel();
// Reserve a session ID so the CDM can return success.
if (session_id) *session_id = new_session->GenerateSessionId();
} else {
@@ -172,10 +171,13 @@ CdmResponseType CdmEngine::OpenKeySetSession(
key_set_in_use =
release_key_sets_.find(key_set_id) != release_key_sets_.end();
}
if (key_set_in_use) CloseKeySetSession(key_set_id);
if (key_set_in_use) {
LOGD("Reopening existing key session");
CloseKeySetSession(key_set_id);
}
CdmSessionId session_id;
CdmResponseType sts =
const CdmResponseType sts =
OpenSession(KEY_SYSTEM, property_set, event_listener,
nullptr /* forced_session_id */, &session_id);
@@ -294,10 +296,6 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
if (KEY_ADDED == sts) {
return sts;
} else if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
session->GetRequestedSecurityLevel();
}
LOGE("Key request generation failed, status = %d", static_cast<int>(sts));
return sts;
}
@@ -414,10 +412,6 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
&error_detail);
session->GetMetrics()->cdm_session_restore_offline_session_.Increment(
sts, error_detail);
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
session->GetRequestedSecurityLevel();
}
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
LOGE("Restore offline session failed: status = %d", static_cast<int>(sts));
}
@@ -703,6 +697,12 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
LOGW("GetMaxUsageTableEntries failed");
return UNKNOWN_ERROR;
}
if (max_number_of_usage_entries == 0) {
// Zero indicates that the table is dynamically allocated and does
// not have a defined limit. Setting to max value of int32_t to
// be able to fit into a Java int.
max_number_of_usage_entries = std::numeric_limits<int32_t>::max();
}
*query_response = std::to_string(max_number_of_usage_entries);
return NO_ERROR;
} else if (query_token == QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION) {
@@ -895,7 +895,8 @@ bool CdmEngine::IsSecurityLevelSupported(CdmSecurityLevel level) {
*/
CdmResponseType CdmEngine::GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
const std::string& service_certificate, CdmProvisioningRequest* request,
const std::string& service_certificate,
SecurityLevel requested_security_level, CdmProvisioningRequest* request,
std::string* default_url) {
LOGI("Getting provisioning request");
if (!request) {
@@ -915,7 +916,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
if (status != NO_ERROR) return status;
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
requested_security_level, cert_type, cert_authority,
file_system_->origin(), spoid_, request, default_url);
if (ret != NO_ERROR) {
cert_provisioning_.reset(); // Release resources.
@@ -931,7 +932,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
* Returns NO_ERROR for success and CdmResponseType error code if fails.
*/
CdmResponseType CdmEngine::HandleProvisioningResponse(
const CdmProvisioningResponse& response, std::string* cert,
const CdmProvisioningResponse& response,
SecurityLevel requested_security_level, std::string* cert,
std::string* wrapped_key) {
LOGI("Handling provision request");
if (response.empty()) {
@@ -955,10 +957,9 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
std::unique_ptr<CryptoSession> crypto_session(
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
CdmResponseType status;
M_TIME(status = crypto_session->Open(
cert_provisioning_requested_security_level_),
M_TIME(status = crypto_session->Open(requested_security_level),
metrics_->GetCryptoMetrics(), crypto_session_open_, status,
cert_provisioning_requested_security_level_);
requested_security_level);
if (NO_ERROR != status) {
LOGE("Provisioning object missing and crypto session open failed");
return EMPTY_PROVISIONING_CERTIFICATE_2;
@@ -1124,14 +1125,19 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
property_set.set_security_level(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("Cannot initialize device files: security_level = %s",
security_level == kSecurityLevelL3 ? "L3" : "Default");
return REMOVE_OFFLINE_LICENSE_ERROR_1;
}
CdmResponseType sts = OpenKeySetSession(key_set_id, &property_set,
nullptr /* event listener */);
if (sts != NO_ERROR) {
if (!handle.Init(security_level)) {
LOGE("Cannot initialize device files");
}
LOGE("Failed to open key set session: status = %d", static_cast<int>(sts));
handle.DeleteLicense(key_set_id);
return REMOVE_OFFLINE_LICENSE_ERROR_1;
return sts;
}
CdmSessionId session_id;
@@ -1151,6 +1157,14 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
session_id = iter->second.first;
sts = RemoveLicense(session_id);
}
} else if (sts == LICENSE_USAGE_ENTRY_MISSING) {
// It is possible that the CDM is tracking a key set ID, but has
// removed the usage information associated with it. In this case,
// it will no longer be possible to load the license for release;
// and the file should simply be deleted.
LOGW("License usage entry is missing, deleting license file");
handle.DeleteLicense(key_set_id);
sts = NO_ERROR;
}
if (sts != NO_ERROR) {

View File

@@ -159,8 +159,12 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
// License server client ID token is a stored certificate. Stage it or
// indicate that provisioning is needed. Get token from stored certificate
std::string wrapped_key;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
&serial_number, nullptr)) {
bool atsc_mode_enabled = false;
if (cdm_client_property_set != nullptr)
atsc_mode_enabled = cdm_client_property_set->use_atsc_mode();
if (!file_handle_->RetrieveCertificate(atsc_mode_enabled, &client_token,
&wrapped_key, &serial_number,
nullptr)) {
return NEED_PROVISIONING;
}
CdmResponseType load_cert_sts;
@@ -186,7 +190,7 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
if (forced_session_id) {
key_set_id_ = *forced_session_id;
} else {
bool ok = GenerateKeySetId(&key_set_id_);
bool ok = GenerateKeySetId(atsc_mode_enabled, &key_set_id_);
(void)ok; // ok is now used when assertions are turned off.
assert(ok);
}
@@ -282,6 +286,17 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
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;
} else if (!VerifyOfflineUsageEntry()) {
LOGE("License usage entry is invalid, cannot restore");
return LICENSE_USAGE_ENTRY_MISSING;
} else {
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
@@ -529,16 +544,18 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
metrics_->license_sdk_version_.Record(
version_info.license_service_version());
// Update or delete entry if usage table header+entries are supported
// Update or invalidate entry if usage table header+entries are supported
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty() && usage_table_header_ != nullptr) {
if (sts != KEY_ADDED) {
CdmResponseType delete_sts = usage_table_header_->DeleteEntry(
usage_entry_number_, file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment(delete_sts);
if (delete_sts != NO_ERROR) {
LOGW("Delete usage entry failed: status = %d",
static_cast<int>(delete_sts));
const CdmResponseType invalidate_sts =
usage_table_header_->InvalidateEntry(
usage_entry_number_, true, file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment(
invalidate_sts);
if (invalidate_sts != NO_ERROR) {
LOGW("Invalidate usage entry failed: status = %d",
static_cast<int>(invalidate_sts));
}
}
}
@@ -824,8 +841,8 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
sts = usage_table_header_->DeleteEntry(usage_entry_number, file_handle_.get(),
crypto_metrics_);
sts = usage_table_header_->InvalidateEntry(
usage_entry_number, true, file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment(sts);
return sts;
}
@@ -844,7 +861,8 @@ CdmSessionId CdmSession::GenerateSessionId() {
return SESSION_ID_PREFIX + IntToString(++session_num);
}
bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
bool CdmSession::GenerateKeySetId(bool atsc_mode_enabled,
CdmKeySetId* key_set_id) {
RETURN_FALSE_IF_NULL(key_set_id);
std::vector<uint8_t> random_data(
@@ -856,7 +874,10 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
return false;
}
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
if (atsc_mode_enabled)
*key_set_id = ATSC_KEY_SET_ID_PREFIX + b2a_hex(random_data);
else
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
// key set collision
if (file_handle_->LicenseExists(*key_set_id)) {
@@ -960,11 +981,10 @@ CdmResponseType CdmSession::RemoveKeys() {
}
CdmResponseType CdmSession::RemoveLicense() {
CdmResponseType sts = NO_ERROR;
if (is_offline_ || has_provider_session_token()) {
if (usage_support_type_ == kUsageEntrySupport &&
has_provider_session_token()) {
sts = DeleteUsageEntry(usage_entry_number_);
DeleteUsageEntry(usage_entry_number_);
}
DeleteLicenseFile();
}
@@ -1131,6 +1151,25 @@ void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) {
license_request_latency_.Clear();
}
bool CdmSession::VerifyOfflineUsageEntry() {
// Check that the current license is the same as the expected
// entry in the usage table. It is possible that the license has
// been removed from the usage table but the license file remains.
if (usage_entry_number_ >= usage_table_header_->size()) {
LOGD("License usage entry does not exist: entry_number = %u, size = %zu",
usage_entry_number_, usage_table_header_->size());
return false;
}
const CdmUsageEntryInfo& usage_entry_info =
usage_table_header_->usage_entry_info().at(usage_entry_number_);
if (usage_entry_info.storage_type != kStorageLicense ||
usage_entry_info.key_set_id != key_set_id_) {
LOGD("License usage entry does not match");
return false;
}
return true;
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {

View File

@@ -708,12 +708,10 @@ uint8_t CryptoSession::GetSecurityPatchLevel() {
}
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGD(
"Opening crypto session: requested_security_level: "
"requested_security_level = %s",
requested_security_level == kLevel3
? QUERY_VALUE_SECURITY_LEVEL_L3.c_str()
: QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str());
LOGD("Opening crypto session: requested_security_level = %s",
requested_security_level == kLevel3
? QUERY_VALUE_SECURITY_LEVEL_L3.c_str()
: QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str());
RETURN_IF_UNINITIALIZED(UNKNOWN_ERROR);
if (open_) return NO_ERROR;
@@ -750,12 +748,13 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
open_ = true;
// Get System ID and save it.
if (GetSystemIdInternal(&system_id_) == NO_ERROR) {
result = GetSystemIdInternal(&system_id_);
if (result == NO_ERROR) {
metrics_->crypto_session_system_id_.Record(system_id_);
} else {
LOGE("Failed to fetch system ID");
metrics_->crypto_session_system_id_.SetError(LOAD_SYSTEM_ID_ERROR);
return LOAD_SYSTEM_ID_ERROR;
metrics_->crypto_session_system_id_.SetError(result);
return result;
}
// Set up request ID
@@ -802,35 +801,33 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
CdmSecurityLevel security_level = GetSecurityLevel();
if (security_level == kSecurityLevelL1 ||
security_level == kSecurityLevelL3) {
{
// This block cannot use |WithStaticFieldWriteLock| because it needs
// to unlock the lock partway through.
LOGV("Static field write lock: Open() initializing usage table");
std::unique_lock<shared_mutex> auto_lock(static_field_mutex_);
// This block cannot use |WithStaticFieldWriteLock| because it needs
// to unlock the lock partway through.
LOGV("Static field write lock: Open() initializing usage table");
std::unique_lock<shared_mutex> auto_lock(static_field_mutex_);
UsageTableHeader** header = security_level == kSecurityLevelL1
? &usage_table_header_l1_
: &usage_table_header_l3_;
if (*header == nullptr) {
*header = new UsageTableHeader();
// Ignore errors since we do not know when a session is opened,
// if it is intended to be used for offline/usage session related
// or otherwise.
auto_lock.unlock();
bool is_usage_table_header_inited =
(*header)->Init(security_level, this);
auto_lock.lock();
if (!is_usage_table_header_inited) {
delete *header;
*header = nullptr;
usage_table_header_ = nullptr;
return NO_ERROR;
}
UsageTableHeader** header = security_level == kSecurityLevelL1
? &usage_table_header_l1_
: &usage_table_header_l3_;
if (*header == nullptr) {
*header = new UsageTableHeader();
// Ignore errors since we do not know when a session is opened,
// if it is intended to be used for offline/usage session related
// or otherwise.
auto_lock.unlock();
bool is_usage_table_header_inited =
(*header)->Init(security_level, this);
auto_lock.lock();
if (!is_usage_table_header_inited) {
delete *header;
*header = nullptr;
usage_table_header_ = nullptr;
return NO_ERROR;
}
usage_table_header_ = *header;
metrics_->usage_table_header_initial_size_.Record((*header)->size());
}
}
usage_table_header_ = *header;
metrics_->usage_table_header_initial_size_.Record((*header)->size());
} // End |static_field_mutex_| block.
}
} else {
metrics_->oemcrypto_usage_table_support_.SetError(result);
@@ -943,7 +940,7 @@ CdmResponseType CryptoSession::LoadKeys(
update_usage_table_after_close_session_ = true;
return KEY_ADDED;
case OEMCrypto_ERROR_TOO_MANY_KEYS:
return INSUFFICIENT_CRYPTO_RESOURCES_4;
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE:
// Handle vendor specific error
return NEED_PROVISIONING;
@@ -983,7 +980,7 @@ CdmResponseType CryptoSession::LoadLicense(const std::string& signed_message,
return LOAD_LICENSE_ERROR;
case OEMCrypto_ERROR_TOO_MANY_KEYS:
LOGE("Too many keys in license");
return INSUFFICIENT_CRYPTO_RESOURCES_4;
return INSUFFICIENT_CRYPTO_RESOURCES;
default:
break;
}
@@ -1185,16 +1182,15 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
CdmResponseType CryptoSession::LoadEntitledContentKeys(
const std::vector<CryptoKey>& key_array) {
OEMCryptoResult sts;
WithOecSessionLock("LoadEntitledContentKeys", [&] {
sts = key_session_->LoadEntitledContentKeys(key_array);
});
const OEMCryptoResult sts = WithOecSessionLock(
"LoadEntitledContentKeys",
[&] { return key_session_->LoadEntitledContentKeys(key_array); });
switch (sts) {
case OEMCrypto_SUCCESS:
return KEY_ADDED;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES_6;
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_INVALID_CONTEXT:
return NOT_AN_ENTITLEMENT_SESSION;
case OEMCrypto_KEY_NOT_ENTITLED:
@@ -1243,39 +1239,46 @@ CdmResponseType CryptoSession::LoadCertificatePrivateKey(
// Private.
CdmResponseType CryptoSession::SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
OEMCryptoResult sts;
WithOecSessionLock(
"SelectKey", [&] { sts = key_session_->SelectKey(key_id, cipher_mode); });
const OEMCryptoResult sts = WithOecSessionLock("SelectKey", [&] {
return key_session_->SelectKey(key_id, cipher_mode);
});
switch (sts) {
// SelectKey errors.
case OEMCrypto_SUCCESS:
return NO_ERROR;
case OEMCrypto_ERROR_KEY_EXPIRED:
return NEED_KEY;
case OEMCrypto_ERROR_INSUFFICIENT_HDCP:
return INSUFFICIENT_OUTPUT_PROTECTION;
case OEMCrypto_ERROR_ANALOG_OUTPUT:
return ANALOG_OUTPUT_ERROR;
case OEMCrypto_ERROR_INVALID_SESSION:
return INVALID_SESSION_1;
case OEMCrypto_ERROR_NO_DEVICE_KEY:
return NO_DEVICE_KEY_1;
case OEMCrypto_ERROR_NO_CONTENT_KEY:
return NO_CONTENT_KEY_2;
case OEMCrypto_KEY_NOT_LOADED: // obsolete.
return NO_CONTENT_KEY_3;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES_2;
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
return UNKNOWN_SELECT_KEY_ERROR_1;
case OEMCrypto_ERROR_SESSION_LOST_STATE:
return SESSION_LOST_STATE_ERROR;
case OEMCrypto_ERROR_SYSTEM_INVALIDATED:
return SYSTEM_INVALIDATED_ERROR;
case OEMCrypto_ERROR_CONTROL_INVALID:
case OEMCrypto_ERROR_KEYBOX_INVALID:
default:
return UNKNOWN_SELECT_KEY_ERROR_2;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
return UNKNOWN_SELECT_KEY_ERROR_1;
case OEMCrypto_ERROR_ANALOG_OUTPUT:
return ANALOG_OUTPUT_ERROR;
case OEMCrypto_ERROR_INSUFFICIENT_HDCP:
return INSUFFICIENT_OUTPUT_PROTECTION;
// LoadEntitledContentKeys errors.
// |key_session_| may make calls to OEMCrypto_LoadEntitledContentKeys
// if the key selected has not yet been loaded.
case OEMCrypto_ERROR_INVALID_CONTEXT:
return NOT_AN_ENTITLEMENT_SESSION;
case OEMCrypto_KEY_NOT_ENTITLED:
return NO_MATCHING_ENTITLEMENT_KEY;
// Obsolete errors.
case OEMCrypto_KEY_NOT_LOADED:
return NO_CONTENT_KEY_3;
// Catch all else.
default:
return MapOEMCryptoResult(sts, UNKNOWN_SELECT_KEY_ERROR_2, "SelectKey");
}
}
@@ -1530,7 +1533,7 @@ CdmResponseType CryptoSession::Decrypt(
case OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION:
return NO_ERROR;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES_5;
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_KEY_EXPIRED:
return NEED_KEY;
case OEMCrypto_ERROR_INVALID_SESSION:
@@ -2005,6 +2008,11 @@ bool CryptoSession::GetMaximumUsageTableEntries(SecurityLevel security_level,
metrics_->oemcrypto_maximum_usage_table_header_size_.Record(
*number_of_entries);
if (*number_of_entries == 0) {
// Special value, indicating that the table size is not directly
// limited.
return true;
}
return *number_of_entries >= kMinimumUsageTableEntriesSupported;
}
@@ -2349,18 +2357,20 @@ CdmResponseType CryptoSession::GetUsageSupportType(
}
CdmResponseType CryptoSession::CreateUsageTableHeader(
SecurityLevel requested_security_level,
CdmUsageTableHeader* usage_table_header) {
LOGV("Creating usage table header: id = %u", oec_session_id_);
LOGV("Creating usage table header: requested_security_level = %s",
requested_security_level == kLevel3
? QUERY_VALUE_SECURITY_LEVEL_L3.c_str()
: QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str());
RETURN_IF_NULL(usage_table_header, PARAMETER_NULL);
usage_table_header->resize(kEstimatedInitialUsageTableHeader);
size_t usage_table_header_size = usage_table_header->size();
OEMCryptoResult result;
WithOecWriteLock("CreateUsageTableHeader Attempt 1", [&] {
result = OEMCrypto_CreateUsageTableHeader(
requested_security_level_,
requested_security_level,
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_size);
@@ -2371,7 +2381,7 @@ CdmResponseType CryptoSession::CreateUsageTableHeader(
usage_table_header->resize(usage_table_header_size);
WithOecWriteLock("CreateUsageTableHeader Attempt 2", [&] {
result = OEMCrypto_CreateUsageTableHeader(
requested_security_level_,
requested_security_level,
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_size);
@@ -2379,22 +2389,28 @@ CdmResponseType CryptoSession::CreateUsageTableHeader(
});
}
if (result == OEMCrypto_SUCCESS) {
usage_table_header->resize(usage_table_header_size);
switch (result) {
case OEMCrypto_SUCCESS:
usage_table_header->resize(usage_table_header_size);
return NO_ERROR;
default:
return MapOEMCryptoResult(result, CREATE_USAGE_TABLE_ERROR,
"CreateUsageTableHeader");
}
return MapOEMCryptoResult(result, CREATE_USAGE_TABLE_ERROR,
"CreateUsageTableHeader");
}
CdmResponseType CryptoSession::LoadUsageTableHeader(
SecurityLevel requested_security_level,
const CdmUsageTableHeader& usage_table_header) {
LOGV("Loading usage table header: id = %u", oec_session_id_);
LOGV("Loading usage table header: requested_security_level = %s",
requested_security_level == kLevel3
? QUERY_VALUE_SECURITY_LEVEL_L3.c_str()
: QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str());
OEMCryptoResult result;
WithOecWriteLock("LoadUsageTableHeader", [&] {
result = OEMCrypto_LoadUsageTableHeader(
requested_security_level_,
requested_security_level,
reinterpret_cast<const uint8_t*>(usage_table_header.data()),
usage_table_header.size());
metrics_->oemcrypto_load_usage_table_header_.Increment(result);
@@ -2427,6 +2443,48 @@ CdmResponseType CryptoSession::LoadUsageTableHeader(
}
}
CdmResponseType CryptoSession::ShrinkUsageTableHeader(
SecurityLevel requested_security_level, uint32_t new_entry_count,
CdmUsageTableHeader* usage_table_header) {
LOGV("Shrinking usage table header: requested_security_level = %s",
requested_security_level == kLevel3
? QUERY_VALUE_SECURITY_LEVEL_L3.c_str()
: QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str());
RETURN_IF_NULL(usage_table_header, PARAMETER_NULL);
size_t usage_table_header_len = 0;
OEMCryptoResult result;
WithOecWriteLock("ShrinkUsageTableHeader Attempt 1", [&] {
result = OEMCrypto_ShrinkUsageTableHeader(requested_security_level,
new_entry_count, nullptr,
&usage_table_header_len);
metrics_->oemcrypto_shrink_usage_table_header_.Increment(result);
});
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
usage_table_header->resize(usage_table_header_len);
WithOecWriteLock("ShrinkUsageTableHeader Attempt 2", [&] {
result = OEMCrypto_ShrinkUsageTableHeader(
requested_security_level, new_entry_count,
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_len);
metrics_->oemcrypto_shrink_usage_table_header_.Increment(result);
});
}
switch (result) {
case OEMCrypto_SUCCESS:
usage_table_header->resize(usage_table_header_len);
return NO_ERROR;
case OEMCrypto_ERROR_ENTRY_IN_USE:
return SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE;
default:
return MapOEMCryptoResult(result, SHRINK_USAGE_TABLE_HEADER_UNKNOWN_ERROR,
"ShrinkUsageTableHeader");
}
}
CdmResponseType CryptoSession::CreateUsageEntry(uint32_t* entry_number) {
LOGV("Creating usage entry: id = %u", oec_session_id_);
@@ -2447,7 +2505,7 @@ CdmResponseType CryptoSession::CreateUsageEntry(uint32_t* entry_number) {
case OEMCrypto_SUCCESS:
return NO_ERROR;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES_3;
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_SESSION_LOST_STATE:
return SESSION_LOST_STATE_ERROR;
case OEMCrypto_ERROR_SYSTEM_INVALIDATED:
@@ -2483,18 +2541,19 @@ CdmResponseType CryptoSession::LoadUsageEntry(
case OEMCrypto_SUCCESS:
case OEMCrypto_WARNING_GENERATION_SKEW:
return NO_ERROR;
case OEMCrypto_ERROR_INVALID_SESSION:
// This case is special, as it could imply that the provided
// session ID is invalid (CDM internal bug), or that the entry
// being loaded is already in use in a different session.
// It is up to the caller to handle this.
return LOAD_USAGE_ENTRY_INVALID_SESSION;
case OEMCrypto_ERROR_GENERATION_SKEW:
return LOAD_USAGE_ENTRY_GENERATION_SKEW;
case OEMCrypto_ERROR_SIGNATURE_FAILURE:
return LOAD_USAGE_ENTRY_SIGNATURE_FAILURE;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES_3;
case OEMCrypto_ERROR_SESSION_LOST_STATE:
return SESSION_LOST_STATE_ERROR;
case OEMCrypto_ERROR_SYSTEM_INVALIDATED:
return SYSTEM_INVALIDATED_ERROR;
default:
return LOAD_USAGE_ENTRY_UNKNOWN_ERROR;
return MapOEMCryptoResult(result, LOAD_USAGE_ENTRY_UNKNOWN_ERROR,
"LoadUsageEntry");
}
}
@@ -2540,42 +2599,6 @@ CdmResponseType CryptoSession::UpdateUsageEntry(
"UpdateUsageEntry");
}
CdmResponseType CryptoSession::ShrinkUsageTableHeader(
uint32_t new_entry_count, CdmUsageTableHeader* usage_table_header) {
LOGV("Shrinking usage table header: id = %u", oec_session_id_);
RETURN_IF_NULL(usage_table_header, PARAMETER_NULL);
size_t usage_table_header_len = 0;
OEMCryptoResult result;
WithOecWriteLock("ShrinkUsageTableHeader Attempt 1", [&] {
result = OEMCrypto_ShrinkUsageTableHeader(requested_security_level_,
new_entry_count, nullptr,
&usage_table_header_len);
metrics_->oemcrypto_shrink_usage_table_header_.Increment(result);
});
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
usage_table_header->resize(usage_table_header_len);
WithOecWriteLock("ShrinkUsageTableHeader Attempt 2", [&] {
result = OEMCrypto_ShrinkUsageTableHeader(
requested_security_level_, new_entry_count,
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_len);
metrics_->oemcrypto_shrink_usage_table_header_.Increment(result);
});
}
if (result == OEMCrypto_SUCCESS) {
usage_table_header->resize(usage_table_header_len);
}
return MapOEMCryptoResult(result, SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR,
"ShrinkUsageTableHeader");
}
CdmResponseType CryptoSession::MoveUsageEntry(uint32_t new_entry_number) {
LOGV("Moving usage entry: id = %u", oec_session_id_);
@@ -2585,8 +2608,15 @@ CdmResponseType CryptoSession::MoveUsageEntry(uint32_t new_entry_number) {
metrics_->oemcrypto_move_entry_.Increment(result);
});
return MapOEMCryptoResult(result, MOVE_USAGE_ENTRY_UNKNOWN_ERROR,
"MoveUsageEntry");
switch (result) {
case OEMCrypto_ERROR_ENTRY_IN_USE:
LOGW("OEMCrypto_MoveEntry failed: Destination index in use: index = %u",
new_entry_number);
return MOVE_USAGE_ENTRY_DESTINATION_IN_USE;
default:
return MapOEMCryptoResult(result, MOVE_USAGE_ENTRY_UNKNOWN_ERROR,
"MoveUsageEntry");
}
}
bool CryptoSession::GetAnalogOutputCapabilities(bool* can_support_output,

View File

@@ -6,6 +6,7 @@
#include <string.h>
#include <algorithm>
#include <string>
#include "certificate_provisioning.h"
@@ -70,6 +71,7 @@ using video_widevine_client::sdk::
namespace {
const char kAtscCertificateFileName[] = "atsccert.bin";
const char kCertificateFileName[] = "cert.bin";
const char kHlsAttributesFileNameExt[] = ".hal";
const char kUsageInfoFileNamePrefix[] = "usage";
@@ -126,19 +128,25 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(GetCertificateFileName(), serialized_file) ==
return StoreFileWithHash(GetCertificateFileName(false), serialized_file) ==
kNoError;
}
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
bool DeviceFiles::RetrieveCertificate(bool atsc_mode_enabled,
std::string* certificate,
std::string* wrapped_private_key,
std::string* serial_number,
uint32_t* system_id) {
RETURN_FALSE_IF_UNINITIALIZED();
if (!HasCertificate(atsc_mode_enabled)) {
return false;
}
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(GetCertificateFileName(), &file) != kNoError) {
LOGE("Unable to retrieve certificate file");
if (RetrieveHashedFile(GetCertificateFileName(atsc_mode_enabled), &file) !=
kNoError) {
LOGW("Unable to retrieve certificate file");
return false;
}
@@ -166,14 +174,16 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
device_certificate.certificate(), serial_number, system_id);
}
bool DeviceFiles::HasCertificate() {
bool DeviceFiles::HasCertificate(bool atsc_mode_enabled) {
RETURN_FALSE_IF_UNINITIALIZED();
return FileExists(GetCertificateFileName());
return FileExists(GetCertificateFileName(atsc_mode_enabled));
}
bool DeviceFiles::RemoveCertificate() {
RETURN_FALSE_IF_UNINITIALIZED()
return RemoveFile(GetCertificateFileName());
return RemoveFile(GetCertificateFileName(false));
}
bool DeviceFiles::StoreLicense(const CdmLicenseData& license_data,
@@ -1103,7 +1113,7 @@ DeviceFiles::ResponseType DeviceFiles::RetrieveHashedFile(
path += name;
if (!file_system_->Exists(path)) {
LOGE("File does not exist: path = %s", path.c_str());
LOGW("File does not exist: path = %s", path.c_str());
return kFileNotFound;
}
@@ -1214,8 +1224,8 @@ ssize_t DeviceFiles::GetFileSize(const std::string& name) {
return file_system_->FileSize(path);
}
std::string DeviceFiles::GetCertificateFileName() {
return kCertificateFileName;
std::string DeviceFiles::GetCertificateFileName(bool atsc_mode_enabled) {
return atsc_mode_enabled ? kAtscCertificateFileName : kCertificateFileName;
}
std::string DeviceFiles::GetUsageTableFileName() { return kUsageTableFileName; }

View File

@@ -237,6 +237,7 @@ bool CdmLicense::Init(const std::string& client_token,
crypto_session_ = session;
policy_engine_ = policy_engine;
use_privacy_mode_ = use_privacy_mode;
license_nonce_ = 0;
initialized_ = true;
return true;
}
@@ -313,8 +314,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
uint32_t nonce;
status = crypto_session_->GenerateNonce(&nonce);
status = crypto_session_->GenerateNonce(&license_nonce_);
switch (status) {
case NO_ERROR:
@@ -325,9 +325,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
default:
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
license_request.set_key_control_nonce(nonce);
LOGD("nonce = %u", nonce);
license_request.set_key_control_nonce(license_nonce_);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
@@ -462,11 +460,11 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
LOGW("Unknown API Version");
api_version = 15;
}
uint32_t nonce = 0;
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(&nonce);
const CdmResponseType status =
crypto_session_->GenerateNonce(&license_nonce_);
switch (status) {
case NO_ERROR:
break;
@@ -477,8 +475,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
return LICENSE_RENEWAL_NONCE_GENERATION_ERROR;
}
}
license_request.set_key_control_nonce(nonce);
LOGD("nonce = %u", nonce);
license_request.set_key_control_nonce(license_nonce_);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
@@ -705,12 +702,13 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
return INVALID_LICENSE_TYPE;
}
// At this point of the license life-cycle (handling a renewal or
// release), we should already know if the license is v15 or not.
// If license is v16, then there should be a |core_message|
// present; otherwise there might have beeen some tampering with the
// request or response.
if (supports_core_messages() &&
// 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())) {
LOGE("Renewal response is missing |core_message| field");
@@ -723,8 +721,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
const std::string& signed_message = signed_response.msg();
const std::string core_message =
supports_core_messages() ? signed_response.oemcrypto_core_message()
: std::string();
signed_response.has_oemcrypto_core_message()
? signed_response.oemcrypto_core_message()
: std::string();
const std::string& signature = signed_response.signature();
License license;
@@ -811,6 +810,13 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
}
key_request_ = signed_request.msg();
LicenseRequest original_license_request;
if (!original_license_request.ParseFromString(key_request_)) {
LOGW("Could not parse original request.");
} else {
license_nonce_ = original_license_request.key_control_nonce();
}
CdmResponseType sts = HandleKeyResponse(license_response);
if (sts != KEY_ADDED) return sts;

File diff suppressed because it is too large Load Diff