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:
Fred Gylys-Colwell
2020-01-18 10:11:24 -08:00
parent 7e2619e379
commit 7665614b2e
132 changed files with 12331 additions and 9341 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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);