Source release 18.5.0

This commit is contained in:
Matt Feddersen
2024-03-28 19:15:22 -07:00
parent b2c35151ad
commit 28ec8548c6
109 changed files with 3623 additions and 1012 deletions

View File

@@ -35,5 +35,5 @@ cc_library {
static_libs: ["libcdm_metrics_protos"],
whole_static_libs: ["libcdm_metrics_protos"],
export_static_lib_headers: ["libcdm_metrics_protos"],
min_sdk_version: "UpsideDownCake",
min_sdk_version: "34",
}

View File

@@ -38,8 +38,8 @@ std::string MapHdcpVersion(CryptoSession::HdcpCapability version) {
switch (version) {
case HDCP_NONE:
return QUERY_VALUE_HDCP_NONE;
case HDCP_V1:
return QUERY_VALUE_HDCP_V1;
case HDCP_V1: // 1.x, not 1.0
return QUERY_VALUE_HDCP_V1_X;
case HDCP_V2:
return QUERY_VALUE_HDCP_V2_0;
case HDCP_V2_1:
@@ -752,6 +752,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
}
switch (token_type) {
case kClientTokenDrmCert:
case kClientTokenDrmCertificateReprovisioning:
*query_response = QUERY_VALUE_DRM_CERTIFICATE;
break;
case kClientTokenKeybox:
@@ -887,7 +888,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
const CdmResponseType status = crypto_session->GetBootCertificateChain(
security_level, &bcc, &signature_unused);
if (status == NO_ERROR) {
LOGD("BCC length: %zu", bcc.size());
LOGV("BCC length: %zu", bcc.size());
*query_response = std::move(bcc);
return CdmResponseType(NO_ERROR);
}
@@ -900,6 +901,24 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
LOGE("Failed to extract BCC: status = %d", status.ToInt());
return status;
}
if (query_token == QUERY_KEY_DEVICE_INFORMATION) {
std::string device_info;
const CdmResponseType status =
crypto_session->GetDeviceInformation(security_level, &device_info);
if (status == NO_ERROR) {
LOGV("device_info length: %zu", device_info.size());
*query_response = std::move(device_info);
return CdmResponseType(NO_ERROR);
}
if (status == NOT_IMPLEMENTED_ERROR ||
status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) {
LOGV("device_info not available: %s", status.ToString().c_str());
*query_response = QUERY_VALUE_NONE;
return CdmResponseType(NO_ERROR);
}
LOGE("Failed to extract device_info: %s", status.ToString().c_str());
return status;
}
CdmResponseType status;
M_TIME(status = crypto_session->Open(security_level),
@@ -1038,6 +1057,34 @@ CdmResponseType CdmEngine::QueryOemCryptoSessionId(
return session->QueryOemCryptoSessionId(query_response);
}
CdmResponseType CdmEngine::QueryDeviceSignedCsrPayload(
const std::string& challenge, const std::string& device_info,
std::string* query_response) {
if (query_response == nullptr) {
LOGE("Output |query_response| is null");
return CdmResponseType(PARAMETER_NULL);
}
std::unique_ptr<CryptoSession> crypto_session(
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
std::string signed_csr_payload;
const CdmResponseType status = crypto_session->GetDeviceSignedCsrPayload(
kLevelDefault, challenge, device_info, &signed_csr_payload);
if (status == NO_ERROR) {
LOGV("signed_csr_payload length: %zu", signed_csr_payload.size());
*query_response = std::move(signed_csr_payload);
return CdmResponseType(NO_ERROR);
}
if (status == NOT_IMPLEMENTED_ERROR ||
status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) {
LOGD("signed_csr_payload not available: %s", status.ToString().c_str());
*query_response = QUERY_VALUE_NONE;
return CdmResponseType(NO_ERROR);
}
LOGE("Failed to extract signed_csr_payload: %s", status.ToString().c_str());
return status;
}
// static
bool CdmEngine::IsSecurityLevelSupported(CdmSecurityLevel level) {
LOGI("level = %s", CdmSecurityLevelToString(level));
@@ -2358,9 +2405,9 @@ CdmResponseType CdmEngine::SignRsa(const std::string& wrapped_key,
{
std::unique_lock<std::recursive_mutex> lock(session_map_lock_);
if (!session_map_.FindSession(session_id, &session)) {
LOGE("Session not found: session_id = %s", IdToString(session_id));
CloseSession(session_id);
return CdmResponseType(SESSION_NOT_FOUND_24);
LOGE("Session not found: session_id = %s", IdToString(session_id));
CloseSession(session_id);
return CdmResponseType(SESSION_NOT_FOUND_24);
}
}

View File

@@ -81,9 +81,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
is_usage_update_needed_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
@@ -866,18 +864,10 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_index) {
// The usage entry cannot be deleted if it has a crypto session handling
// it, so close and reopen session.
UpdateUsageEntryInformation();
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
if (sts != NO_ERROR) return sts;
usage_table_ = nullptr;
bool has_support = false;
if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) {
usage_table_ = crypto_session_->GetUsageTable();
}
crypto_session_->Close();
CdmResponseType sts = ResetCryptoSession();
if (sts != NO_ERROR) return sts;
if (usage_table_ == nullptr) {
LOGE("Usage table header unavailable");
@@ -1012,14 +1002,8 @@ bool CdmSession::StoreLicense(CdmOfflineLicenseState state, int* error_detail) {
}
CdmResponseType CdmSession::RemoveKeys() {
CdmResponseType sts;
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
// Ignore errors
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
policy_engine_.reset(
new PolicyEngine(session_id_, nullptr, crypto_session_.get()));
return CdmResponseType(NO_ERROR);
crypto_session_->Close();
return ResetCryptoSession();
}
CdmResponseType CdmSession::RemoveLicense() {
@@ -1310,8 +1294,78 @@ CdmResponseType CdmSession::LoadCastPrivateKey(
CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message,
std::string* signature,
RSA_Padding_Scheme scheme) {
return crypto_session_->GenerateRsaSignature(message, signature,
scheme);
return crypto_session_->GenerateRsaSignature(message, signature, scheme);
}
CdmResponseType CdmSession::ResetCryptoSession() {
LOGD("Resetting crypto session: session_id = %s, ksid = %s",
IdToString(session_id_), IdToString(key_set_id_));
if (mock_crypto_session_in_use_) {
// If the crypto session is not reset, then there is nothing to do.
return CdmResponseType(NO_ERROR);
}
CdmResponseType sts;
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
usage_table_ = nullptr;
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
CdmResponseType final_sts(NO_ERROR);
if (sts != NO_ERROR) {
// Challenging case, still need to reset other components.
LOGE("Failed to open crypto session: sts = %s", sts.ToString().c_str());
final_sts = sts;
} else {
// Reset all component dependent on the crypto session.
security_level_ = crypto_session_->GetSecurityLevel();
crypto_metrics_->crypto_session_security_level_.Record(security_level_);
if (!file_handle_->Init(security_level_)) {
LOGE("Unable to initialize file handle");
final_sts = CdmResponseType(SESSION_FILE_HANDLE_INIT_ERROR);
}
if (!file_handle_->HasCertificate(atsc_mode_enabled_)) {
LOGE("Missing certificate: atsc_mode_enabled = %s",
BoolToString(atsc_mode_enabled_));
final_sts = CdmResponseType(NEED_PROVISIONING);
}
bool has_support = false;
if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) {
usage_table_ = crypto_session_->GetUsageTable();
}
}
// Even if the session is not open, other members need new session pointer.
if (mock_policy_engine_in_use_) {
// Simply pass the new pointer.
policy_engine_->set_crypto_session(crypto_session_.get());
} else {
// Attempt to maintain event listener.
WvCdmEventListener* event_listener =
policy_engine_ ? policy_engine_->event_listener() : nullptr;
policy_engine_.reset(
new PolicyEngine(session_id_, event_listener, crypto_session_.get()));
}
if (mock_license_parser_in_use_) {
license_parser_->set_crypto_session(crypto_session_.get());
license_parser_->set_policy_engine(policy_engine_.get());
} else {
license_parser_.reset(new CdmLicense(session_id_));
std::string service_certificate;
if (!Properties::GetServiceCertificate(session_id_, &service_certificate))
service_certificate.clear();
if (!license_parser_->Init(Properties::UsePrivacyMode(session_id_),
service_certificate, crypto_session_.get(),
policy_engine_.get())) {
LOGE("Failed to initialize license parser");
final_sts = CdmResponseType(LICENSE_PARSER_INIT_ERROR);
}
}
return final_sts;
}
// For testing only - takes ownership of pointers
@@ -1323,6 +1377,7 @@ void CdmSession::set_license_parser(CdmLicense* license_parser) {
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
crypto_session_.reset(crypto_session);
mock_crypto_session_in_use_ = true;
}
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {

View File

@@ -382,6 +382,9 @@ CdmResponseType CdmUsageTable::InvalidateEntryInternal(
// sent back to the caller for the CDM as a whole to handle.
const uint32_t pre_defrag_store_counter = store_table_counter_;
const CdmResponseType status = DefragTable(device_files, metrics);
if (status != NO_ERROR) {
LOGW("Failed to defrag usage table: sts = %s", status.ToString().c_str());
}
if (pre_defrag_store_counter == store_table_counter_) {
// It is possible that DefragTable() does not result in any
// changes to the table, and as a result, it will not store the
@@ -468,6 +471,10 @@ bool CdmUsageTable::CapacityCheck(CryptoSession* const crypto_session) {
return false;
}
// Session must be closed before invalidating, otherwise Shrink() will
// fail in call to InvalidateEntry().
local_crypto_session->Close();
status =
InvalidateEntry(temporary_entry_index,
/* defrag_table = */ true, device_files_.get(), metrics);
@@ -480,7 +487,10 @@ bool CdmUsageTable::CapacityCheck(CryptoSession* const crypto_session) {
// The entry should have been deleted from the usage table,
// not just marked as type unknown. Failure to call
// Shrink() may be an indicator of other issues.
LOGE("Failed to shrink table for capacity test");
LOGE(
"Failed to shrink table for capacity test: "
"post_check_size = %zu, check_usage_entry_number = %u",
entry_info_list_.size(), temporary_entry_index);
return false;
}
return true;
@@ -949,7 +959,12 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files,
if (entries_to_move.empty()) {
LOGD("No valid entries found, shrinking entire table: size = %zu",
entry_info_list_.size());
return Shrink(metrics, static_cast<uint32_t>(entry_info_list_.size()));
const CdmResponseType status =
Shrink(metrics, static_cast<uint32_t>(entry_info_list_.size()));
if (status != NO_ERROR) {
LOGE("Failed to shrink table: sts = %s", status.ToString().c_str());
}
return status;
}
// Step 3: Ignore invalid entries that are after the last valid
@@ -973,7 +988,11 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files,
last_valid_entry - 1;
LOGD("Removing all entries after the last valid entry: count = %u",
to_remove);
return Shrink(metrics, to_remove);
const CdmResponseType status = Shrink(metrics, to_remove);
if (status != NO_ERROR) {
LOGE("Failed to shrink table: sts = %s", status.ToString().c_str());
}
return status;
}
// Step 4: Move the valid entries to overwrite the invalid entries.
@@ -1101,7 +1120,12 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files,
LOGD(
"All entries have been invalidated, shrinking entire table: size = %zu",
entry_info_list_.size());
return Shrink(metrics, static_cast<uint32_t>(entry_info_list_.size()));
const CdmResponseType status =
Shrink(metrics, static_cast<uint32_t>(entry_info_list_.size()));
if (status != NO_ERROR) {
LOGE("Failed to shrink table: sts = %s", status.ToString().c_str());
}
return status;
}
const UsageEntryIndex to_remove =
@@ -1120,7 +1144,11 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files,
// Step 6: Shrink table to the new size.
LOGD("Clean up complete, shrinking table: count = %u", to_remove);
return Shrink(metrics, to_remove);
const CdmResponseType status = Shrink(metrics, to_remove);
if (status != NO_ERROR) {
LOGE("Failed to shrink table: sts = %s", status.ToString().c_str());
}
return status;
} // End Defrag().
CdmResponseType CdmUsageTable::ReleaseOldestEntry(

View File

@@ -151,7 +151,12 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter(
return status;
}
request->set_stable_id(device_unique_id + origin);
} // No else clause, by design. It is valid to do nothing.
} else {
// It is valid to do nothing for legacy devices. For most recently
// launched devices this is an error. For now, we will log
// but not return an error.
LOGE("No spoid/provider id/stable id set");
}
return CdmResponseType(NO_ERROR);
}
@@ -164,6 +169,8 @@ CertificateProvisioning::GetProvisioningType() {
return SignedProvisioningMessage::PROVISIONING_40;
case kClientTokenOemCert:
return SignedProvisioningMessage::PROVISIONING_30;
case kClientTokenDrmCertificateReprovisioning:
return SignedProvisioningMessage::DRM_REPROVISIONING;
default:
return SignedProvisioningMessage::PROVISIONING_20;
}
@@ -210,7 +217,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
if (crypto_session_->GetPreProvisionTokenType() ==
kClientTokenBootCertChain) {
return GetProvisioning40RequestInternal(file_system, origin, spoid, request,
default_url);
default_url, cert_type,
cert_authority);
}
// Prepare device provisioning request.
@@ -236,21 +244,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
sizeof(nonce));
provisioning_request.set_nonce(encoded_nonce);
ProvisioningOptions* options = provisioning_request.mutable_options();
switch (cert_type) {
case kCertificateWidevine:
options->set_certificate_type(ProvisioningOptions::WIDEVINE_DRM);
break;
case kCertificateX509:
options->set_certificate_type(ProvisioningOptions::X509);
break;
default:
LOGE("Unknown certificate type: %d", static_cast<int>(cert_type));
return CdmResponseType(CERT_PROVISIONING_INVALID_CERT_TYPE);
}
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
status = CertTypeAssign(provisioning_request, cert_type, cert_authority);
if (status != NO_ERROR) return status;
status = SetSpoidParameter(origin, spoid, &provisioning_request);
if (status != NO_ERROR) return status;
@@ -309,7 +304,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
wvutil::FileSystem* file_system, const std::string& origin,
const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url) {
std::string* default_url, CdmCertificateType cert_type,
const std::string& cert_authority) {
if (!crypto_session_->IsOpen()) {
LOGE("Crypto session is not open");
return CdmResponseType(PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN);
@@ -380,9 +376,8 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
// Since |stored_oem_cert| is empty, the client identification token will be
// retrieved from OEMCrypto, which is the BCC in this case.
status = FillEncryptedClientIdWithAdditionalParameter(
stored_oem_cert, additional_parameter, provisioning_request,
wv_service_cert);
status = FillEncryptedClientId(stored_oem_cert, provisioning_request,
wv_service_cert);
if (status != NO_ERROR) return status;
} else {
// This is the second stage provisioning.
@@ -393,6 +388,29 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
stored_oem_cert, additional_parameter, provisioning_request,
*service_certificate_);
if (status != NO_ERROR) return status;
// If cert type is X509, provisioning Cast cert.
if (cert_type == kCertificateX509) {
uint32_t nonce;
status = crypto_session_->GenerateNonce(&nonce);
if (status != NO_ERROR) {
LOGE("Failed to generate a nonce: status = %d",
static_cast<int>(status));
return status == NONCE_GENERATION_ERROR
? CdmResponseType(CERT_PROVISIONING_NONCE_GENERATION_ERROR)
: status;
}
// The provisioning server does not convert the nonce to uint32_t, it just
// passes the binary data to the response message.
const std::string encoded_nonce(reinterpret_cast<char*>(&nonce),
sizeof(nonce));
provisioning_request.set_nonce(encoded_nonce);
status = CertTypeAssign(provisioning_request, cert_type, cert_authority);
if (status != NO_ERROR) return status;
}
}
std::string public_key;
@@ -415,6 +433,7 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
provisioning_request_message_ = serialized_message;
SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message);
@@ -511,8 +530,11 @@ CertificateProvisioning::FillEncryptedClientIdWithAdditionalParameter(
}
CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
wvutil::FileSystem* file_system, const std::string& response_message) {
wvutil::FileSystem* file_system,
const SignedProvisioningMessage& signed_response, std::string* cert,
std::string* wrapped_key) {
ProvisioningResponse provisioning_response;
const std::string& response_message = signed_response.message();
if (response_message.empty() ||
!provisioning_response.ParseFromString(response_message)) {
return CdmResponseType(PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE);
@@ -540,9 +562,44 @@ CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
LOGE("No private key was generated");
return CdmResponseType(PROVISIONING_4_NO_PRIVATE_KEY);
}
const CryptoWrappedKey private_key(provisioning_40_key_type_,
provisioning_40_wrapped_private_key_);
if (cert_type_ == kCertificateX509) {
// Load csr private key to decrypt session key
auto status = crypto_session_->LoadCertificatePrivateKey(private_key);
if (status != NO_ERROR) {
LOGE("Failed to load x509 certificate.");
return status;
}
status = crypto_session_->GenerateDerivedKeys(
provisioning_request_message_, signed_response.session_key());
if (status != NO_ERROR) {
LOGE("Failed to generate derived keys.");
return status;
}
// Get wrapped private key for cast cert
CryptoWrappedKey cast_cert_private_key;
const std::string& signature = signed_response.signature();
const std::string& core_message = signed_response.oemcrypto_core_message();
status = crypto_session_->LoadProvisioning(response_message, core_message,
signature,
&cast_cert_private_key.key());
if (status != NO_ERROR) {
LOGE("Failed to generate wrapped key for cast cert.");
return status;
}
CloseSession();
*cert = device_certificate;
*wrapped_key = cast_cert_private_key.key();
return CdmResponseType(NO_ERROR);
}
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
CloseSession();
wvutil::FileSystem global_file_system;
@@ -621,7 +678,8 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (signed_response.provisioning_type() ==
SignedProvisioningMessage::PROVISIONING_40) {
return HandleProvisioning40Response(file_system, signed_response.message());
return HandleProvisioning40Response(file_system, signed_response, cert,
wrapped_key);
}
bool error = false;
@@ -841,4 +899,25 @@ CdmResponseType CertificateProvisioning::CloseSessionOnError(
return status;
}
CdmResponseType CertificateProvisioning::CertTypeAssign(
video_widevine::ProvisioningRequest& provisioning_request,
CdmCertificateType cert_type, const std::string& cert_authority) {
ProvisioningOptions* options = provisioning_request.mutable_options();
switch (cert_type) {
case kCertificateWidevine:
options->set_certificate_type(ProvisioningOptions::WIDEVINE_DRM);
break;
case kCertificateX509:
options->set_certificate_type(ProvisioningOptions::X509);
break;
default:
LOGE("Unknown certificate type: %d", static_cast<int>(cert_type));
return CdmResponseType(CERT_PROVISIONING_INVALID_CERT_TYPE);
}
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
return CdmResponseType(NO_ERROR);
}
} // namespace wvcdm

View File

@@ -104,7 +104,8 @@ CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning(
/*
* Return the ClientIdentification message token type for provisioning request.
* NOTE: a DRM Cert should never be presented to the provisioning server.
* NOTE: a DRM Cert should never be presented to the provisioning server unless
* DRM re-provisioning is being used.
*/
CdmResponseType ClientIdentification::Prepare(
const CdmAppParameterMap& app_parameters,
@@ -145,7 +146,8 @@ CdmResponseType ClientIdentification::Prepare(
}
ClientIdentification_NameValue* client_info;
if (is_license_request_) {
// Include app parameters for license and provisioning requests
if (!is_okp_request_) {
CdmAppParameterMap::const_iterator iter;
for (iter = app_parameters.begin(); iter != app_parameters.end(); ++iter) {
if (IsPropertyKeyReserved(iter->first)) {
@@ -382,6 +384,10 @@ bool ClientIdentification::GetProvisioningTokenType(
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
return true;
case kClientTokenDrmCertificateReprovisioning:
*token_type =
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
return true;
case kClientTokenDrmCert:
default:
// shouldn't happen

View File

@@ -105,18 +105,36 @@ OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id,
// Decrypt for ContentKeySession
OEMCryptoResult ContentKeySession::Decrypt(
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
const OEMCrypto_CENCEncryptPatternDesc& pattern,
bool is_any_subsample_protected) {
size_t total_size = 0;
for (size_t i = 0; i < samples_length; ++i) {
total_size += samples[i].buffers.input_data_length;
}
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DecryptCENC(security_level_, key_handle_.data(),
key_handle_.size(), samples,
samples_length, &pattern),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(total_size));
OEMCryptoResult sts = OEMCrypto_SUCCESS;
if (key_handle_.size() == 0 && !is_any_subsample_protected) {
for (size_t i = 0; i < samples_length; ++i) {
if (samples[i].subsamples != nullptr &&
samples[i].subsamples_length > 0) {
const OEMCrypto_SubSampleDescription& subsample =
samples[i].subsamples[0];
M_TIME(sts = OEMCrypto_CopyBuffer(oec_session_id_,
samples[i].buffers.input_data,
samples[i].buffers.input_data_length,
&samples[i].buffers.output_descriptor,
subsample.subsample_flags),
metrics_, oemcrypto_copy_buffer_, sts,
metrics::Pow2Bucket(samples[i].buffers.input_data_length));
}
}
} else {
M_TIME(sts = OEMCrypto_DecryptCENC(security_level_, key_handle_.data(),
key_handle_.size(), samples,
samples_length, &pattern),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(total_size));
}
return sts;
}

View File

@@ -62,7 +62,7 @@ OEMCryptoResult WrapIfNecessary(OEMCryptoResult ret_value) { return ret_value; }
}
#define RETURN_IF_NOT_OPEN(ret_value) \
if (!open_) { \
if (!IsOpen()) { \
LOGE("Crypto session is not open"); \
return WrapIfNecessary(ret_value); \
}
@@ -70,17 +70,8 @@ OEMCryptoResult WrapIfNecessary(OEMCryptoResult ret_value) { return ret_value; }
#define CRYPTO_ERROR(cdm_err, oem_err) \
CdmResponseType(cdm_err, oem_err, __func__)
#ifdef HAS_DUAL_KEY
/**
* Internal only OEMCrypto method. This is called before parsing the license
* response to indicate the response uses dual keys. If this isn't called,
* it is using single keys.
*/
extern "C" OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION session,
bool dual_key);
#endif
namespace wvcdm {
namespace {
using UsageTableLock = std::unique_lock<std::recursive_mutex>;
@@ -93,6 +84,8 @@ constexpr size_t kAes128BlockSize = 16;
constexpr int kMaxTerminateCountDown = 5;
constexpr char kUnavailableHdcpLevel[] = "<unavailable>";
const std::string kStringNotAvailable = "NA";
// TODO(b/174412779): Remove when b/170704368 is fixed.
@@ -292,6 +285,37 @@ OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) {
: OEMCrypto_CipherMode_CBCS;
}
// static
const char* CryptoSession::HdcpCapabilityToString(HdcpCapability hdcp_level) {
switch (hdcp_level) {
case HDCP_NONE:
return "None";
case HDCP_V1: // Means v1.x
return "v1.x";
case HDCP_V2: // Means v2.0
return "v2.0";
case HDCP_V2_1:
return "v2.1";
case HDCP_V2_2:
return "v2.2";
case HDCP_V2_3:
return "v2.3";
case HDCP_V1_0:
return "v1.0";
case HDCP_V1_1:
return "v1.1";
case HDCP_V1_2:
return "v1.2";
case HDCP_V1_3:
return "v1.3";
case HDCP_V1_4:
return "v1.4";
case HDCP_NO_DIGITAL_OUTPUT:
return "No-Digital-Output";
}
return UnknownEnumValueToString(static_cast<int>(hdcp_level));
}
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
: metrics_(metrics),
system_id_(NULL_SYSTEM_ID),
@@ -345,6 +369,9 @@ CdmResponseType CryptoSession::GetProvisioningMethod(
case OEMCrypto_BootCertificateChain:
type = kClientTokenBootCertChain;
break;
case OEMCrypto_DrmReprovisioning:
type = kClientTokenDrmCertificateReprovisioning;
break;
case OEMCrypto_ProvisioningError:
default:
if (static_cast<int>(method) == 0 && needs_keybox_provisioning_) {
@@ -661,6 +688,9 @@ CdmResponseType CryptoSession::GetProvisioningToken(
} else if (pre_provision_token_type_ == kClientTokenBootCertChain) {
status = GetBootCertificateChain(requested_security_level, token,
additional_token);
} else if (pre_provision_token_type_ ==
kClientTokenDrmCertificateReprovisioning) {
status = GetTokenFromEmbeddedCertificate(token);
}
metrics_->crypto_session_get_token_.Increment(status);
return status;
@@ -1229,6 +1259,7 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
}
OEMCryptoResult sts;
// TODO: b/305093063 - Refactor to a switch statement to improve readability
if (pre_provision_token_type_ == kClientTokenKeybox) {
should_specify_algorithm = false;
const CdmResponseType status = GenerateDerivedKeys(message);
@@ -1246,6 +1277,11 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
should_specify_algorithm = true;
// Do nothing here. The key to signing the provisioning 4.0 request for each
// stage has been loaded already when it was generated by OEMCrypto.
} else if (pre_provision_token_type_ ==
kClientTokenDrmCertificateReprovisioning) {
should_specify_algorithm = false;
// Do nothing here. The baked-in certificate used as the token has already
// been loaded when the EncryptedClientId was filled in.
} else {
LOGE("Unknown method %d", pre_provision_token_type_);
return CdmResponseType(UNKNOWN_CLIENT_TOKEN_TYPE);
@@ -1417,6 +1453,153 @@ CdmResponseType CryptoSession::GetBootCertificateChain(
return CdmResponseType(NO_ERROR);
}
CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate(
std::string* token) {
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
RETURN_IF_NULL(token, PARAMETER_NULL);
CdmClientTokenType token_type = kClientTokenUninitialized;
const CdmResponseType sts =
GetProvisioningMethod(requested_security_level_, &token_type);
if (sts != NO_ERROR) {
LOGE("Failed to get token type");
return sts;
}
if (token_type != kClientTokenDrmCertificateReprovisioning) {
token->clear();
return CdmResponseType(NO_ERROR);
}
size_t certificate_length = CERTIFICATE_DATA_SIZE;
std::string embedded_certificate(certificate_length, '\0');
OEMCryptoResult status =
WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 1", [&] {
return OEMCrypto_GetEmbeddedDrmCertificate(
reinterpret_cast<uint8_t*>(&embedded_certificate.front()),
&certificate_length);
});
if (status == OEMCrypto_ERROR_SHORT_BUFFER) {
embedded_certificate.assign(certificate_length, '\0');
status =
WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 2", [&] {
return OEMCrypto_GetEmbeddedDrmCertificate(
reinterpret_cast<uint8_t*>(&embedded_certificate.front()),
&certificate_length);
});
}
if (status == OEMCrypto_SUCCESS) {
// TODO: b/312782308 - Store embedded certificate as SignedDrmCertificate
video_widevine_client::sdk::HashedFile hashed_file;
video_widevine_client::sdk::File file;
embedded_certificate.resize(certificate_length);
if (hashed_file.ParseFromString(embedded_certificate) &&
file.ParseFromString(hashed_file.file())) {
*token = file.device_certificate().certificate();
return CdmResponseType(NO_ERROR);
}
}
token->clear();
return MapOEMCryptoResult(status, GET_TOKEN_FROM_EMBEDDED_CERT_ERROR,
"GetTokenFromEmbeddedCertificate");
}
CdmResponseType CryptoSession::GetDeviceInformation(
RequestedSecurityLevel requested_security_level, std::string* device_info) {
RETURN_IF_NULL(device_info, PARAMETER_NULL);
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
if (GetSecurityLevel(requested_security_level) != kSecurityLevelL1) {
LOGE("CDM only supports L1 device_info");
return CdmResponseType(NOT_IMPLEMENTED_ERROR);
}
CdmClientTokenType token_type = kClientTokenUninitialized;
const CdmResponseType status =
GetProvisioningMethod(requested_security_level, &token_type);
if (status != NO_ERROR) {
LOGE("Failed to get token type");
return status;
}
if (token_type != kClientTokenBootCertChain) {
return CdmResponseType(
PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR);
}
size_t device_info_length = 0;
OEMCryptoResult sts = WithOecReadLock("GetDeviceInformation Attempt 1", [&] {
return OEMCrypto_GetDeviceInformation(nullptr, &device_info_length);
});
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
device_info->resize(device_info_length);
sts = WithOecReadLock("GetDeviceInformation Attempt 2", [&] {
return OEMCrypto_GetDeviceInformation(
MutableStringDataPointer(device_info), &device_info_length);
});
}
if (sts != OEMCrypto_SUCCESS) {
LOGE("OEMCrypto_GetDeviceInformation failed: status = %d",
static_cast<int>(sts));
device_info->clear();
return MapOEMCryptoResult(sts, GET_DEVICE_INFORMATION_ERROR,
"GetDeviceInformation");
}
device_info->resize(device_info_length);
return CdmResponseType(NO_ERROR);
}
CdmResponseType CryptoSession::GetDeviceSignedCsrPayload(
RequestedSecurityLevel requested_security_level,
const std::string& challenge, const std::string& device_info,
std::string* signed_csr_payload) {
RETURN_IF_NULL(signed_csr_payload, PARAMETER_NULL);
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
if (GetSecurityLevel(requested_security_level) != kSecurityLevelL1) {
LOGE("CDM only supports L1 CSR payload");
return CdmResponseType(NOT_IMPLEMENTED_ERROR);
}
CdmClientTokenType token_type = kClientTokenUninitialized;
const CdmResponseType status =
GetProvisioningMethod(requested_security_level, &token_type);
if (status != NO_ERROR) {
LOGE("Failed to get token type");
return status;
}
if (token_type != kClientTokenBootCertChain) {
return CdmResponseType(
PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR);
}
size_t signed_csr_payload_length = 0;
OEMCryptoResult sts =
WithOecReadLock("GetDeviceSignedCsrPayload Attempt 1", [&] {
return OEMCrypto_GetDeviceSignedCsrPayload(
reinterpret_cast<const uint8_t*>(challenge.data()),
challenge.size(),
reinterpret_cast<const uint8_t*>(device_info.data()),
device_info.size(), nullptr, &signed_csr_payload_length);
});
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
signed_csr_payload->resize(signed_csr_payload_length);
sts = WithOecReadLock("GetDeviceSignedCsrPayload Attempt 2", [&] {
return OEMCrypto_GetDeviceSignedCsrPayload(
reinterpret_cast<const uint8_t*>(challenge.data()), challenge.size(),
reinterpret_cast<const uint8_t*>(device_info.data()),
device_info.size(), MutableStringDataPointer(signed_csr_payload),
&signed_csr_payload_length);
});
}
if (sts != OEMCrypto_SUCCESS) {
LOGE("OEMCrypto_GetDeviceSignedCsrPayload failed: status = %d",
static_cast<int>(sts));
signed_csr_payload->clear();
return MapOEMCryptoResult(sts, GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR,
"GetDeviceSignedCsrPayload");
}
signed_csr_payload->resize(signed_csr_payload_length);
return CdmResponseType(NO_ERROR);
}
CdmResponseType CryptoSession::GenerateCertificateKeyPair(
std::string* public_key, std::string* public_key_signature,
std::string* wrapped_private_key, CryptoWrappedKey::Type* key_type) {
@@ -1513,6 +1696,35 @@ CdmResponseType CryptoSession::SelectKey(const std::string& key_id,
return key_session_->SelectKey(key_id, cipher_mode);
});
if (sts != last_select_key_error_) {
// Only log errors on first occurrence. Calls to SelectKey from
// the app are typically queued. If an error occurs, then it is
// expected that multiple failures will occur before the app is
// notified and stops queueing calls to the CDM.
last_select_key_error_ = sts;
if (sts == OEMCrypto_ERROR_INSUFFICIENT_HDCP ||
sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) {
const char* status_message = OemCryptoResultToString(sts);
HdcpCapability current_hdcp_level;
HdcpCapability max_hdcp_level;
const CdmResponseType hdcp_status =
GetHdcpCapabilities(&current_hdcp_level, &max_hdcp_level);
const char* current_hdcp_message = kUnavailableHdcpLevel;
const char* max_hdcp_message = kUnavailableHdcpLevel;
if (hdcp_status == NO_ERROR) {
current_hdcp_message = HdcpCapabilityToString(current_hdcp_level);
max_hdcp_message = HdcpCapabilityToString(max_hdcp_level);
}
LOGE(
"oec_session_id = %u, security_level = %s, "
"status = %s, current_hdcp_level = %s, "
"max_hdcp_level = %s",
oec_session_id_,
RequestedSecurityLevelToString(requested_security_level_),
status_message, current_hdcp_message, max_hdcp_message);
}
}
switch (sts) {
// SelectKey errors.
case OEMCrypto_SUCCESS:
@@ -1781,8 +1993,8 @@ CdmResponseType CryptoSession::Decrypt(
}
// Perform decrypt
const OEMCryptoResult sts =
DecryptMultipleSamples(oec_samples, params.cipher_mode, oec_pattern);
const OEMCryptoResult sts = DecryptMultipleSamples(
oec_samples, params.cipher_mode, oec_pattern, is_any_sample_protected);
if (sts != OEMCrypto_SUCCESS && last_decrypt_error_ != sts) {
// Decrypt errors and warnings are only logged when the error code
@@ -1791,11 +2003,26 @@ CdmResponseType CryptoSession::Decrypt(
// the next call. The calling application may make several more
// decrypt requests before the error is handled by the app.
last_decrypt_error_ = sts;
if (sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) {
LOGW(
"OEMCrypto_DecryptCENC is warning of mixed HDCP output protection: "
"oec_session_id = %u",
oec_session_id_);
if (sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION ||
sts == OEMCrypto_ERROR_INSUFFICIENT_HDCP) {
const char* status_message = OemCryptoResultToString(sts);
HdcpCapability current_hdcp_level;
HdcpCapability max_hdcp_level;
const CdmResponseType hdcp_status =
GetHdcpCapabilities(&current_hdcp_level, &max_hdcp_level);
const char* current_hdcp_message = kUnavailableHdcpLevel;
const char* max_hdcp_message = kUnavailableHdcpLevel;
if (hdcp_status == NO_ERROR) {
current_hdcp_message = HdcpCapabilityToString(current_hdcp_level);
max_hdcp_message = HdcpCapabilityToString(max_hdcp_level);
}
LOGE(
"OEMCrypto_DecryptCENC failed: oec_session_id = %u, "
"security_level = %s, status = %s, current_hdcp_level = %s, "
"max_hdcp_level = %s",
oec_session_id_,
RequestedSecurityLevelToString(requested_security_level_),
status_message, current_hdcp_message, max_hdcp_message);
} else {
LOGE(
"OEMCrypto_DecryptCENC failed: oec_session_id = %u, "
@@ -2914,22 +3141,24 @@ bool CryptoSession::GetAnalogOutputCapabilities(bool* can_support_output,
OEMCryptoResult CryptoSession::DecryptMultipleSamples(
const std::vector<OEMCrypto_SampleDescription>& samples,
CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
CdmCipherMode cipher_mode, const OEMCrypto_CENCEncryptPatternDesc& pattern,
bool is_any_subsample_protected) {
OEMCryptoResult sts = OEMCrypto_ERROR_BUFFER_TOO_LARGE;
// If there's only one sample, automatically fall through to avoid a redundant
// roundtrip through OEMCrypto_DecryptCENC()
if (samples.size() > 1) {
WithOecSessionLock("DecryptMultipleSamples", [&] {
sts = key_session_->Decrypt(samples.data(), samples.size(), pattern);
sts = key_session_->Decrypt(samples.data(), samples.size(), pattern,
is_any_subsample_protected);
});
}
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
// Fall back to sending each sample individually
for (const OEMCrypto_SampleDescription& sample : samples) {
sts = DecryptSample(sample, cipher_mode, pattern);
sts = DecryptSample(sample, cipher_mode, pattern,
is_any_subsample_protected);
if (sts != OEMCrypto_SUCCESS) break;
}
}
@@ -2939,7 +3168,8 @@ OEMCryptoResult CryptoSession::DecryptMultipleSamples(
OEMCryptoResult CryptoSession::DecryptSample(
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
const OEMCrypto_CENCEncryptPatternDesc& pattern,
bool is_any_subsample_protected) {
OEMCryptoResult sts = OEMCrypto_ERROR_BUFFER_TOO_LARGE;
// If there's only one subsample and it contains only one type of region,
@@ -2949,7 +3179,8 @@ OEMCryptoResult CryptoSession::DecryptSample(
(sample.subsamples[0].num_bytes_clear > 0 &&
sample.subsamples[0].num_bytes_encrypted > 0)) {
WithOecSessionLock("DecryptSample", [&] {
sts = key_session_->Decrypt(&sample, 1, pattern);
sts = key_session_->Decrypt(&sample, 1, pattern,
is_any_subsample_protected);
});
}
@@ -2981,7 +3212,8 @@ OEMCryptoResult CryptoSession::DecryptSample(
fake_sample.subsamples = &clear_subsample;
fake_sample.subsamples_length = 1;
sts = LegacyDecrypt(fake_sample, cipher_mode, pattern);
sts = LegacyDecrypt(fake_sample, cipher_mode, pattern,
is_any_subsample_protected);
if (sts != OEMCrypto_SUCCESS) break;
fake_sample.buffers.input_data += length;
@@ -3007,7 +3239,8 @@ OEMCryptoResult CryptoSession::DecryptSample(
fake_sample.subsamples = &encrypted_subsample;
fake_sample.subsamples_length = 1;
sts = LegacyDecrypt(fake_sample, cipher_mode, pattern);
sts = LegacyDecrypt(fake_sample, cipher_mode, pattern,
is_any_subsample_protected);
if (sts != OEMCrypto_SUCCESS) break;
fake_sample.buffers.input_data += length;
@@ -3026,7 +3259,8 @@ OEMCryptoResult CryptoSession::DecryptSample(
OEMCryptoResult CryptoSession::LegacyDecrypt(
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
const OEMCrypto_CENCEncryptPatternDesc& pattern,
bool is_any_subsample_protected) {
const size_t max_chunk_size = GetMaxSubsampleRegionSize();
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
@@ -3056,7 +3290,8 @@ OEMCryptoResult CryptoSession::LegacyDecrypt(
}
if (is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
WithOecSessionLock("LegacyDecrypt() calling key_session_->Decrypt()", [&] {
sts = key_session_->Decrypt(&sample, 1, pattern);
sts = key_session_->Decrypt(&sample, 1, pattern,
is_any_subsample_protected);
});
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
@@ -3072,7 +3307,8 @@ OEMCryptoResult CryptoSession::LegacyDecrypt(
: max_chunk_size;
if (sample.buffers.input_data_length > chunk_size) {
sts = LegacyDecryptInChunks(sample, cipher_mode, pattern, chunk_size);
sts = LegacyDecryptInChunks(sample, cipher_mode, pattern, chunk_size,
is_any_subsample_protected);
}
}
}
@@ -3121,7 +3357,8 @@ OEMCryptoResult CryptoSession::LegacyCopyBufferInChunks(
OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size) {
const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size,
bool is_any_subsample_protected) {
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
const bool is_protected = (subsample.num_bytes_encrypted > 0);
@@ -3155,7 +3392,8 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
// pattern length long, which is also guaranteed to be an exact number
// of AES blocks long.
WithOecSessionLock("LegacyDecryptInChunks", [&] {
sts = key_session_->Decrypt(&fake_sample, 1, pattern);
sts = key_session_->Decrypt(&fake_sample, 1, pattern,
is_any_subsample_protected);
});
if (sts != OEMCrypto_SUCCESS) break;

View File

@@ -20,6 +20,10 @@ message NameValue {
optional string value = 2;
}
message SavedStorage {
map<string, string> files = 1;
}
message OemCertificate {
enum PrivateKeyType {
RSA = 0;

View File

@@ -195,9 +195,7 @@ std::vector<CryptoKey> ExtractContentKeys(const License& license) {
} // namespace
CdmLicense::CdmLicense(const CdmSessionId& session_id)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
: session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
@@ -206,15 +204,17 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
license_key_type_(kLicenseKeyTypeContent) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
: session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
clock_(clock),
license_key_type_(kLicenseKeyTypeContent) {
clock_.reset(clock);
if (!clock_) {
LOGW("Input |clock| is null, using default");
clock_.reset(new wvutil::Clock());
}
}
CdmLicense::~CdmLicense() {}

View File

@@ -316,6 +316,7 @@ void LicenseKeyStatus::ParseContentKey(const KeyContainer& key,
if (key.has_required_protection()) {
default_hdcp_level_ =
ProtobufHdcpToOemCryptoHdcp(key.required_protection().hdcp());
last_reported_license_hdcp_level_ = default_hdcp_level_;
}
}
@@ -399,14 +400,36 @@ void LicenseKeyStatus::ApplyConstraints(
}
}
const bool device_hdcp_level_change =
(last_reported_device_hdcp_level_ != new_hdcp_level);
last_reported_device_hdcp_level_ = new_hdcp_level;
CryptoSession::HdcpCapability desired_hdcp_level = default_hdcp_level_;
if (current_constraint && current_constraint->has_required_protection()) {
desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp(
current_constraint->required_protection().hdcp());
}
const bool license_hdcp_level_change =
(desired_hdcp_level != last_reported_license_hdcp_level_);
last_reported_license_hdcp_level_ = desired_hdcp_level;
meets_constraints_ =
const bool meets_new_constrains =
MeetsHdcpRequirements(desired_hdcp_level, new_hdcp_level);
const bool meets_constraints_change =
(meets_new_constrains != meets_constraints_);
meets_constraints_ = meets_new_constrains;
const bool change = device_hdcp_level_change || license_hdcp_level_change ||
meets_constraints_change;
if (!meets_constraints_ && change) {
LOGD(
"new_hdcp_level = %s, desired_hdcp_level = %s, "
"default_hdcp_level = %s, video_pixels = %u",
CryptoSession::HdcpCapabilityToString(new_hdcp_level),
CryptoSession::HdcpCapabilityToString(desired_hdcp_level),
CryptoSession::HdcpCapabilityToString(default_hdcp_level_),
video_pixels);
}
}
void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) {

View File

@@ -1026,10 +1026,21 @@ message SignedProvisioningMessage {
ARCPP_PROVISIONING = 4; // ChromeOS/Arc++ devices.
// Android-Attestation-based OTA keyboxes.
ANDROID_ATTESTATION_KEYBOX_OTA = 6;
// DRM certificate reprovisioning for individualization of embedded
// DRM certificates used by internal L3 CDMs only.
DRM_REPROVISIONING = 7;
INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol.
INTEL_SIGMA_210 = 210; // Intel Sigma 2.1.0 protocol.
}
// Used by provisioning 4.0 to deliver cast certificates in which the server
// delivers a new rsa private key that must be encrypted and signed.
enum SessionKeyType {
UNDEFINED = 0;
WRAPPED_AES_KEY = 1;
EPHEMERAL_ECC_PUBLIC_KEY = 2;
}
// 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, 4.0 and ARCPP_PROVISIONING. Required.
@@ -1056,6 +1067,21 @@ message SignedProvisioningMessage {
optional HashAlgorithmProto hash_algorithm = 7;
// Indicates which version of the protocol is in use.
optional ProvisioningProtocolVersion protocol_version = 8;
// 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
// EPHEMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1
// serialized ECC public key.
// This field is only required to be set in a success response to
// Provisioning 4.0 X509 (cast) certificate request.
optional bytes session_key = 9;
// Optional field that contains the algorithm type used to generate the
// session_key and signature in a ProvisioningResponse message. This value is
// populated in a success response to a request for a X509 (cast) certificate.
// The value used depends on the key type of the PublicKeyToCertify contained
// in Provisioning 4.0 ProvisioningMessage.
// This value must be populated if session_key is populated.
optional SessionKeyType session_key_type = 10;
}
// ----------------------------------------------------------------------------
@@ -1247,6 +1273,10 @@ message DrmCertificate {
DEVICE = 2;
SERVICE = 3;
PROVISIONER = 4;
// Only used by internal L3 CDMs with baked-in (embedded) certificates that
// support the Drm Reprovisioning method for individualization of embedded
// certificates.
DEVICE_EMBEDDED = 5;
}
enum ServiceType {
UNKNOWN_SERVICE_TYPE = 0;

View File

@@ -41,10 +41,12 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox(
}
OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) {
(void)count;
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool count) {
OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow) {
(void)allow;
return OEMCrypto_SUCCESS;
}
@@ -54,7 +56,8 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength, RequestedSecurityLevel) {
size_t keyBoxLength,
RequestedSecurityLevel) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
@@ -104,28 +107,29 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(RequestedSecurityLevel,
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
}
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(RequestedSecurityLevel) {
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
RequestedSecurityLevel) {
return ::OEMCrypto_GetProvisioningMethod();
}
uint32_t OEMCrypto_SupportedCertificates(RequestedSecurityLevel level) {
uint32_t OEMCrypto_SupportedCertificates(RequestedSecurityLevel) {
return ::OEMCrypto_SupportedCertificates();
}
OEMCryptoResult OEMCrypto_CreateUsageTableHeader(RequestedSecurityLevel level,
OEMCryptoResult OEMCrypto_CreateUsageTableHeader(RequestedSecurityLevel,
uint8_t* header_buffer,
size_t* header_buffer_length) {
return ::OEMCrypto_CreateUsageTableHeader(header_buffer,
header_buffer_length);
}
OEMCryptoResult OEMCrypto_LoadUsageTableHeader(RequestedSecurityLevel level,
OEMCryptoResult OEMCrypto_LoadUsageTableHeader(RequestedSecurityLevel,
const uint8_t* buffer,
size_t buffer_length) {
return ::OEMCrypto_LoadUsageTableHeader(buffer, buffer_length);
}
OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(RequestedSecurityLevel level,
OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(RequestedSecurityLevel,
uint32_t new_table_size,
uint8_t* header_buffer,
size_t* header_buffer_length) {
@@ -174,46 +178,46 @@ OEMCryptoResult OEMCrypto_ProductionReady(RequestedSecurityLevel) {
}
OEMCryptoResult OEMCrypto_DecryptCENC(
RequestedSecurityLevel level, const uint8_t* key_handle,
size_t key_handle_length, const OEMCrypto_SampleDescription* samples,
size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) {
RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length,
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
return OEMCrypto_DecryptCENC(key_handle, key_handle_length, samples,
samples_length, pattern);
}
OEMCryptoResult OEMCrypto_Generic_Encrypt(
RequestedSecurityLevel level, const uint8_t* key_handle,
size_t key_handle_length, const OEMCrypto_SharedMemory* in_buffer,
size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm,
RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length,
const OEMCrypto_SharedMemory* in_buffer, size_t in_buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm,
OEMCrypto_SharedMemory* out_buffer) {
return OEMCrypto_Generic_Encrypt(key_handle, key_handle_length, in_buffer,
in_buffer_length, iv, algorithm, out_buffer);
}
OEMCryptoResult OEMCrypto_Generic_Decrypt(
RequestedSecurityLevel level, const uint8_t* key_handle,
size_t key_handle_length, const OEMCrypto_SharedMemory* in_buffer,
size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm,
RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length,
const OEMCrypto_SharedMemory* in_buffer, size_t in_buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm,
OEMCrypto_SharedMemory* out_buffer) {
return OEMCrypto_Generic_Decrypt(key_handle, key_handle_length, in_buffer,
in_buffer_length, iv, algorithm, out_buffer);
}
OEMCryptoResult OEMCrypto_Generic_Sign(
RequestedSecurityLevel level, const uint8_t* key_handle,
size_t key_handle_length, const OEMCrypto_SharedMemory* buffer,
size_t buffer_length, OEMCrypto_Algorithm algorithm,
OEMCrypto_SharedMemory* signature, size_t* signature_length) {
RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length,
const OEMCrypto_SharedMemory* buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* signature,
size_t* signature_length) {
return OEMCrypto_Generic_Sign(key_handle, key_handle_length, buffer,
buffer_length, algorithm, signature,
signature_length);
}
OEMCryptoResult OEMCrypto_Generic_Verify(
RequestedSecurityLevel level, const uint8_t* key_handle,
size_t key_handle_length, const OEMCrypto_SharedMemory* buffer,
size_t buffer_length, OEMCrypto_Algorithm algorithm,
const OEMCrypto_SharedMemory* signature, size_t signature_length) {
RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length,
const OEMCrypto_SharedMemory* buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm, const OEMCrypto_SharedMemory* signature,
size_t signature_length) {
return OEMCrypto_Generic_Verify(key_handle, key_handle_length, buffer,
buffer_length, algorithm, signature,
signature_length);

View File

@@ -0,0 +1,12 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "OEMCryptoCENC.h"
OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate(
uint8_t* public_cert, size_t* public_cert_length) {
(void)public_cert;
(void)public_cert_length;
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -7,6 +7,10 @@
extern "C" OEMCryptoResult OEMCrypto_GenerateOTARequest(
OEMCrypto_SESSION session, uint8_t* buffer, size_t* buffer_length,
uint32_t use_test_key) {
(void)session;
(void)buffer;
(void)buffer_length;
(void)use_test_key;
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
@@ -14,5 +18,9 @@ extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session,
const uint8_t* buffer,
size_t buffer_length,
uint32_t use_test_key) {
(void)session;
(void)buffer;
(void)buffer_length;
(void)use_test_key;
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -39,9 +39,12 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
if (version >= kMinOemCryptoApiVersionSupportsRenewalDelayBase) {
policy_timers_.reset(new PolicyTimersV18());
}
} else {
LOGW("Failed to get API version: session_id = %s", IdToString(session_id));
}
if (policy_timers_ == nullptr) {
if (!policy_timers_) {
// Use V16 policy timers if getting version failed.
policy_timers_.reset(new PolicyTimersV16());
}
InitDevice(crypto_session);

View File

@@ -268,11 +268,10 @@ bool AesCbcKey::Init(const std::string& key) {
return true;
}
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
std::string* iv) {
bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
std::string* out) {
assert(!in.empty());
assert(iv != nullptr);
assert(iv->size() == kCCBlockSizeAES128);
assert(iv.size() == kCCBlockSizeAES128);
assert(out != nullptr);
assert(!key_.empty());
@@ -281,7 +280,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
size_t length;
CCCryptorStatus result =
CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
key_.c_str(), key_.length(), iv->c_str(), in.c_str(), in.length(),
key_.c_str(), key_.length(), iv.c_str(), in.c_str(), in.length(),
&temp[0], temp.size(), &length);
if (result != kCCSuccess) {

View File

@@ -89,18 +89,14 @@ bool AesCbcKey::Init(const std::string& key) {
return true;
}
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
std::string* iv) {
bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
std::string* out) {
if (in.empty()) {
LOGE("No cleartext provided");
return false;
}
if (iv == nullptr) {
LOGE("Initialization vector output parameter |iv| not provided");
return false;
}
if (iv->size() != AES_BLOCK_SIZE) {
LOGE("Invalid IV size: %zu", iv->size());
if (iv.size() != AES_BLOCK_SIZE) {
LOGE("Invalid IV size: %zu", iv.size());
return false;
}
if (out == nullptr) {
@@ -114,8 +110,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new();
if (EVP_EncryptInit(evp_cipher_ctx, EVP_aes_128_cbc(),
reinterpret_cast<uint8_t*>(&key_[0]),
reinterpret_cast<uint8_t*>(&(*iv)[0])) == 0) {
reinterpret_cast<const uint8_t*>(&key_[0]),
reinterpret_cast<const uint8_t*>(&iv[0])) == 0) {
LOGE("AES CBC setup failure: %s",
ERR_error_string(ERR_get_error(), nullptr));
EVP_CIPHER_CTX_free(evp_cipher_ctx);
@@ -124,10 +120,10 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
out->resize(in.size() + AES_BLOCK_SIZE);
int out_length = static_cast<int>(out->size());
if (EVP_EncryptUpdate(
evp_cipher_ctx, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(in.data())),
static_cast<int>(in.size())) == 0) {
if (EVP_EncryptUpdate(evp_cipher_ctx, reinterpret_cast<uint8_t*>(&(*out)[0]),
&out_length,
reinterpret_cast<const uint8_t*>(in.data()),
static_cast<int>(in.size())) == 0) {
LOGE("AES CBC encryption failure: %s",
ERR_error_string(ERR_get_error(), nullptr));
EVP_CIPHER_CTX_free(evp_cipher_ctx);

View File

@@ -29,8 +29,8 @@ AesCbcKey::~AesCbcKey() {}
bool AesCbcKey::Init(const std::string& key) { return false; }
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
std::string* iv) {
bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv,
std::string* out) {
return false;
}

View File

@@ -251,7 +251,7 @@ CdmResponseType ServiceCertificate::EncryptClientId(
AesCbcKey aes;
if (!aes.Init(key)) return CdmResponseType(CLIENT_ID_AES_INIT_ERROR);
if (!aes.Encrypt(id, &enc_id, &iv))
if (!aes.Encrypt(id, iv, &enc_id))
return CdmResponseType(CLIENT_ID_AES_ENCRYPT_ERROR);
CdmResponseType encrypt_result = EncryptRsaOaep(key, &enc_key);

View File

@@ -59,6 +59,8 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
bool success = false;
switch (type) {
case kClientTokenDrmCert:
// TODO: b/309675153 - Extract system id when using DRM reprovisioning.
case kClientTokenDrmCertificateReprovisioning:
LOGW(
"Cannot get a system ID from a DRM certificate, "
"using null system ID: security_level = %s",

View File

@@ -12,6 +12,8 @@ namespace wvcdm {
namespace {
const char kEmptyIdRep[] = "<empty>";
const char kNullIdRep[] = "<null>";
const char kFalseRep[] = "false";
const char kTrueRep[] = "true";
// Thread local buffer used by UnknownEnumValueToString() to represent
// unknown enum values.
@@ -74,6 +76,8 @@ const char* CdmClientTokenTypeToString(CdmClientTokenType type) {
return "BootCertChain";
case kClientTokenUninitialized:
return "Uninitialized";
case kClientTokenDrmCertificateReprovisioning:
return "DrmCertificateReprovisioning";
}
return UnknownValueRep(type);
}
@@ -747,6 +751,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) {
return "GET_TOKEN_FROM_KEYBOX_ERROR";
case KEYBOX_TOKEN_TOO_SHORT:
return "KEYBOX_TOKEN_TOO_SHORT";
case GET_TOKEN_FROM_EMBEDDED_CERT_ERROR:
return "GET_TOKEN_FROM_EMBEDDED_CERT_ERROR";
case EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR:
return "EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR";
case RSA_SIGNATURE_GENERATION_ERROR:
@@ -819,6 +825,10 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) {
return "PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR";
case GET_BOOT_CERTIFICATE_CHAIN_ERROR:
return "GET_BOOT_CERTIFICATE_CHAIN_ERROR";
case GET_DEVICE_INFORMATION_ERROR:
return "GET_DEVICE_INFORMATION_ERROR";
case GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR:
return "GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR";
case GENERATE_CERTIFICATE_KEY_PAIR_ERROR:
return "GENERATE_CERTIFICATE_KEY_PAIR_ERROR";
case GENERATE_CERTIFICATE_KEY_PAIR_UNKNOWN_TYPE_ERROR:
@@ -888,6 +898,8 @@ const char* IdPtrToString(const std::string* id) {
return id->empty() ? kEmptyIdRep : id->c_str();
}
const char* BoolToString(bool value) { return value ? kTrueRep : kFalseRep; }
const char* OemCryptoResultToString(OEMCryptoResult result) {
switch (result) {
/* OEMCrypto return values */