Source release 18.5.0
This commit is contained in:
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(¤t_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(¤t_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;
|
||||
|
||||
@@ -20,6 +20,10 @@ message NameValue {
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
message SavedStorage {
|
||||
map<string, string> files = 1;
|
||||
}
|
||||
|
||||
message OemCertificate {
|
||||
enum PrivateKeyType {
|
||||
RSA = 0;
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
12
core/src/oemcrypto_embedded_cert_stubs.cpp
Normal file
12
core/src/oemcrypto_embedded_cert_stubs.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user