OEMCrypto v16.1
Merge of http://go/wvgerrit/93404 This CL updates the Widevine CDM to support OEMCrypto v16.1 Test: Tested in 16.2 CL Bug: 141247171 Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
This commit is contained in:
@@ -27,7 +27,6 @@
|
||||
namespace {
|
||||
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
|
||||
wvcdm::CdmOfflineLicenseState MapDeviceFilesLicenseState(
|
||||
wvcdm::DeviceFiles::LicenseState state) {
|
||||
@@ -571,7 +570,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
return NO_ERROR;
|
||||
} else if (query_token == QUERY_KEY_USAGE_SUPPORT) {
|
||||
bool supports_usage_reporting;
|
||||
bool got_info = crypto_session->UsageInformationSupport(
|
||||
const bool got_info = crypto_session->UsageInformationSupport(
|
||||
security_level, &supports_usage_reporting);
|
||||
|
||||
if (!got_info) {
|
||||
@@ -698,6 +697,24 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
break;
|
||||
}
|
||||
return NO_ERROR;
|
||||
} else if (query_token == QUERY_KEY_MAX_USAGE_TABLE_ENTRIES) {
|
||||
size_t max_number_of_usage_entries;
|
||||
if (!crypto_session->GetMaximumUsageTableEntries(
|
||||
security_level, &max_number_of_usage_entries)) {
|
||||
LOGW("GetMaxUsageTableEntries failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
*query_response = std::to_string(max_number_of_usage_entries);
|
||||
} else if (query_token == QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION) {
|
||||
uint32_t api_minor_version;
|
||||
if (!crypto_session->GetApiMinorVersion(security_level,
|
||||
&api_minor_version)) {
|
||||
LOGW("GetApiMinorVersion failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
*query_response = std::to_string(api_minor_version);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
M_TIME(status = crypto_session->Open(security_level),
|
||||
@@ -804,9 +821,6 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
|
||||
CdmKeyAllowedUsage* key_usage) {
|
||||
LOGI("Querying allowed key useage (all sessions): key_id = %s",
|
||||
key_id.c_str());
|
||||
CdmResponseType sts;
|
||||
CdmKeyAllowedUsage found_in_this_session;
|
||||
bool found = false;
|
||||
if (!key_usage) {
|
||||
LOGE("No response destination");
|
||||
return PARAMETER_NULL;
|
||||
@@ -816,9 +830,12 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
bool found = false;
|
||||
for (CdmSessionList::iterator iter = sessions.begin(); iter != sessions.end();
|
||||
++iter) {
|
||||
sts = (*iter)->QueryKeyAllowedUsage(key_id, &found_in_this_session);
|
||||
CdmKeyAllowedUsage found_in_this_session;
|
||||
CdmResponseType sts =
|
||||
(*iter)->QueryKeyAllowedUsage(key_id, &found_in_this_session);
|
||||
if (sts == NO_ERROR) {
|
||||
if (found) {
|
||||
// Found another key. If usage settings do not match, fail.
|
||||
@@ -890,8 +907,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
return INVALID_PROVISIONING_REQUEST_PARAM_2;
|
||||
}
|
||||
|
||||
DeleteAllUsageReportsUponFactoryReset();
|
||||
|
||||
// TODO(b/141705730): Remove usage entries on provisioning.
|
||||
if (!cert_provisioning_) {
|
||||
cert_provisioning_.reset(
|
||||
new CertificateProvisioning(metrics_->GetCryptoMetrics()));
|
||||
@@ -1004,6 +1020,8 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
return UNPROVISION_ERROR_1;
|
||||
}
|
||||
|
||||
// TODO(b/141705730): Remove usage entries during unprovisioning.
|
||||
|
||||
if (!file_system_->IsGlobal()) {
|
||||
if (!handle.RemoveCertificate()) {
|
||||
LOGE("Unable to delete certificate");
|
||||
@@ -1015,34 +1033,10 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
LOGE("Unable to delete files");
|
||||
return UNPROVISION_ERROR_3;
|
||||
}
|
||||
return DeleteUsageTable(security_level);
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) {
|
||||
LOGI("Deleting usage table: security_level = %d",
|
||||
static_cast<int>(security_level));
|
||||
std::unique_ptr<CryptoSession> crypto_session(
|
||||
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
|
||||
CdmResponseType status;
|
||||
M_TIME(status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault),
|
||||
metrics_->GetCryptoMetrics(), crypto_session_open_, status,
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("Error opening crypto session: status = %d", static_cast<int>(status));
|
||||
return UNPROVISION_ERROR_4;
|
||||
}
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
metrics_->GetCryptoMetrics()
|
||||
->crypto_session_delete_all_usage_reports_.Increment(status);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Error deleteing usage reports: status = %d",
|
||||
static_cast<int>(status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ListStoredLicenses(
|
||||
CdmSecurityLevel security_level, std::vector<std::string>* key_set_ids) {
|
||||
DeviceFiles handle(file_system_);
|
||||
@@ -1195,9 +1189,6 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
return GET_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
|
||||
CdmKeyMessage license_request;
|
||||
CdmKeyResponse license_response;
|
||||
std::string usage_entry;
|
||||
DeviceFiles::CdmUsageData usage_data;
|
||||
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), ssid,
|
||||
&usage_data)) {
|
||||
@@ -1206,7 +1197,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
|
||||
status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("Session init error");
|
||||
LOGE("Session init error: status = %d", static_cast<int>(status));
|
||||
return status;
|
||||
}
|
||||
if (!handle.Reset(usage_session_->GetSecurityLevel())) {
|
||||
@@ -1280,6 +1271,11 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
CdmUsageInfo* usage_info) {
|
||||
LOGI("Getting usage info: app_id = %s, security_level = %d", app_id.c_str(),
|
||||
static_cast<int>(requested_security_level));
|
||||
if (!usage_info) {
|
||||
LOGE("No usage info destination");
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
|
||||
if (!usage_property_set_) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
@@ -1307,20 +1303,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
return GET_USAGE_INFO_ERROR_4;
|
||||
}
|
||||
|
||||
if (!usage_info) {
|
||||
LOGE("No usage info destination");
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
if (0 == usage_data.size()) {
|
||||
usage_info->resize(0);
|
||||
if (usage_data.empty()) {
|
||||
usage_info->clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
usage_info->resize(kUsageReportsPerRequest);
|
||||
|
||||
size_t index = CdmRandom::RandomInRange(usage_data.size() - 1);
|
||||
const size_t index = CdmRandom::RandomInRange(usage_data.size() - 1);
|
||||
status = usage_session_->RestoreUsageSession(usage_data[index], error_detail);
|
||||
if (KEY_ADDED != status) {
|
||||
// TODO(b/141704872): Make multiple attempts.
|
||||
LOGE("RestoreUsageSession failed: index = %zu, status = %d", index,
|
||||
static_cast<int>(status));
|
||||
usage_info->clear();
|
||||
@@ -1367,64 +1358,42 @@ CdmResponseType CdmEngine::RemoveAllUsageInfo(
|
||||
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
|
||||
usage_session_->Init(usage_property_set_.get());
|
||||
|
||||
switch (usage_session_->get_usage_support_type()) {
|
||||
case kUsageEntrySupport: {
|
||||
std::vector<DeviceFiles::CdmUsageData> usage_data;
|
||||
// Retrieve all usage information but delete only one before
|
||||
// refetching. This is because deleting the usage entry
|
||||
// might cause other entries to be shifted and information updated.
|
||||
do {
|
||||
if (!handle.RetrieveUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), &usage_data)) {
|
||||
LOGW("Failed to retrieve usage info");
|
||||
break;
|
||||
}
|
||||
|
||||
if (usage_data.empty()) break;
|
||||
|
||||
CdmResponseType res = usage_session_->DeleteUsageEntry(
|
||||
usage_data[0].usage_entry_number);
|
||||
|
||||
if (res != NO_ERROR) {
|
||||
LOGW("Failed to delete usage entry: status = %d",
|
||||
static_cast<int>(res));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
usage_data[0].provider_session_token)) {
|
||||
LOGW("Failed to delete usage info");
|
||||
break;
|
||||
}
|
||||
} while (!usage_data.empty());
|
||||
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&provider_session_tokens)) {
|
||||
status = REMOVE_ALL_USAGE_INFO_ERROR_5;
|
||||
if (usage_session_->get_usage_support_type() == kUsageEntrySupport) {
|
||||
std::vector<DeviceFiles::CdmUsageData> usage_data;
|
||||
// Retrieve all usage information but delete only one before
|
||||
// refetching. This is because deleting the usage entry
|
||||
// might cause other entries to be shifted and information updated.
|
||||
do {
|
||||
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&usage_data)) {
|
||||
LOGW("Failed to retrieve usage info");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kUsageTableSupport: {
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&provider_session_tokens)) {
|
||||
LOGE("Failed to delete secure stops: cdm_security_level = %d",
|
||||
static_cast<int>(cdm_security_level));
|
||||
status = REMOVE_ALL_USAGE_INFO_ERROR_1;
|
||||
} else {
|
||||
CdmResponseType status2 =
|
||||
usage_session_->DeleteMultipleUsageInformation(
|
||||
provider_session_tokens);
|
||||
if (status2 != NO_ERROR) status = status2;
|
||||
|
||||
if (usage_data.empty()) break;
|
||||
|
||||
CdmResponseType res =
|
||||
usage_session_->DeleteUsageEntry(usage_data[0].usage_entry_number);
|
||||
|
||||
if (res != NO_ERROR) {
|
||||
LOGW("Failed to delete usage entry: status = %d",
|
||||
static_cast<int>(res));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
usage_data[0].provider_session_token)) {
|
||||
LOGW("Failed to delete usage info");
|
||||
break;
|
||||
}
|
||||
} while (!usage_data.empty());
|
||||
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&provider_session_tokens)) {
|
||||
status = REMOVE_ALL_USAGE_INFO_ERROR_5;
|
||||
}
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
usage_session_.reset();
|
||||
@@ -1479,38 +1448,14 @@ CdmResponseType CdmEngine::RemoveUsageInfo(
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (usage_session_->get_usage_support_type()) {
|
||||
case kUsageEntrySupport: {
|
||||
status = usage_session_->DeleteUsageEntry(usage_entry_number);
|
||||
|
||||
if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
provider_session_token)) {
|
||||
status = REMOVE_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
usage_session_.reset();
|
||||
return status;
|
||||
if (usage_session_->get_usage_support_type() == kUsageEntrySupport) {
|
||||
status = usage_session_->DeleteUsageEntry(usage_entry_number);
|
||||
if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
provider_session_token)) {
|
||||
status = REMOVE_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
case kUsageTableSupport: {
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
provider_session_token);
|
||||
std::unique_ptr<CryptoSession> crypto_session(
|
||||
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
|
||||
status = crypto_session->Open(static_cast<CdmSecurityLevel>(j) ==
|
||||
kSecurityLevelL3
|
||||
? kLevel3
|
||||
: kLevelDefault);
|
||||
if (status == NO_ERROR) {
|
||||
crypto_session->UpdateUsageInformation();
|
||||
status =
|
||||
crypto_session->DeleteUsageInformation(provider_session_token);
|
||||
crypto_session->UpdateUsageInformation();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
usage_session_.reset();
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
LOGE("Failed to initialize L%d device files", j);
|
||||
@@ -1530,6 +1475,7 @@ CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
}
|
||||
|
||||
CdmResponseType status = usage_session_->ReleaseKey(message);
|
||||
// Q: Should this only be reset if release key was successful?
|
||||
usage_session_.reset();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("ReleaseKey failed: status = %d", status);
|
||||
@@ -1908,36 +1854,15 @@ void CdmEngine::OnTimerEvent() {
|
||||
|
||||
if (is_usage_update_needed &&
|
||||
(usage_update_period_expired || is_initial_usage_update)) {
|
||||
bool has_usage_been_updated = false;
|
||||
|
||||
// Session list may have changed. Rebuild.
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
(*iter)->reset_usage_flags();
|
||||
switch ((*iter)->get_usage_support_type()) {
|
||||
case kUsageEntrySupport:
|
||||
if ((*iter)->has_provider_session_token()) {
|
||||
(*iter)->UpdateUsageEntryInformation();
|
||||
}
|
||||
break;
|
||||
case kUsageTableSupport:
|
||||
if (!has_usage_been_updated) {
|
||||
// usage is updated for all sessions so this needs to be
|
||||
// called only once per update usage information period
|
||||
CdmResponseType status = (*iter)->UpdateUsageTableInformation();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("UpdateUsageTableInformation failed: status = %d",
|
||||
static_cast<int>(status));
|
||||
} else {
|
||||
has_usage_been_updated = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
if ((*iter)->get_usage_support_type() == kUsageEntrySupport &&
|
||||
(*iter)->has_provider_session_token()) {
|
||||
(*iter)->UpdateUsageEntryInformation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2004,38 +1929,4 @@ void CdmEngine::CloseExpiredReleaseSessions() {
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
|
||||
std::string device_base_path_level1 = "";
|
||||
std::string device_base_path_level3 = "";
|
||||
Properties::GetDeviceFilesBasePath(kSecurityLevelL1,
|
||||
&device_base_path_level1);
|
||||
Properties::GetDeviceFilesBasePath(kSecurityLevelL3,
|
||||
&device_base_path_level3);
|
||||
|
||||
if (!file_system_->Exists(device_base_path_level1) &&
|
||||
!file_system_->Exists(device_base_path_level3)) {
|
||||
std::unique_ptr<CryptoSession> crypto_session(
|
||||
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
|
||||
CdmResponseType status;
|
||||
M_TIME(status = crypto_session->Open(
|
||||
cert_provisioning_requested_security_level_),
|
||||
metrics_->GetCryptoMetrics(), crypto_session_open_, status,
|
||||
cert_provisioning_requested_security_level_);
|
||||
if (NO_ERROR == status) {
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
metrics_->GetCryptoMetrics()
|
||||
->crypto_session_delete_all_usage_reports_.Increment(status);
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("Failed to delete usage reports: status = %d",
|
||||
static_cast<int>(status));
|
||||
}
|
||||
} else {
|
||||
LOGW(
|
||||
"Failed to open crypto session: status = %d\n"
|
||||
"Usage reports are not removed after factory reset",
|
||||
static_cast<int>(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -894,18 +894,11 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
usage_entry_number_)) {
|
||||
LOGE("Unable to store usage info");
|
||||
// Usage info file is corrupt. Delete current usage entry and file.
|
||||
switch (usage_support_type_) {
|
||||
case kUsageEntrySupport:
|
||||
DeleteUsageEntry(usage_entry_number_);
|
||||
break;
|
||||
case kUsageTableSupport:
|
||||
crypto_session_->DeleteUsageInformation(provider_session_token);
|
||||
crypto_session_->UpdateUsageInformation();
|
||||
break;
|
||||
default:
|
||||
LOGW("Unexpected usage support type: %d",
|
||||
static_cast<int>(usage_support_type_));
|
||||
break;
|
||||
if (usage_support_type_ == kUsageEntrySupport) {
|
||||
DeleteUsageEntry(usage_entry_number_);
|
||||
} else {
|
||||
LOGW("Unexpected usage support type: %d",
|
||||
static_cast<int>(usage_support_type_));
|
||||
}
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
file_handle_->DeleteAllUsageInfoForApp(
|
||||
@@ -1006,41 +999,6 @@ void CdmSession::GetApplicationId(std::string* app_id) {
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
CdmUsageSupportType usage_support_type;
|
||||
CdmResponseType sts =
|
||||
crypto_session_->GetUsageSupportType(&usage_support_type);
|
||||
if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) {
|
||||
for (size_t i = 0; i < provider_session_tokens.size(); ++i) {
|
||||
crypto_session_->DeactivateUsageInformation(provider_session_tokens[i]);
|
||||
UpdateUsageTableInformation();
|
||||
}
|
||||
}
|
||||
|
||||
if (sts == NO_ERROR) {
|
||||
sts = crypto_session_->DeleteMultipleUsageInformation(
|
||||
provider_session_tokens);
|
||||
crypto_metrics_->crypto_session_delete_multiple_usage_information_
|
||||
.Increment(sts);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageTableInformation() {
|
||||
CdmUsageSupportType usage_support_type;
|
||||
CdmResponseType sts =
|
||||
crypto_session_->GetUsageSupportType(&usage_support_type);
|
||||
|
||||
if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) {
|
||||
M_TIME(sts = crypto_session_->UpdateUsageInformation(), crypto_metrics_,
|
||||
crypto_session_update_usage_information_, sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
return NO_ERROR; // Ignore
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
|
||||
if (usage_support_type_ != kUsageEntrySupport ||
|
||||
!has_provider_session_token() || usage_table_header_ == nullptr) {
|
||||
|
||||
@@ -174,9 +174,9 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter(
|
||||
SignedProvisioningMessage::ProtocolVersion
|
||||
CertificateProvisioning::GetProtocolVersion() {
|
||||
if (crypto_session_->GetPreProvisionTokenType() == kClientTokenOemCert)
|
||||
return SignedProvisioningMessage::VERSION_3;
|
||||
return SignedProvisioningMessage::PROVISIONING_30;
|
||||
else
|
||||
return SignedProvisioningMessage::VERSION_2;
|
||||
return SignedProvisioningMessage::PROVISIONING_20;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -191,8 +191,9 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
const std::string& cert_authority, const std::string& origin,
|
||||
const std::string& spoid, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!default_url) {
|
||||
LOGE("Output parameter |default_url| is not provided");
|
||||
if (!request || !default_url) {
|
||||
LOGE("Output parameter |%s| is not provided",
|
||||
request ? "default_url" : "request");
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_1;
|
||||
}
|
||||
|
||||
@@ -212,11 +213,10 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
status = id.Init(crypto_session_.get());
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
video_widevine::ClientIdentification* client_id =
|
||||
provisioning_request.mutable_client_id();
|
||||
video_widevine::ClientIdentification client_id;
|
||||
|
||||
CdmAppParameterMap app_parameter;
|
||||
status = id.Prepare(app_parameter, kEmptyString, client_id);
|
||||
status = id.Prepare(app_parameter, kEmptyString, &client_id);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
if (!service_certificate_->has_certificate()) {
|
||||
@@ -228,8 +228,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
provisioning_request.mutable_encrypted_client_id();
|
||||
status = service_certificate_->EncryptClientId(
|
||||
crypto_session_.get(), client_id, encrypted_client_id);
|
||||
provisioning_request.clear_client_id();
|
||||
crypto_session_.get(), &client_id, encrypted_client_id);
|
||||
|
||||
uint32_t nonce;
|
||||
status = crypto_session_->GenerateNonce(&nonce);
|
||||
@@ -271,9 +270,10 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
provisioning_request.SerializeToString(&serialized_message);
|
||||
|
||||
// Derives signing and encryption keys and constructs signature.
|
||||
std::string core_message;
|
||||
std::string request_signature;
|
||||
status = crypto_session_->PrepareRequest(serialized_message, true,
|
||||
&request_signature);
|
||||
status = crypto_session_->PrepareAndSignProvisioningRequest(
|
||||
serialized_message, &core_message, &request_signature);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to prepare provisioning request: status = %d",
|
||||
@@ -290,6 +290,12 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
signed_provisioning_msg.set_message(serialized_message);
|
||||
signed_provisioning_msg.set_signature(request_signature);
|
||||
signed_provisioning_msg.set_protocol_version(GetProtocolVersion());
|
||||
if (core_message.empty()) {
|
||||
// OEMCrypto does not support core messages.
|
||||
supports_core_messages_ = false;
|
||||
} else {
|
||||
signed_provisioning_msg.set_oemcrypto_core_message(core_message);
|
||||
}
|
||||
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
@@ -298,9 +304,9 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
// Return request as web-safe base64 string
|
||||
std::vector<uint8_t> request_vector(serialized_request.begin(),
|
||||
serialized_request.end());
|
||||
request->assign(Base64SafeEncodeNoPad(request_vector));
|
||||
*request = Base64SafeEncodeNoPad(request_vector);
|
||||
} else {
|
||||
request->swap(serialized_request);
|
||||
*request = std::move(serialized_request);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -353,66 +359,59 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (supports_core_messages() &&
|
||||
!signed_response.has_oemcrypto_core_message()) {
|
||||
LOGE("Signed response does not have core message");
|
||||
error = true;
|
||||
} else if (!supports_core_messages() &&
|
||||
signed_response.has_oemcrypto_core_message()) {
|
||||
const std::string& core_message = signed_response.oemcrypto_core_message();
|
||||
// This case should not occur. However, the CDM will let OEMCrypto
|
||||
// fail.
|
||||
LOGW(
|
||||
"Received unexpected core message in provisioning request: "
|
||||
"core_message_size = %zu",
|
||||
core_message.size());
|
||||
}
|
||||
|
||||
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
const std::string& signature = signed_response.signature();
|
||||
ProvisioningResponse provisioning_response;
|
||||
const std::string core_message =
|
||||
supports_core_messages() ? signed_response.oemcrypto_core_message()
|
||||
: std::string();
|
||||
|
||||
ProvisioningResponse provisioning_response;
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("Failed to parse provisioning response");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_4;
|
||||
}
|
||||
|
||||
if (!provisioning_response.has_device_rsa_key()) {
|
||||
LOGE("Provisioning response does not have RSA key");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_5;
|
||||
}
|
||||
|
||||
// If Provisioning 3.0 (OEM Cert provisioned), verify that the
|
||||
// message is properly signed.
|
||||
if (crypto_session_->GetPreProvisionTokenType() == kClientTokenOemCert) {
|
||||
if (service_certificate_->VerifySignedMessage(signed_message, signature) !=
|
||||
NO_ERROR) {
|
||||
// TODO(b/69562876): if the cert is bad, request a new one.
|
||||
LOGE("Provisioning response message not properly signed");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& new_private_key = provisioning_response.device_rsa_key();
|
||||
const std::string& nonce = provisioning_response.nonce();
|
||||
const std::string& iv = provisioning_response.device_rsa_key_iv();
|
||||
|
||||
const std::string& wrapping_key = (provisioning_response.has_wrapping_key())
|
||||
? provisioning_response.wrapping_key()
|
||||
: std::string();
|
||||
|
||||
std::string wrapped_private_key;
|
||||
|
||||
CdmResponseType status = crypto_session_->RewrapCertificate(
|
||||
signed_message, signature, nonce, new_private_key, iv, wrapping_key,
|
||||
&wrapped_private_key);
|
||||
const CdmResponseType status = crypto_session_->LoadProvisioning(
|
||||
signed_message, core_message, signature, &wrapped_private_key);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("RewrapCertificate failed: status = %d", static_cast<int>(status));
|
||||
LOGE("LoadProvisioning failed: status = %d", static_cast<int>(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
|
||||
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
|
||||
crypto_session_->Close();
|
||||
|
||||
// This is the entire certificate (SignedDrmDeviceCertificate).
|
||||
const std::string& device_certificate =
|
||||
provisioning_response.device_certificate();
|
||||
|
||||
if (cert_type_ == kCertificateX509) {
|
||||
*cert = provisioning_response.device_certificate();
|
||||
*cert = device_certificate;
|
||||
*wrapped_key = wrapped_private_key;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// This is the entire certificate (SignedDrmDeviceCertificate).
|
||||
// This will be stored to the device as the final step in the device
|
||||
// provisioning process.
|
||||
const std::string& device_certificate =
|
||||
provisioning_response.device_certificate();
|
||||
// The certificate will be stored to the device as the final step in
|
||||
// the device provisioning process.
|
||||
|
||||
DeviceFiles handle(file_system);
|
||||
if (!handle.Init(security_level)) {
|
||||
|
||||
@@ -109,6 +109,8 @@ OEMCryptoResult ContentKeySession::Decrypt(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor,
|
||||
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
#if 0 // TODO(b/135285640): fix this.
|
||||
OEMCryptoResult sts;
|
||||
M_TIME(sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
@@ -116,6 +118,7 @@ OEMCryptoResult ContentKeySession::Decrypt(
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
|
||||
metrics_, oemcrypto_decrypt_cenc_, sts,
|
||||
metrics::Pow2Bucket(params.encrypt_length));
|
||||
#endif
|
||||
return sts;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,6 +89,11 @@ InitializationData::InitializationData(const std::string& type,
|
||||
}
|
||||
}
|
||||
|
||||
const std::string InitializationData::WidevineSystemID() {
|
||||
return std::string(reinterpret_cast<const char*>(kWidevineSystemId),
|
||||
sizeof(kWidevineSystemId));
|
||||
}
|
||||
|
||||
std::vector<video_widevine::WidevinePsshData_EntitledKey>
|
||||
InitializationData::ExtractWrappedKeys() const {
|
||||
std::vector<video_widevine::WidevinePsshData_EntitledKey> keys;
|
||||
@@ -671,4 +676,64 @@ bool InitializationData::DetectEntitlementPreference(
|
||||
return std::stoul(oec_version_string) >= 14;
|
||||
}
|
||||
|
||||
void InitializationData::DumpToLogs() const {
|
||||
if (!is_supported()) {
|
||||
LOGD("InitData: Not supported");
|
||||
}
|
||||
if (!IsEmpty()) {
|
||||
LOGD("InitData: Empty");
|
||||
}
|
||||
std::string type_info = type();
|
||||
if (is_cenc()) type_info += " (cenc)";
|
||||
if (is_hls()) type_info += " (hls)";
|
||||
if (is_webm()) type_info += " (webm)";
|
||||
if (is_audio()) type_info += " (audio)";
|
||||
LOGD("InitData: type = %s", type_info.c_str());
|
||||
|
||||
video_widevine::WidevinePsshData pssh;
|
||||
if (!pssh.ParseFromString(data())) {
|
||||
LOGD("InitData: invalid pssh: %s", b2a_hex(data()).c_str());
|
||||
return;
|
||||
}
|
||||
if (pssh.has_content_id()) {
|
||||
LOGD("InitData: content_id = '%s'", pssh.content_id().c_str());
|
||||
}
|
||||
if (pssh.has_protection_scheme()) {
|
||||
uint32_t scheme = pssh.protection_scheme();
|
||||
LOGD("InitData: Protection Scheme: %c%c%c%c", (scheme >> 24) & 0xFF,
|
||||
(scheme >> 16) & 0xFF, (scheme >> 8) & 0xFF, (scheme >> 0) & 0xFF);
|
||||
}
|
||||
switch (pssh.type()) {
|
||||
case video_widevine::WidevinePsshData_Type_SINGLE:
|
||||
// Don't bother printing.
|
||||
break;
|
||||
case video_widevine::WidevinePsshData_Type_ENTITLEMENT:
|
||||
LOGD("InitData: Entitlement License");
|
||||
break;
|
||||
case video_widevine::WidevinePsshData_Type_ENTITLED_KEY:
|
||||
LOGD("InitData: Entitled Key");
|
||||
break;
|
||||
default:
|
||||
LOGE("Undefine pssh type: %d", pssh.type());
|
||||
break;
|
||||
}
|
||||
if (pssh.has_crypto_period_index())
|
||||
LOGD("InitData: Crypto Period Index %u", pssh.crypto_period_index());
|
||||
if (pssh.has_crypto_period_seconds())
|
||||
LOGD("InitData: Crypto Period seconds %u", pssh.crypto_period_seconds());
|
||||
if (pssh.has_key_sequence())
|
||||
LOGD("InitData: Key Sequence %u", pssh.key_sequence());
|
||||
|
||||
for (int i = 0; i < pssh.key_ids_size(); i++) {
|
||||
LOGD("InitData: key_id %d: %s", i, b2a_hex(pssh.key_ids(i)).c_str());
|
||||
}
|
||||
|
||||
for (int i = 0; i < pssh.entitled_keys_size(); i++) {
|
||||
video_widevine::WidevinePsshData_EntitledKey key = pssh.entitled_keys(i);
|
||||
LOGD("InitData: entitlement_key_id %d: %s -> %s", i,
|
||||
b2a_hex(key.entitlement_key_id()).c_str(),
|
||||
b2a_hex(key.key_id()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -53,7 +53,8 @@ using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated;
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
static std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
|
||||
namespace {
|
||||
std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
|
||||
std::vector<CryptoKey> key_array;
|
||||
|
||||
for (int i = 0; i < license.key_size(); ++i) {
|
||||
@@ -108,7 +109,7 @@ static std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
|
||||
return key_array;
|
||||
}
|
||||
|
||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
std::vector<CryptoKey> key_array;
|
||||
|
||||
// Extract content key(s)
|
||||
@@ -174,6 +175,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
|
||||
return key_array;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CdmLicense::CdmLicense(const CdmSessionId& session_id)
|
||||
: crypto_session_(nullptr),
|
||||
@@ -182,6 +184,7 @@ 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 Clock()),
|
||||
license_key_type_(kLicenseKeyTypeContent) {}
|
||||
@@ -193,6 +196,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, 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);
|
||||
@@ -336,10 +340,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
|
||||
key_request_ = serialized_license_req;
|
||||
|
||||
// Derive signing and encryption keys and construct signature.
|
||||
// Derive signing and encryption keys and construct core message and
|
||||
// signature.
|
||||
std::string core_message;
|
||||
std::string license_request_signature;
|
||||
status = crypto_session_->PrepareRequest(serialized_license_req, false,
|
||||
&license_request_signature);
|
||||
status = crypto_session_->PrepareAndSignLicenseRequest(
|
||||
serialized_license_req, &core_message, &license_request_signature);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
signed_request->clear();
|
||||
@@ -357,6 +363,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);
|
||||
signed_message.set_oemcrypto_core_message(core_message);
|
||||
|
||||
signed_message.SerializeToString(signed_request);
|
||||
|
||||
@@ -474,10 +481,11 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
std::string serialized_license_req;
|
||||
license_request.SerializeToString(&serialized_license_req);
|
||||
|
||||
// Construct signature.
|
||||
// Construct signature and core message.
|
||||
std::string core_message;
|
||||
std::string license_request_signature;
|
||||
status = crypto_session_->PrepareRenewalRequest(serialized_license_req,
|
||||
&license_request_signature);
|
||||
status = crypto_session_->PrepareAndSignRenewalRequest(
|
||||
serialized_license_req, &core_message, &license_request_signature);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
if (license_request_signature.empty()) {
|
||||
@@ -490,6 +498,11 @@ 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.SerializeToString(signed_request);
|
||||
*server_url = server_url_;
|
||||
@@ -543,8 +556,23 @@ 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 respones does not include a core message
|
||||
// either.
|
||||
if (!signed_response.has_oemcrypto_core_message()) {
|
||||
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& signature = signed_response.signature();
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
if (!license.ParseFromString(signed_message)) {
|
||||
LOGE("Unable to parse license response");
|
||||
return LICENSE_RESPONSE_PARSE_ERROR_1;
|
||||
}
|
||||
@@ -581,6 +609,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
}
|
||||
}
|
||||
|
||||
// A license may contain several types of keys (content, entitlement,
|
||||
// signing, key control and operator sessions); however, it should not
|
||||
// contain both entitlement keys and content keys. To determine the
|
||||
// overall type of the license, we check for the existence of either
|
||||
// type of keys. If both are present, we default to entitlement keys.
|
||||
CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement;
|
||||
std::vector<CryptoKey> key_array = ExtractEntitlementKeys(license);
|
||||
if (key_array.empty()) {
|
||||
@@ -629,13 +662,12 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
|
||||
CdmResponseType resp = NO_CONTENT_KEY;
|
||||
if (kLicenseKeyTypeEntitlement == key_type) {
|
||||
resp = HandleEntitlementKeyResponse(signed_response.msg(),
|
||||
signed_response.signature(), mac_key_iv,
|
||||
mac_keys, key_array, license);
|
||||
resp =
|
||||
HandleEntitlementKeyResponse(signed_message, core_message, signature,
|
||||
mac_key_iv, mac_keys, key_array, license);
|
||||
} else if (kLicenseKeyTypeContent == key_type) {
|
||||
resp = HandleContentKeyResponse(signed_response.msg(),
|
||||
signed_response.signature(), mac_key_iv,
|
||||
mac_keys, key_array, license);
|
||||
resp = HandleContentKeyResponse(signed_message, core_message, signature,
|
||||
mac_key_iv, mac_keys, key_array, license);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
@@ -668,13 +700,29 @@ 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() &&
|
||||
!signed_response.has_oemcrypto_core_message()) {
|
||||
LOGE("Renewal response is missing |core_message| field");
|
||||
return CORE_MESSAGE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature()) {
|
||||
LOGE("Update key response is missing signature");
|
||||
return SIGNATURE_NOT_FOUND;
|
||||
}
|
||||
const std::string& signed_message = signed_response.msg();
|
||||
const std::string core_message =
|
||||
supports_core_messages() ? signed_response.oemcrypto_core_message()
|
||||
: std::string();
|
||||
const std::string& signature = signed_response.signature();
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
if (!license.ParseFromString(signed_message)) {
|
||||
LOGE("Unable to parse license from signed message");
|
||||
return LICENSE_RESPONSE_PARSE_ERROR_3;
|
||||
}
|
||||
@@ -690,12 +738,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
|
||||
if (!is_renewal) {
|
||||
if (!license.id().has_provider_session_token()) return KEY_ADDED;
|
||||
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
CdmResponseType status = crypto_session_->ReleaseUsageInformation(
|
||||
signed_response.msg(), signed_response.signature(),
|
||||
provider_session_token_);
|
||||
return (NO_ERROR == status) ? KEY_ADDED : status;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
@@ -703,11 +747,14 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
|
||||
CdmResponseType status = crypto_session_->RefreshKeys(
|
||||
signed_response.msg(), signed_response.signature(), key_array.size(),
|
||||
&key_array[0]);
|
||||
CdmResponseType 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);
|
||||
}
|
||||
|
||||
if (status == KEY_ADDED) {
|
||||
policy_engine_->UpdateLicense(license);
|
||||
@@ -750,6 +797,12 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
|
||||
return INVALID_LICENSE_REQUEST_TYPE_1;
|
||||
}
|
||||
|
||||
if (!signed_request.has_oemcrypto_core_message()) {
|
||||
// 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();
|
||||
CdmResponseType sts = HandleKeyResponse(license_response);
|
||||
|
||||
@@ -827,6 +880,11 @@ 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;
|
||||
@@ -847,6 +905,13 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
|
||||
return SIGNATURE_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
if (!signed_response.has_oemcrypto_core_message()) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
LOGE("Failed to parse license response");
|
||||
@@ -1016,17 +1081,22 @@ CdmResponseType CdmLicense::PrepareContentId(
|
||||
}
|
||||
|
||||
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 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) {
|
||||
if (key_array.empty()) {
|
||||
LOGE("No content keys provided");
|
||||
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);
|
||||
CdmResponseType resp;
|
||||
if (supports_core_messages()) {
|
||||
resp = crypto_session_->LoadLicense(msg, core_message, signature);
|
||||
} else {
|
||||
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();
|
||||
@@ -1040,17 +1110,23 @@ CdmResponseType CdmLicense::HandleContentKeyResponse(
|
||||
}
|
||||
|
||||
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 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) {
|
||||
if (key_array.empty()) {
|
||||
LOGE("No entitlement keys provided");
|
||||
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);
|
||||
CdmResponseType resp;
|
||||
if (supports_core_messages()) {
|
||||
resp = crypto_session_->LoadLicense(msg, core_message, signature);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
//
|
||||
// Description:
|
||||
// Definitions of the protocol buffer messages used in the Widevine license
|
||||
// exchange protocol.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
@@ -18,6 +14,31 @@ option optimize_for = LITE_RUNTIME;
|
||||
|
||||
option java_package = "com.google.video.widevine.protos";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// remote_attestation.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description of section:
|
||||
// Remote attestation is used by ChromeOS device to authenticate itself
|
||||
// to Widevine services for both licensing and keybox provisioning.
|
||||
|
||||
message RemoteAttestation {
|
||||
// Encrypted ClientIdentification message containing the device remote
|
||||
// attestation certificate. Required.
|
||||
optional EncryptedClientIdentification certificate = 1;
|
||||
// Bytes of salt which were added to the remote attestation challenge prior to
|
||||
// signing it. Required.
|
||||
optional bytes salt = 2;
|
||||
// Signed remote attestation challenge + salt. Required.
|
||||
optional bytes signature = 3;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// license_protocol.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description of section:
|
||||
// Definitions of the protocol buffer messages used in the Widevine license
|
||||
// exchange protocol, described in Widevine license exchange protocol document
|
||||
|
||||
enum LicenseType {
|
||||
STREAMING = 1;
|
||||
OFFLINE = 2;
|
||||
@@ -190,6 +211,8 @@ message License {
|
||||
optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE];
|
||||
// Optional requirement to indicate analog output is not allowed.
|
||||
optional bool disable_analog_output = 4 [default = false];
|
||||
// Optional requirement to indicate digital output is not allowed.
|
||||
optional bool disable_digital_output = 5 [default = false];
|
||||
}
|
||||
|
||||
message VideoResolutionConstraint {
|
||||
@@ -274,6 +297,7 @@ message License {
|
||||
enum ProtocolVersion {
|
||||
VERSION_2_0 = 20;
|
||||
VERSION_2_1 = 21;
|
||||
VERSION_2_2 = 22;
|
||||
}
|
||||
|
||||
message LicenseRequest {
|
||||
@@ -379,21 +403,12 @@ message MetricData {
|
||||
repeated TypeValue metric_data = 2;
|
||||
}
|
||||
|
||||
message RemoteAttestation {
|
||||
// Encrypted ClientIdentification message containing the device remote
|
||||
// attestation certificate. Required.
|
||||
optional EncryptedClientIdentification certificate = 1;
|
||||
// Bytes of salt which were added to the remote attestation challenge prior to
|
||||
// signing it. Required.
|
||||
optional bytes salt = 2;
|
||||
// Signed remote attestation challenge + salt. Required.
|
||||
optional bytes signature = 3;
|
||||
}
|
||||
|
||||
message VersionInfo {
|
||||
// License SDK version reported by the Widevine License SDK.
|
||||
// License SDK version reported by the Widevine License SDK. This field
|
||||
// is populated automatically by the SDK.
|
||||
optional string license_sdk_version = 1;
|
||||
// Version of the service hosting the license SDK.
|
||||
// Version of the service hosting the license SDK. This field is optional.
|
||||
// It may be provided by the hosting service.
|
||||
optional string license_service_version = 2;
|
||||
}
|
||||
|
||||
@@ -407,11 +422,29 @@ message SignedMessage {
|
||||
SUB_LICENSE = 6;
|
||||
CAS_LICENSE_REQUEST = 7;
|
||||
CAS_LICENSE = 8;
|
||||
EXTERNAL_LICENSE_REQUEST = 9;
|
||||
EXTERNAL_LICENSE = 10;
|
||||
}
|
||||
|
||||
enum SessionKeyType {
|
||||
UNDEFINED = 0;
|
||||
WRAPPED_AES_KEY = 1;
|
||||
EPHERMERAL_ECC_PUBLIC_KEY = 2;
|
||||
}
|
||||
|
||||
optional MessageType type = 1;
|
||||
optional bytes msg = 2;
|
||||
// Required field that contains the signature of the bytes of msg.
|
||||
// For license requests, the signing algorithm is determined by the
|
||||
// certificate contained in the request.
|
||||
// For license responses, the signing algorithm is HMAC with signing key based
|
||||
// on |session_key|.
|
||||
optional bytes signature = 3;
|
||||
// If populated, the contents of this field will be signaled by the
|
||||
// |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the
|
||||
// key is the bytes of an encrypted AES key. If the |session_key_type| is
|
||||
// EPHERMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1
|
||||
// serialized ECC public key.
|
||||
optional bytes session_key = 4;
|
||||
// Remote attestation data which will be present in the initial license
|
||||
// request for ChromeOS client devices operating in verified mode. Remote
|
||||
@@ -422,41 +455,18 @@ message SignedMessage {
|
||||
// Version information from the SDK and license service. This information is
|
||||
// provided in the license response.
|
||||
optional VersionInfo service_version_info = 7;
|
||||
}
|
||||
message GroupKeys {
|
||||
enum GroupLicenseVersion {
|
||||
GROUP_LICENSE_VERSION_1 = 0;
|
||||
GROUP_LICENSE_VERSION_2 = 1;
|
||||
}
|
||||
|
||||
message GroupKeyData {
|
||||
// Required track type. This indicates the track type to which this key
|
||||
// belongs.
|
||||
optional string track_type = 1;
|
||||
// A required signed message. The message body contains a serialized group
|
||||
// msg.
|
||||
optional bytes key = 2;
|
||||
}
|
||||
|
||||
// Optional key container array used in group licensing V1. This is not used
|
||||
// in V2.
|
||||
repeated License.KeyContainer key = 1 [deprecated = true];
|
||||
|
||||
// Byte string that identifies the group to which this license material
|
||||
// belongs.
|
||||
optional bytes group_id = 2;
|
||||
|
||||
// Required version id beginning with version 2. If not present version 1
|
||||
// should be assumed.
|
||||
optional GroupLicenseVersion version = 3 [default = GROUP_LICENSE_VERSION_1];
|
||||
// Optional key container array for group licensing V2.
|
||||
repeated GroupKeyData key_data = 4;
|
||||
// Optional field that contains the algorithm type used to generate the
|
||||
// session_key and signature in a LICENSE message.
|
||||
optional SessionKeyType session_key_type = 8 [default = WRAPPED_AES_KEY];
|
||||
// The core message is the simple serialization of fields used by OEMCrypto.
|
||||
// This field was introduced in OEMCrypto API v16.
|
||||
optional bytes oemcrypto_core_message = 9;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// certificate_provisioning.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description:
|
||||
// Description of section:
|
||||
// Public protocol buffer definitions for Widevine Device Certificate
|
||||
// Provisioning protocol.
|
||||
|
||||
@@ -466,6 +476,7 @@ message ProvisioningOptions {
|
||||
enum CertificateType {
|
||||
WIDEVINE_DRM = 0; // Default. The original certificate type.
|
||||
X509 = 1; // X.509 certificate.
|
||||
WIDEVINE_KEYBOX = 2;
|
||||
}
|
||||
|
||||
optional CertificateType certificate_type = 1 [default = WIDEVINE_DRM];
|
||||
@@ -474,36 +485,69 @@ message ProvisioningOptions {
|
||||
// authority for signing the generated certificate. This is required if the
|
||||
// certificate type is X509.
|
||||
optional string certificate_authority = 2;
|
||||
// System ID for OTA keybox provisioning. Requires device secure boot.
|
||||
optional uint32 system_id = 3;
|
||||
}
|
||||
|
||||
// Provisioning request sent by client devices to provisioning service.
|
||||
message ProvisioningRequest {
|
||||
//oneof clear_or_encrypted_client_id {
|
||||
message EncryptedSessionKeys {
|
||||
message SessionKeys {
|
||||
// 16 bytes encryption key generated by client, used by the server to:
|
||||
// (1) AES-128-CBC decrypt encrypted_client_id in
|
||||
// EncryptedClientIdentification which is in RemoteAttestation
|
||||
// (2) AES-128-CBC encrypt device_key to be returned in
|
||||
// ProvisioningResponse.
|
||||
optional bytes encryption_key = 1;
|
||||
// 32 bytes mac key generated by client, used by server to sign
|
||||
// the ProvisioningResponse.
|
||||
optional bytes mac_key = 2;
|
||||
}
|
||||
// Serial number of certificate which was used to encrypt the session keys.
|
||||
// Required.
|
||||
optional bytes certificate_serial_number = 1;
|
||||
// Serialized, encrypted session keys. Required.
|
||||
optional bytes encrypted_session_keys = 2;
|
||||
}
|
||||
oneof clear_or_encrypted_client_id {
|
||||
// Device root of trust and other client identification. Required.
|
||||
optional ClientIdentification client_id = 1;
|
||||
optional EncryptedClientIdentification encrypted_client_id = 5;
|
||||
//}
|
||||
ClientIdentification client_id = 1;
|
||||
EncryptedClientIdentification encrypted_client_id = 5;
|
||||
}
|
||||
// Nonce value used to prevent replay attacks. Required.
|
||||
optional bytes nonce = 2;
|
||||
// Options for type of certificate to generate. Optional.
|
||||
optional ProvisioningOptions options = 3;
|
||||
//oneof spoid_param {
|
||||
oneof spoid_param {
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// To be deprecated.
|
||||
optional bytes stable_id = 4;
|
||||
bytes stable_id = 4;
|
||||
// Service provider ID from the service certificate's provider_id field.
|
||||
// Preferred parameter.
|
||||
optional bytes provider_id = 6;
|
||||
bytes provider_id = 6;
|
||||
// Client-generated stable per-origin identifier to be copied directly
|
||||
// to the client certificater serial number.
|
||||
optional bytes spoid = 7;
|
||||
//}
|
||||
// to the client certificate serial number.
|
||||
bytes spoid = 7;
|
||||
}
|
||||
// SessionKeys encrypted using a service cert public key.
|
||||
// Required for keybox provisioning.
|
||||
optional EncryptedSessionKeys encrypted_session_keys = 8;
|
||||
}
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
// This message is used for both regular Widevine DRM certificates and for
|
||||
// application-specific X.509 certificates.
|
||||
message ProvisioningResponse {
|
||||
message OtaKeybox {
|
||||
// Iv used along with SessionKeys.encryption_key for encrypting device key.
|
||||
optional bytes device_key_encryption_iv = 1;
|
||||
// Device key component of the keybox, encrypted using the
|
||||
// SessionKeys.encryption_key in the request and |device_key_encryption_iv|
|
||||
// above.
|
||||
optional bytes encrypted_device_key = 2;
|
||||
// Device CA token component of the keybox.
|
||||
optional bytes device_ca_token = 3;
|
||||
}
|
||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||
// Required. For X.509 certificates, the private RSA key may also include
|
||||
// a prefix as specified by private_key_prefix in the X509CertificateMetadata
|
||||
@@ -512,7 +556,7 @@ message ProvisioningResponse {
|
||||
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||
optional bytes device_rsa_key_iv = 2;
|
||||
// For Widevine DRM certificates, this contains the serialized
|
||||
// SignedDrmDeviceCertificate. For X.509 certificates, this contains the PEM
|
||||
// SignedDrmCertificate. For X.509 certificates, this contains the PEM
|
||||
// encoded X.509 certificate. Required.
|
||||
optional bytes device_certificate = 3;
|
||||
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||
@@ -521,28 +565,76 @@ message ProvisioningResponse {
|
||||
// provisioned device. Encrypted with the device OEM public key using
|
||||
// RSA-OAEP.
|
||||
optional bytes wrapping_key = 5;
|
||||
// Only populated in OTA keybox provisioning response.
|
||||
optional OtaKeybox ota_keybox = 6;
|
||||
}
|
||||
|
||||
// Protocol-specific context data used to hold the state of the server in
|
||||
// stateful provisioning protocols. For more information, please refer to
|
||||
// "Widevine DRM Provisioning using Third-Part and Stateful Protocols".
|
||||
message ProvisioningContext {
|
||||
// Serialized ProvisioningContextKeyData. Required.
|
||||
optional bytes key_data = 1;
|
||||
// Protocol-dependent context data, encrypted with key and IV in key_data.
|
||||
// Required.
|
||||
optional bytes context_data = 2;
|
||||
}
|
||||
|
||||
message SignedProvisioningContext {
|
||||
// ProvisioningContext in bytes.
|
||||
optional bytes provisioning_context = 1;
|
||||
// RSASSA-PSS signature of provisioning_context. Signed with service private
|
||||
// key.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
|
||||
// Cryptographic tokens to be used for ProvisioningContext.
|
||||
message ProvisioningContextKeyData {
|
||||
// Encryption key, usually 32 bytes used for AES-256-CBC. Required.
|
||||
optional bytes encryption_key = 1;
|
||||
// Encryption IV, 16 bytes. Required.
|
||||
optional bytes encryption_iv = 2;
|
||||
}
|
||||
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse signed with
|
||||
// The message authentication key.
|
||||
message SignedProvisioningMessage {
|
||||
enum ProtocolVersion {
|
||||
VERSION_2 = 2; // Keybox factory-provisioned devices.
|
||||
VERSION_3 = 3; // OEM certificate factory-provisioned devices.
|
||||
SERVICE_CERTIFICATE_REQUEST = 1; // Service certificate request.
|
||||
PROVISIONING_20 = 2; // Keybox factory-provisioned devices.
|
||||
PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices.
|
||||
ARCPP_PROVISIONING = 4; // ChromeOS/Arc++ devices.
|
||||
INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol.
|
||||
}
|
||||
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
|
||||
// Serialized protobuf message for the corresponding protocol and stage of
|
||||
// the provisioning exchange. ProvisioningRequest or ProvisioningResponse
|
||||
// in the case of Provisioning 2.0, 3.0 and ARCPP_PROVISIONING. Required.
|
||||
optional bytes message = 1;
|
||||
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required.
|
||||
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required
|
||||
// for provisioning 2.0 and 3.0. For ARCPP_PROVISIONING, only used in
|
||||
// response.
|
||||
optional bytes signature = 2;
|
||||
// Version number of provisioning protocol.
|
||||
optional ProtocolVersion protocol_version = 3 [default = VERSION_2];
|
||||
optional ProtocolVersion protocol_version = 3 [default = PROVISIONING_20];
|
||||
// Protocol-specific context / state information for multiple-exchange,
|
||||
// stateful provisioing protocols. Optional.
|
||||
optional SignedProvisioningContext signed_provisioning_context = 4;
|
||||
// Remote attestation data to authenticate that the ChromeOS client device
|
||||
// is operating in verified mode. Remote attestation challenge data is
|
||||
// |message| field above. Required for ARCPP_PROVISIONING request.
|
||||
// It contains signature of |message|.
|
||||
optional RemoteAttestation remote_attestation = 5;
|
||||
// The core message is the simple serialization of fields used by OEMCrypto.
|
||||
// This field was introduced in OEMCrypto API v16. The core message format is
|
||||
// documented in the "Widevine Core Message Serialization".
|
||||
optional bytes oemcrypto_core_message = 6;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// client_identification.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description:
|
||||
// Description of section:
|
||||
// ClientIdentification messages used by provisioning and license protocols.
|
||||
|
||||
// ClientIdentification message used to authenticate the client device.
|
||||
@@ -575,6 +667,9 @@ message ClientIdentification {
|
||||
enum CertificateKeyType {
|
||||
RSA_2048 = 0;
|
||||
RSA_3072 = 1;
|
||||
ECC_SECP256R1 = 2;
|
||||
ECC_SECP384R1 = 3;
|
||||
ECC_SECP521R1 = 4;
|
||||
}
|
||||
|
||||
enum AnalogOutputCapabilities {
|
||||
@@ -612,6 +707,11 @@ message ClientIdentification {
|
||||
optional uint32 resource_rating_tier = 12 [default = 0];
|
||||
}
|
||||
|
||||
message ClientCredentials {
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
optional bytes token = 2;
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
// Factory-provisioned device root of trust. Required.
|
||||
@@ -627,6 +727,8 @@ message ClientIdentification {
|
||||
optional ClientCapabilities client_capabilities = 6;
|
||||
// Serialized VmpData message. Optional.
|
||||
optional bytes vmp_data = 7;
|
||||
// Optional field that may contain additional provisioning credentials.
|
||||
repeated ClientCredentials device_credentials = 8;
|
||||
}
|
||||
|
||||
// EncryptedClientIdentification message used to hold ClientIdentification
|
||||
@@ -650,7 +752,7 @@ message EncryptedClientIdentification {
|
||||
// ----------------------------------------------------------------------------
|
||||
// device_certificate.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description:
|
||||
// Description of section:
|
||||
// Device certificate and certificate status list format definitions.
|
||||
|
||||
// DRM certificate definition for user devices, intermediate, service, and root
|
||||
@@ -685,8 +787,18 @@ message DrmDeviceCertificate {
|
||||
optional string provider_id = 7;
|
||||
}
|
||||
|
||||
// Contains DRM and OEM certificate status and device information for a
|
||||
// specific system ID.
|
||||
// DeviceCertificate signed with intermediate or root certificate private key.
|
||||
message SignedDrmDeviceCertificate {
|
||||
// Serialized certificate. Required.
|
||||
optional bytes drm_certificate = 1;
|
||||
// Signature of certificate. Signed with root or intermediate
|
||||
// certificate specified below. Required.
|
||||
optional bytes signature = 2;
|
||||
// SignedDrmDeviceCertificate used to sign this certificate.
|
||||
optional SignedDrmDeviceCertificate signer = 3;
|
||||
}
|
||||
|
||||
// Contains the status of the root or an intermediate DeviceCertificate.
|
||||
message DeviceCertificateStatus {
|
||||
enum Status {
|
||||
VALID = 0;
|
||||
@@ -701,9 +813,6 @@ message DeviceCertificateStatus {
|
||||
// Device model information about the device to which the intermediate
|
||||
// certificate(s) correspond.
|
||||
optional ProvisionedDeviceInfo device_info = 4;
|
||||
// Serial number of the OEM X.509 intermediate certificate for this type
|
||||
// of device. Present only if the device is OEM-provisioned.
|
||||
optional bytes oem_serial_number = 5;
|
||||
}
|
||||
|
||||
// List of DeviceCertificateStatus. Used to propagate certificate revocation
|
||||
@@ -727,7 +836,7 @@ message SignedCertificateStatusList {
|
||||
// ----------------------------------------------------------------------------
|
||||
// provisioned_device_info.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description:
|
||||
// Description of section:
|
||||
// Provisioned device info format definitions.
|
||||
|
||||
// Contains device model information for a provisioned device.
|
||||
@@ -740,52 +849,71 @@ message ProvisionedDeviceInfo {
|
||||
LEVEL_3 = 3;
|
||||
}
|
||||
|
||||
// Widevine initial provisioning / bootstrapping method. DRM certificates are
|
||||
// required for retrieving licenses, so if a DRM certificate is not initially
|
||||
// provisioned, then the provisioned credentials will be used to provision
|
||||
// a DRM certificate via the Widevine Provisioning Service.
|
||||
enum ProvisioningMethod {
|
||||
// Don't use this.
|
||||
PROVISIONING_METHOD_UNSPECIFIED = 0;
|
||||
// Factory-provisioned device-unique keybox.
|
||||
FACTORY_KEYBOX = 1;
|
||||
// Factory-provisioned device-unique OEM certificate.
|
||||
FACTORY_OEM_DEVICE_CERTIFICATE = 2;
|
||||
// Factory-provisioned model-group OEM certificate.
|
||||
FACTORY_OEM_GROUP_CERTIFICATE = 3;
|
||||
// Factory-provisioned model-group DRM certificate (Level-3 "baked in").
|
||||
FACTORY_DRM_GROUP_CERTIFICATE = 4;
|
||||
// OTA-provisioned keybox (Level-1 ARC++).
|
||||
OTA_KEYBOX = 5;
|
||||
// OTA-provisioned device-unique OEM certificate.
|
||||
OTA_OEM_DEVICE_CERTIFICATE = 6;
|
||||
// OTA-provisioned model-group OEM certificate.
|
||||
OTA_OEM_GROUP_CERTIFICATE = 7;
|
||||
// OTA-provisioned device-unique DRM certificate (Bedrock).
|
||||
OTA_DRM_DEVICE_CERTIFICATE = 8;
|
||||
}
|
||||
// Represents additional devices that are associated with the device. These
|
||||
// are devices that have the systemID, but a different 'manufacturer'/'model'
|
||||
// etc.
|
||||
message ModelInfo {
|
||||
// Represents the device manufacturer. Typically, this will be Philips, LG,
|
||||
// Sharp, etc.
|
||||
optional string manufacturer = 1;
|
||||
// Model of the device.
|
||||
optional string model = 2;
|
||||
}
|
||||
// Widevine system ID for the device. Mandatory.
|
||||
optional uint32 system_id = 1;
|
||||
// Name of system-on-a-chip. Optional.
|
||||
optional string soc = 2;
|
||||
// Name of manufacturer. Optional.
|
||||
// First registered manufacturer. Optional.
|
||||
optional string manufacturer = 3;
|
||||
// Manufacturer's model name. Matches "brand" in device metadata. Optional.
|
||||
// First registered manufacturer's model name. Matches "brand" in device
|
||||
// metadata. Optional.
|
||||
optional string model = 4;
|
||||
// Type of device (Phone, Tablet, TV, etc).
|
||||
// First registered type of device (Phone, Tablet, TV, etc).
|
||||
optional string device_type = 5;
|
||||
// Device model year. Optional.
|
||||
// First registered device model year. Optional.
|
||||
optional uint32 model_year = 6;
|
||||
// Widevine-defined security level. Optional.
|
||||
optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED];
|
||||
// True if the certificate corresponds to a test (non production) device.
|
||||
// Optional.
|
||||
optional bool test_device = 8 [default = false];
|
||||
// Indicates the type of device root of trust which was factory provisioned.
|
||||
optional ProvisioningMethod provisioning_method = 9;
|
||||
// A list of ModelInfo using the same system_id.
|
||||
repeated ModelInfo model_info = 10;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// widevine_pssh.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description:
|
||||
// Description of section:
|
||||
// Public protocol buffer definitions for Widevine Cenc Header
|
||||
// protocol.
|
||||
|
||||
// Each SubLicense message represents a single content key. These keys can be
|
||||
// added to Widevine CENC initialization data to support both content grouping
|
||||
// and key rotation.
|
||||
message SubLicense {
|
||||
// Required. The key_id of a SUB_SESSION_KEY received in the master license.
|
||||
// SUB_SESSION_KEY is defined in the Widevine License Protocol.
|
||||
optional string sub_session_key_id = 1;
|
||||
|
||||
// Required. The key_msg contains the bytes of a serialized SignedMessage
|
||||
// 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;
|
||||
}
|
||||
|
||||
message WidevinePsshData {
|
||||
enum Type {
|
||||
SINGLE = 0; // Single PSSH to be used to retrieve content keys.
|
||||
@@ -802,6 +930,8 @@ message WidevinePsshData {
|
||||
optional bytes key = 3;
|
||||
// IV used for wrapping |key|. Required.
|
||||
optional bytes iv = 4;
|
||||
// Size of entitlement key used for wrapping |key|.
|
||||
optional uint32 entitlement_key_size_bytes = 5 [default = 32];
|
||||
}
|
||||
|
||||
// Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT
|
||||
@@ -856,6 +986,12 @@ message WidevinePsshData {
|
||||
// PSSHs of type ENTITLED_KEY.
|
||||
repeated EntitledKey entitled_keys = 14;
|
||||
|
||||
// Video feature identifier, which is used in conjunction with |content_id|
|
||||
// to determine the set of keys to be returned in the license. Cannot be
|
||||
// present in conjunction with |key_ids|.
|
||||
// Current values are "HDR".
|
||||
optional string video_feature = 15;
|
||||
|
||||
//////////////////////////// Deprecated Fields ////////////////////////////
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
@@ -867,15 +1003,3 @@ message WidevinePsshData {
|
||||
optional string policy = 6 [deprecated = true];
|
||||
optional bytes grouped_license = 8 [deprecated = true];
|
||||
}
|
||||
|
||||
// Signed device certificate definition.
|
||||
// DrmDeviceCertificate signed by a higher (CA) DRM certificate.
|
||||
message SignedDrmDeviceCertificate {
|
||||
// Serialized certificate. Required.
|
||||
optional bytes drm_certificate = 1;
|
||||
// Signature of certificate. Signed with root or intermediate
|
||||
// certificate specified below. Required.
|
||||
optional bytes signature = 2;
|
||||
// SignedDrmDeviceCertificate used to sign this certificate.
|
||||
optional SignedDrmDeviceCertificate signer = 3;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -163,11 +163,17 @@ CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
|
||||
LOGE("Failed to parse signed service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
|
||||
#ifdef ACCEPT_TEST_CERT
|
||||
# pragma message "(TEST ONLY): Ignoring service cert's signature."
|
||||
#else
|
||||
if (!root_key.VerifySignature(signed_service_cert.drm_certificate(),
|
||||
signed_service_cert.signature())) {
|
||||
LOGE("Failed to verify service certificate signature");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
#endif
|
||||
|
||||
DrmDeviceCertificate service_cert;
|
||||
if (!service_cert.ParseFromString(signed_service_cert.drm_certificate())) {
|
||||
LOGE("Failed to parse service certificate");
|
||||
|
||||
@@ -17,11 +17,7 @@ namespace wvcdm {
|
||||
namespace {
|
||||
std::string kEmptyString;
|
||||
size_t kMaxCryptoRetries = 3;
|
||||
size_t kMinUsageEntriesSupported = 200;
|
||||
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
||||
uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0;
|
||||
uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0;
|
||||
uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0;
|
||||
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||
std::string kOldUsageEntryPoviderSessionToken =
|
||||
@@ -197,7 +193,7 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
// minimum capacity (>200), we need to make sure we can still add and
|
||||
// remove entries. If not, clear files/data and recreate usage header table.
|
||||
if (status == NO_ERROR && lru_success) {
|
||||
if (usage_entry_info_.size() > kMinUsageEntriesSupported) {
|
||||
if (usage_entry_info_.size() > kMinimumUsageTableEntriesSupported) {
|
||||
uint32_t temporary_usage_entry_number;
|
||||
|
||||
// Create a new temporary usage entry, close the session and then
|
||||
@@ -251,9 +247,6 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
status = crypto_session->CreateUsageTableHeader(&usage_table_header_);
|
||||
if (status != NO_ERROR) return false;
|
||||
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
|
||||
|
||||
UpgradeFromUsageTable(file_handle_.get(), metrics);
|
||||
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
|
||||
}
|
||||
|
||||
is_inited_ = true;
|
||||
@@ -750,179 +743,6 @@ CdmResponseType UsageTableHeader::Shrink(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType UsageTableHeader::UpgradeFromUsageTable(
|
||||
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
|
||||
UpgradeLicensesFromUsageTable(handle, metrics);
|
||||
UpgradeUsageInfoFromUsageTable(handle, metrics);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool UsageTableHeader::UpgradeLicensesFromUsageTable(
|
||||
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
|
||||
// Fetch the key set IDs for each offline license. For each license
|
||||
// * retrieve the provider session token,
|
||||
// * load the old usage table by creating a new dummy entry
|
||||
// * copy over the entry from the old usage table and
|
||||
// * update the usage header table and entry numbers
|
||||
// * save the usage table header and store the usage entry number and
|
||||
// usage entry along with the license to persistent memory
|
||||
std::vector<std::string> key_set_ids;
|
||||
if (!handle->ListLicenses(&key_set_ids)) {
|
||||
LOGE("Unable to retrieve list of licenses");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < key_set_ids.size(); ++i) {
|
||||
DeviceFiles::CdmLicenseData license_data;
|
||||
DeviceFiles::ResponseType sub_error_code = DeviceFiles::kNoError;
|
||||
|
||||
if (!handle->RetrieveLicense(key_set_ids[i], &license_data,
|
||||
&sub_error_code)) {
|
||||
LOGW("Failed to retrieve license: key_set_index = %zu, status = %d", i,
|
||||
static_cast<int>(sub_error_code));
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string provider_session_token;
|
||||
if (!CdmLicense::ExtractProviderSessionToken(license_data.license,
|
||||
&provider_session_token)) {
|
||||
LOGW("Failed to extract provider session token: key_set_index = %zu", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (provider_session_token.empty()) continue;
|
||||
|
||||
std::unique_ptr<CryptoSession> crypto_session(
|
||||
CryptoSession::MakeCryptoSession(metrics));
|
||||
CdmResponseType status = crypto_session->Open(requested_security_level_);
|
||||
|
||||
if (status != NO_ERROR) continue;
|
||||
|
||||
// We create this entry since OEMCrypto_CopyOldUsageEntry needs the old
|
||||
// usage table to be loaded in order to copy an entry.
|
||||
if (!CreateDummyOldUsageEntry(crypto_session.get())) continue;
|
||||
|
||||
status = AddEntry(crypto_session.get(), true /* persistent license */,
|
||||
key_set_ids[i], kEmptyString, license_data.license,
|
||||
&license_data.usage_entry_number);
|
||||
|
||||
if (status != NO_ERROR) continue;
|
||||
|
||||
status = crypto_session->CopyOldUsageEntry(provider_session_token);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
crypto_session->Close();
|
||||
Shrink(metrics, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = UpdateEntry(license_data.usage_entry_number, crypto_session.get(),
|
||||
&license_data.usage_entry);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
crypto_session->Close();
|
||||
Shrink(metrics, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!handle->StoreLicense(license_data, &sub_error_code)) {
|
||||
LOGW("Failed to store license: key_set_index = %zu, status = %d", i,
|
||||
static_cast<int>(sub_error_code));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool UsageTableHeader::UpgradeUsageInfoFromUsageTable(
|
||||
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
|
||||
// Fetch all usage files. For each file retrieve all the usage info records
|
||||
// within the file. For each piece of usage information
|
||||
// * load the old usage table by creating a new dummy entry
|
||||
// * copy over the entry from the old usage table and
|
||||
// * update the usage header table and entry numbers
|
||||
// * save the usage table header
|
||||
// * once done processing all the usage records from a file, save the usage
|
||||
// information to persistent memory along with usage entry number and usage
|
||||
// entry.
|
||||
std::vector<std::string> usage_info_file_names;
|
||||
if (!handle->ListUsageInfoFiles(&usage_info_file_names)) {
|
||||
LOGE("Unable to retrieve list of usage info file names");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < usage_info_file_names.size(); ++i) {
|
||||
std::vector<DeviceFiles::CdmUsageData> usage_data;
|
||||
if (!handle->RetrieveUsageInfo(usage_info_file_names[i], &usage_data)) {
|
||||
LOGW("Failed to retrieve usage records: usage_info_file_name = %s",
|
||||
usage_info_file_names[i].c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < usage_data.size(); ++j) {
|
||||
if (usage_data[j].provider_session_token.empty()) {
|
||||
LOGW("Provider session ID is empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<CryptoSession> crypto_session(
|
||||
CryptoSession::MakeCryptoSession(metrics));
|
||||
CdmResponseType status = crypto_session->Open(requested_security_level_);
|
||||
|
||||
if (status != NO_ERROR) continue;
|
||||
|
||||
// We create this entry since OEMCrypto_CopyOldUsageEntry needs the old
|
||||
// usage table to be loaded in order to copy an entry.
|
||||
if (!CreateDummyOldUsageEntry(crypto_session.get())) continue;
|
||||
|
||||
// TODO(rfrias): We need to fill in the app id, but it is hashed
|
||||
// and we have no way to extract. Use the hased filename instead?
|
||||
status =
|
||||
AddEntry(crypto_session.get(), false /* usage info */,
|
||||
usage_data[j].key_set_id, usage_info_file_names[i],
|
||||
usage_data[j].license, &(usage_data[j].usage_entry_number));
|
||||
|
||||
if (status != NO_ERROR) continue;
|
||||
|
||||
status = crypto_session->CopyOldUsageEntry(
|
||||
usage_data[j].provider_session_token);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
crypto_session->Close();
|
||||
Shrink(metrics, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = UpdateEntry(usage_data[j].usage_entry_number,
|
||||
crypto_session.get(), &(usage_data[j].usage_entry));
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
crypto_session->Close();
|
||||
Shrink(metrics, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handle->StoreUsageInfo(usage_info_file_names[i], usage_data)) {
|
||||
LOGE("Failed to store upgraded usage records: usage_info_file_name = %s",
|
||||
usage_info_file_names[i].c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// TODO(fredgc): remove when b/65730828 is addressed
|
||||
bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) {
|
||||
return crypto_session->CreateOldUsageEntry(
|
||||
kOldUsageEntryTimeSinceLicenseReceived,
|
||||
kOldUsageEntryTimeSinceFirstDecrypt, kOldUsageEntryTimeSinceLastDecrypt,
|
||||
CryptoSession::kUsageDurationsInvalid, kOldUsageEntryServerMacKey,
|
||||
kOldUsageEntryClientMacKey, kOldUsageEntryPoviderSessionToken);
|
||||
}
|
||||
|
||||
// Test only method.
|
||||
void UsageTableHeader::DeleteEntryForTest(uint32_t usage_entry_number) {
|
||||
LOGV("Deleting entry for test: usage_entry_number = %u", usage_entry_number);
|
||||
|
||||
Reference in New Issue
Block a user