Source release 19.1.0

This commit is contained in:
Matt Feddersen
2024-03-28 19:21:54 -07:00
parent 28ec8548c6
commit b8bdfccebe
182 changed files with 10645 additions and 2040 deletions

View File

@@ -95,7 +95,9 @@ class UsagePropertySet : public CdmClientPropertySet {
CdmEngine::CdmEngine(wvutil::FileSystem* file_system,
std::shared_ptr<metrics::EngineMetrics> metrics)
: metrics_(metrics), file_system_(file_system), spoid_(EMPTY_SPOID) {
: metrics_(std::move(metrics)),
file_system_(file_system),
spoid_(EMPTY_SPOID) {
assert(file_system);
Properties::Init();
}
@@ -712,7 +714,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
LOGW("GetWVCdmVersion failed");
return CdmResponseType(UNKNOWN_ERROR);
}
*query_response = cdm_version;
*query_response = std::move(cdm_version);
return CdmResponseType(NO_ERROR);
}
if (query_token == QUERY_KEY_RESOURCE_RATING_TIER) {
@@ -934,7 +936,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
metrics_->GetCryptoMetrics()
->crypto_session_get_device_unique_id_.Increment(status);
if (status != NO_ERROR) return status;
*query_response = device_id;
*query_response = std::move(device_id);
return CdmResponseType(NO_ERROR);
}
if (query_token == QUERY_KEY_PROVISIONING_ID) {
@@ -944,7 +946,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
LOGW("GetProvisioningId failed: status = %d", static_cast<int>(status));
return status;
}
*query_response = provisioning_id;
*query_response = std::move(provisioning_id);
return CdmResponseType(NO_ERROR);
}
LOGW("Unknown status requested: query_token = %s", IdToString(query_token));
@@ -1255,11 +1257,24 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
const CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
file_system_, response, cert, wrapped_key);
// Release resources only on success. It is possible that a provisioning
// attempt was made after this one was requested but before the response was
// received, which will cause this attempt to fail. Not releasing will
// allow for the possibility that the later attempt succeeds.
if (NO_ERROR == ret) cert_provisioning_.reset();
if (NO_ERROR == ret) {
// Release resources on success.
cert_provisioning_.reset();
} else if (DEVICE_REVOKED == ret) {
// If a device is revoked, future attempts will likely fail.
// Caller may attempt changing security level to recover.
LOGE("Device has been revoked, cannot provision: status = %s",
ret.ToString().c_str());
cert_provisioning_.reset();
} else {
// It is possible that a provisioning attempt was made after this one was
// requested but before the response was received, which will cause this
// attempt to fail. Not releasing will allow for the possibility that the
// later attempt succeeds.
LOGW("Provisioning failed, app may try again: status = %s",
ret.ToString().c_str());
}
return ret;
}

View File

@@ -70,7 +70,7 @@ int DrmKeyTypeToMetricValue(CryptoWrappedKey::Type type) {
CdmSession::CdmSession(wvutil::FileSystem* file_system,
std::shared_ptr<metrics::SessionMetrics> metrics)
: metrics_(metrics),
: metrics_(std::move(metrics)),
initialized_(false),
closed_(true),
file_handle_(new DeviceFiles(file_system)),
@@ -81,7 +81,9 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_usage_update_(true),
is_usage_update_needed_(false) {
is_usage_update_needed_(false),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
@@ -864,11 +866,19 @@ 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();
CdmResponseType sts = ResetCryptoSession();
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();
}
if (usage_table_ == nullptr) {
LOGE("Usage table header unavailable");
return CdmResponseType(INCORRECT_USAGE_SUPPORT_TYPE_1);
@@ -1002,8 +1012,14 @@ bool CdmSession::StoreLicense(CdmOfflineLicenseState state, int* error_detail) {
}
CdmResponseType CdmSession::RemoveKeys() {
crypto_session_->Close();
return ResetCryptoSession();
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);
}
CdmResponseType CdmSession::RemoveLicense() {
@@ -1297,77 +1313,6 @@ CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message,
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
void CdmSession::set_license_parser(CdmLicense* license_parser) {
@@ -1377,7 +1322,6 @@ 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

@@ -261,6 +261,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
status = crypto_session_->PrepareAndSignProvisioningRequest(
serialized_message, &core_message, &request_signature,
should_specify_algorithm, oec_algorithm);
request_ = serialized_message;
if (status != NO_ERROR) {
LOGE("Failed to prepare provisioning request: status = %d",
@@ -488,6 +489,7 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
} else {
*request = std::move(serialized_request);
}
request_ = std::move(serialized_message);
return CdmResponseType(NO_ERROR);
}
@@ -574,20 +576,14 @@ CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
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());
status = crypto_session_->LoadProvisioningCast(
signed_response.session_key(), provisioning_request_message_,
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;
@@ -727,14 +723,26 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
}
CryptoWrappedKey private_key;
const CdmResponseType status = crypto_session_->LoadProvisioning(
signed_message, core_message, signature, &private_key.key());
// TODO(b/316053127): clean this up a bit.
if (cert_type_ == kCertificateX509) {
const std::string dummy_key;
const CdmResponseType status = crypto_session_->LoadProvisioningCast(
dummy_key, request_, signed_message, core_message, signature,
&private_key.key());
if (status != NO_ERROR) {
LOGE("LoadProvisioning failed: status = %d", static_cast<int>(status));
return status;
if (status != NO_ERROR) {
LOGE("LoadProvisioning failed: status = %d", static_cast<int>(status));
return status;
}
} else {
const CdmResponseType status = crypto_session_->LoadProvisioning(
request_, signed_message, core_message, signature, &private_key.key());
if (status != NO_ERROR) {
LOGE("LoadProvisioning failed: status = %d", static_cast<int>(status));
return status;
}
}
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
CloseSession();

View File

@@ -380,10 +380,30 @@ bool ClientIdentification::GetProvisioningTokenType(
*token_type =
video_widevine::ClientIdentification::OEM_DEVICE_CERTIFICATE;
return true;
case kClientTokenBootCertChain:
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
case kClientTokenBootCertChain: {
OEMCrypto_BCCType bcc_type;
const CdmResponseType result =
crypto_session_->GetProvisioning40TokenType(&bcc_type);
if (result == NOT_IMPLEMENTED_ERROR) {
// Default to CBOR BCC for OEMCrypto that doesn't support GetBCCType().
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
return true;
}
if (result != NO_ERROR) return false;
if (bcc_type == OEMCrypto_CBOR) {
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
} else if (bcc_type == OEMCrypto_X509) {
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN_X509;
} else {
// shouldn't happen
LOGE("Unexpected BCC type: %d", static_cast<int>(bcc_type));
return false;
}
return true;
}
case kClientTokenDrmCertificateReprovisioning:
*token_type =
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;

View File

@@ -11,59 +11,6 @@
namespace wvcdm {
// Generate Derived Keys for ContentKeySession
OEMCryptoResult ContentKeySession::GenerateDerivedKeys(
const std::string& message) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("Generating derived keys: id = %u", oec_session_id_);
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_GenerateDerivedKeys(
oec_session_id_,
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_generate_derived_keys_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_GenerateDerivedKeys failed: status = %d",
static_cast<int>(sts));
}
return sts;
}
// Generate Derived Keys (from session key) for ContentKeySession
OEMCryptoResult ContentKeySession::GenerateDerivedKeys(
const std::string& message, const std::string& session_key) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("Generating derived keys from session key: id = %u", oec_session_id_);
OEMCryptoResult sts;
M_TIME(
sts = OEMCrypto_DeriveKeysFromSessionKey(
oec_session_id_, reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_DeriveKeysFromSessionKey failed: status = %d",
static_cast<int>(sts));
}
return sts;
}
// Load Keys for ContentKeySession
OEMCryptoResult ContentKeySession::LoadKeys(
const std::string& message, const std::string& signature,

View File

@@ -248,38 +248,6 @@ OEMCrypto_Substring GetSubstring(const std::string& message,
return substring;
}
void GenerateMacContext(const std::string& input_context,
std::string* deriv_context) {
if (!deriv_context) {
LOGE("Output parameter |deriv_context| not provided");
return;
}
const std::string kSigningKeyLabel = "AUTHENTICATION";
const size_t kSigningKeySizeBits = wvcdm::MAC_KEY_SIZE * 8;
deriv_context->assign(kSigningKeyLabel);
deriv_context->append(1, '\0');
deriv_context->append(input_context);
deriv_context->append(wvutil::EncodeUint32(kSigningKeySizeBits * 2));
}
void GenerateEncryptContext(const std::string& input_context,
std::string* deriv_context) {
if (!deriv_context) {
LOGE("Output parameter |deriv_context| not provided");
return;
}
const std::string kEncryptionKeyLabel = "ENCRYPTION";
const size_t kEncryptionKeySizeBits = wvcdm::CONTENT_KEY_SIZE * 8;
deriv_context->assign(kEncryptionKeyLabel);
deriv_context->append(1, '\0');
deriv_context->append(input_context);
deriv_context->append(wvutil::EncodeUint32(kEncryptionKeySizeBits));
}
OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) {
return cipher_mode == kCipherModeCtr ? OEMCrypto_CipherMode_CENC
: OEMCrypto_CipherMode_CBCS;
@@ -696,6 +664,24 @@ CdmResponseType CryptoSession::GetProvisioningToken(
return status;
}
CdmResponseType CryptoSession::GetProvisioning40TokenType(
OEMCrypto_BCCType* bcc_type) {
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
return GetProvisioning40TokenType(requested_security_level_, bcc_type);
}
CdmResponseType CryptoSession::GetProvisioning40TokenType(
RequestedSecurityLevel requested_security_level,
OEMCrypto_BCCType* bcc_type) {
RETURN_IF_NULL(bcc_type, PARAMETER_NULL);
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
OEMCryptoResult sts = WithOecReadLock("GetBCCType", [&] {
return OEMCrypto_GetBCCType(requested_security_level, bcc_type);
});
return MapOEMCryptoResult(sts, UNKNOWN_CLIENT_TOKEN_TYPE,
"GetProvisioning40TokenType");
}
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
LOGV("Getting security level");
RETURN_IF_NOT_OPEN(kSecurityLevelUninitialized);
@@ -1104,7 +1090,6 @@ CdmResponseType CryptoSession::PrepareAndSignLicenseRequest(
"PrepareAndSignLicenseRequest");
}
#ifdef HAS_DUAL_KEY
CdmResponseType CryptoSession::UseSecondaryKey(bool dual_key) {
OEMCryptoResult sts;
WithOecSessionLock("UseSecondaryKey", [&] {
@@ -1113,13 +1098,10 @@ CdmResponseType CryptoSession::UseSecondaryKey(bool dual_key) {
return MapOEMCryptoResult(sts, LOAD_KEY_ERROR, "UseSecondaryKey");
}
#else
CdmResponseType CryptoSession::UseSecondaryKey(bool /* dual_key */) {
return CdmResponseType(NO_ERROR);
}
#endif
CdmResponseType CryptoSession::LoadLicense(const std::string& signed_message,
CdmResponseType CryptoSession::LoadLicense(const std::string& context,
const std::string& session_key,
const std::string& signed_message,
const std::string& core_message,
const std::string& signature,
CdmLicenseKeyType key_type) {
@@ -1135,6 +1117,9 @@ CdmResponseType CryptoSession::LoadLicense(const std::string& signed_message,
M_TIME(sts = OEMCrypto_LoadLicense(
oec_session_id_,
reinterpret_cast<const uint8_t*>(context.data()), context.size(),
reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
@@ -1262,8 +1247,6 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
// 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);
if (status != NO_ERROR) return status;
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
should_specify_algorithm = true;
WithOecSessionLock("LoadOEMPrivateKey", [&] {
@@ -1764,26 +1747,6 @@ CdmResponseType CryptoSession::SelectKey(const std::string& key_id,
}
}
CdmResponseType CryptoSession::GenerateDerivedKeys(const std::string& message) {
OEMCryptoResult sts;
WithOecSessionLock("GenerateDerivedKeys without session_key",
[&] { sts = key_session_->GenerateDerivedKeys(message); });
return MapOEMCryptoResult(sts, GENERATE_DERIVED_KEYS_ERROR_2,
"GenerateDerivedKeys");
}
CdmResponseType CryptoSession::GenerateDerivedKeys(
const std::string& message, const std::string& session_key) {
OEMCryptoResult sts;
WithOecSessionLock("GenerateDerivedKeys with session_key", [&] {
sts = key_session_->GenerateDerivedKeys(message, session_key);
});
return MapOEMCryptoResult(sts, GENERATE_DERIVED_KEYS_ERROR,
"GenerateDerivedKeys");
}
CdmResponseType CryptoSession::GenerateRsaSignature(const std::string& message,
std::string* signature,
RSA_Padding_Scheme scheme) {
@@ -2265,8 +2228,9 @@ bool CryptoSession::SetDestinationBufferType() {
}
CdmResponseType CryptoSession::LoadProvisioning(
const std::string& signed_message, const std::string& core_message,
const std::string& signature, std::string* wrapped_private_key) {
const std::string& request, const std::string& signed_message,
const std::string& core_message, const std::string& signature,
std::string* wrapped_private_key) {
LOGV("Loading provisioning certificate: id = %u", oec_session_id_);
if (wrapped_private_key == nullptr) {
LOGE("Missing wrapped |wrapped_private_key|");
@@ -2280,6 +2244,7 @@ CdmResponseType CryptoSession::LoadProvisioning(
WithOecSessionLock("LoadProvisioning Attempt 1", [&] {
M_TIME(status = OEMCrypto_LoadProvisioning(
oec_session_id_,
reinterpret_cast<const uint8_t*>(request.data()), request.size(),
reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
@@ -2297,6 +2262,7 @@ CdmResponseType CryptoSession::LoadProvisioning(
WithOecSessionLock("LoadProvisioning Attempt 2", [&] {
M_TIME(status = OEMCrypto_LoadProvisioning(
oec_session_id_,
reinterpret_cast<const uint8_t*>(request.data()), request.size(),
reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
@@ -2315,6 +2281,64 @@ CdmResponseType CryptoSession::LoadProvisioning(
"LoadProvisioning");
}
CdmResponseType CryptoSession::LoadProvisioningCast(
const std::string& derivation_key, const std::string& request,
const std::string& signed_message, const std::string& core_message,
const std::string& signature, std::string* wrapped_private_key) {
LOGV("Loading provisioning certificate: id = %u", oec_session_id_);
if (wrapped_private_key == nullptr) {
LOGE("Missing wrapped |wrapped_private_key|");
return CdmResponseType(PARAMETER_NULL);
}
const std::string combined_message = core_message + signed_message;
// Round 1, get the size of the wrapped private key buffer.
size_t wrapped_private_key_length = 0;
OEMCryptoResult status;
WithOecSessionLock("LoadProvisioningCast Attempt 1", [&] {
M_TIME(status = OEMCrypto_LoadProvisioningCast(
oec_session_id_,
reinterpret_cast<const uint8_t*>(derivation_key.data()),
derivation_key.size(),
reinterpret_cast<const uint8_t*>(request.data()), request.size(),
reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), nullptr, &wrapped_private_key_length),
metrics_, oemcrypto_load_provisioning_, status);
});
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
return MapOEMCryptoResult(status, LOAD_PROVISIONING_ERROR,
"LoadProvisioningCast");
}
wrapped_private_key->resize(wrapped_private_key_length);
WithOecSessionLock("LoadProvisioningCast Attempt 2", [&] {
M_TIME(status = OEMCrypto_LoadProvisioningCast(
oec_session_id_,
reinterpret_cast<const uint8_t*>(derivation_key.data()),
derivation_key.size(),
reinterpret_cast<const uint8_t*>(request.data()), request.size(),
reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(),
reinterpret_cast<uint8_t*>(&wrapped_private_key->front()),
&wrapped_private_key_length),
metrics_, oemcrypto_load_provisioning_, status);
});
if (status == OEMCrypto_SUCCESS) {
wrapped_private_key->resize(wrapped_private_key_length);
return CdmResponseType(NO_ERROR);
}
wrapped_private_key->clear();
return MapOEMCryptoResult(status, LOAD_PROVISIONING_ERROR,
"LoadProvisioningCast");
}
CdmResponseType CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
HdcpCapability* max) {
LOGV("Getting HDCP capabilities: id = %u", oec_session_id_);
@@ -2625,10 +2649,15 @@ CdmResponseType CryptoSession::SetDecryptHash(uint32_t frame_number,
const std::string& hash) {
LOGV("Setting decrypt hash");
OEMCryptoResult sts;
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
if (hash.size() != sizeof(uint32_t)) {
LOGE("Unsupported hash size: hash_size = %zu, expected = %zu", hash.size(),
sizeof(uint32_t));
return CdmResponseType(UNKNOWN_ERROR);
}
WithOecSessionLock("SetDecryptHash", [&] {
sts = OEMCrypto_SetDecryptHash(
oec_session_id_, frame_number,
reinterpret_cast<const uint8_t*>(hash.data()), hash.size());
const uint32_t crc32 = *reinterpret_cast<const uint32_t*>(hash.data());
sts = OEMCrypto_SetDecryptHash(oec_session_id_, frame_number, crc32);
metrics_->oemcrypto_set_decrypt_hash_.Increment(sts);
});
@@ -3442,11 +3471,6 @@ CdmResponseType CryptoSession::SetDebugIgnoreKeyboxCount(uint32_t count) {
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetDebugIgnoreKeyboxCount");
}
CdmResponseType CryptoSession::SetAllowTestKeybox(bool allow) {
OEMCryptoResult status = OEMCrypto_SetAllowTestKeybox(allow);
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetAllowTestKeybox");
}
okp::SystemFallbackPolicy* CryptoSession::GetOkpFallbackPolicy() {
const auto getter = [&]() -> okp::SystemFallbackPolicy* {
// If not set, then OTA keybox provisioning is not supported or
@@ -3505,40 +3529,40 @@ CdmResponseType CryptoSession::LoadOtaProvisioning(
}
template <class Func>
auto CryptoSession::WithStaticFieldWriteLock(const char* tag, Func body)
-> decltype(body()) {
auto CryptoSession::WithStaticFieldWriteLock(const char* tag,
Func body) -> decltype(body()) {
LOGV("Static field write lock: %s", tag);
std::unique_lock<wvutil::shared_mutex> auto_lock(static_field_mutex_);
return body();
}
template <class Func>
auto CryptoSession::WithStaticFieldReadLock(const char* tag, Func body)
-> decltype(body()) {
auto CryptoSession::WithStaticFieldReadLock(const char* tag,
Func body) -> decltype(body()) {
LOGV("Static field read lock: %s", tag);
wvutil::shared_lock<wvutil::shared_mutex> auto_lock(static_field_mutex_);
return body();
}
template <class Func>
auto CryptoSession::WithOecWriteLock(const char* tag, Func body)
-> decltype(body()) {
auto CryptoSession::WithOecWriteLock(const char* tag,
Func body) -> decltype(body()) {
LOGV("OEMCrypto write lock: %s", tag);
std::unique_lock<wvutil::shared_mutex> auto_lock(oem_crypto_mutex_);
return body();
}
template <class Func>
auto CryptoSession::WithOecReadLock(const char* tag, Func body)
-> decltype(body()) {
auto CryptoSession::WithOecReadLock(const char* tag,
Func body) -> decltype(body()) {
LOGV("OEMCrypto read lock: %s", tag);
wvutil::shared_lock<wvutil::shared_mutex> auto_lock(oem_crypto_mutex_);
return body();
}
template <class Func>
auto CryptoSession::WithOecSessionLock(const char* tag, Func body)
-> decltype(body()) {
auto CryptoSession::WithOecSessionLock(const char* tag,
Func body) -> decltype(body()) {
LOGV("OEMCrypto session lock: %s", tag);
wvutil::shared_lock<wvutil::shared_mutex> oec_auto_lock(oem_crypto_mutex_);
std::unique_lock<std::mutex> session_auto_lock(oem_crypto_session_mutex_);

View File

@@ -115,7 +115,7 @@ OEMCryptoResult EntitlementKeySession::SelectKey(const std::string& key_id,
OEMCrypto_EntitledContentKeyObject EntitlementKeySession::MakeOecEntitledKey(
const CryptoKey& input_key, std::string& message) {
OEMCrypto_EntitledContentKeyObject output_key;
OEMCrypto_EntitledContentKeyObject output_key = {};
message.clear();
const std::string& entitlement_key_id = input_key.entitlement_key_id();

View File

@@ -113,7 +113,8 @@ std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
return key_array;
}
std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> ExtractContentKeys(
const License& license, video_widevine::ProtocolVersion version) {
std::vector<CryptoKey> key_array;
// Extract content key(s)
@@ -130,21 +131,20 @@ std::vector<CryptoKey> ExtractContentKeys(const License& license) {
// TODO(b/232464183): When we switch to License Protocol 2.2, there will
// no longer be padding on these keys, so this
// removal code must be removed at the same time.
if (license.key(i).key().size() !=
CONTENT_KEY_SIZE + LICENSE_PROTOCOL_2_1_PADDING &&
license.key(i).key().size() !=
MAC_KEY_SIZE + LICENSE_PROTOCOL_2_1_PADDING) {
const auto padding = version <= video_widevine::VERSION_2_1
? LICENSE_PROTOCOL_2_1_PADDING
: 0;
if (license.key(i).key().size() != CONTENT_KEY_SIZE + padding &&
license.key(i).key().size() != MAC_KEY_SIZE + padding) {
LOGE(
"Skipping key %s because it is an unexpected size. Expected: %zu "
"or %zu, Actual: %zu",
license.key(i).id().c_str(),
CONTENT_KEY_SIZE + LICENSE_PROTOCOL_2_1_PADDING,
MAC_KEY_SIZE + LICENSE_PROTOCOL_2_1_PADDING,
license.key(i).key().size());
license.key(i).id().c_str(), CONTENT_KEY_SIZE + padding,
MAC_KEY_SIZE + padding, license.key(i).key().size());
continue;
}
const size_t length =
license.key(i).key().size() - LICENSE_PROTOCOL_2_1_PADDING;
license.key(i).key().size() - padding;
key.set_key_data(license.key(i).key().substr(0, length));
key.set_key_data_iv(license.key(i).iv());
if (license.key(i).has_key_control()) {
@@ -195,8 +195,11 @@ std::vector<CryptoKey> ExtractContentKeys(const License& license) {
} // namespace
CdmLicense::CdmLicense(const CdmSessionId& session_id)
: session_id_(session_id),
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
initialized_(false),
protocol_version_(video_widevine::VERSION_2_2),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
@@ -204,17 +207,16 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
license_key_type_(kLicenseKeyTypeContent) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock)
: session_id_(session_id),
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
initialized_(false),
protocol_version_(video_widevine::VERSION_2_2),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
clock_(clock),
license_key_type_(kLicenseKeyTypeContent) {
if (!clock_) {
LOGW("Input |clock| is null, using default");
clock_.reset(new wvutil::Clock());
}
clock_.reset(clock);
}
CdmLicense::~CdmLicense() {}
@@ -244,6 +246,13 @@ bool CdmLicense::Init(bool use_privacy_mode,
return false;
}
uint32_t api_version;
if (!session->GetApiVersion(&api_version)) {
api_version = 16;
}
protocol_version_ = api_version >= 19 ? video_widevine::VERSION_2_2
: video_widevine::VERSION_2_1;
crypto_session_ = session;
policy_engine_ = policy_engine;
use_privacy_mode_ = use_privacy_mode;
@@ -338,7 +347,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return CdmResponseType(LICENSE_REQUEST_NONCE_GENERATION_ERROR);
}
license_request.set_key_control_nonce(license_nonce_);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
license_request.set_protocol_version(protocol_version_);
// License request is complete. Serialize it.
std::string serialized_license_req;
@@ -494,7 +503,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
current_license->set_seconds_since_last_played(seconds_since_last_played);
}
license_request.set_protocol_version(video_widevine::VERSION_2_1);
license_request.set_protocol_version(protocol_version_);
// License request is complete. Serialize it.
std::string serialized_license_req;
@@ -585,10 +594,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
LOGE("Signed response has no session keys present");
return CdmResponseType(SESSION_KEYS_NOT_FOUND);
}
CdmResponseType status = crypto_session_->GenerateDerivedKeys(
key_request_, signed_response.session_key());
if (status != NO_ERROR) return status;
// Extract mac key
std::string mac_key_iv;
@@ -623,7 +628,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement;
std::vector<CryptoKey> key_array = ExtractEntitlementKeys(license);
if (key_array.empty()) {
key_array = ExtractContentKeys(license);
key_array = ExtractContentKeys(license, protocol_version_);
key_type = kLicenseKeyTypeContent;
}
if (key_array.empty()) {
@@ -655,18 +660,19 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
// If the field is not set, it will default to false.
status =
CdmResponseType status =
crypto_session_->UseSecondaryKey(signed_response.using_secondary_key());
if (status != NO_ERROR) return status;
CdmResponseType resp(NO_CONTENT_KEY);
if (kLicenseKeyTypeEntitlement == key_type) {
resp =
HandleEntitlementKeyResponse(is_restore, signed_message, core_message,
signature, key_array, license);
resp = HandleEntitlementKeyResponse(
is_restore, signed_response.session_key(), signed_message, core_message,
signature, key_array, license);
} else if (kLicenseKeyTypeContent == key_type) {
resp = HandleContentKeyResponse(is_restore, signed_message, core_message,
signature, key_array, license);
resp = HandleContentKeyResponse(is_restore, signed_response.session_key(),
signed_message, core_message, signature,
key_array, license);
}
return resp;
}
@@ -797,6 +803,7 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
LOGW("Could not parse original request.");
} else {
license_nonce_ = original_license_request.key_control_nonce();
protocol_version_ = original_license_request.protocol_version();
}
CdmResponseType sts = HandleKeyResponse(true, license_response);
@@ -1026,13 +1033,20 @@ CdmResponseType CdmLicense::PrepareClientId(
license_request->mutable_encrypted_client_id();
status = service_certificate_.EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
if (NO_ERROR == status) {
license_request->clear_client_id();
} else {
if (status != NO_ERROR) {
LOGE("Failed to encrypt client ID: status = %s",
status.ToString().c_str());
license_request->clear_encrypted_client_id();
return status;
}
return status;
license_request->clear_client_id();
}
std::string client_version;
if (Properties::GetWVCdmVersion(&client_version)) {
license_request->set_client_version(std::move(client_version));
}
return CdmResponseType(NO_ERROR);
}
@@ -1079,15 +1093,19 @@ CdmResponseType CdmLicense::PrepareContentId(
}
CdmResponseType CdmLicense::HandleContentKeyResponse(
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::vector<CryptoKey>& key_array,
bool is_restore, const std::string& session_key, const std::string& msg,
const std::string& core_message, const std::string& signature,
const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("No content keys provided");
return CdmResponseType(NO_CONTENT_KEY);
}
const CdmResponseType resp = crypto_session_->LoadLicense(
msg, core_message, signature, kLicenseKeyTypeContent);
protocol_version_ <= video_widevine::VERSION_2_1
? key_request_
: Sha512Hash(key_request_),
session_key, msg, core_message, signature, kLicenseKeyTypeContent);
if (KEY_ADDED == resp) {
loaded_keys_.clear();
for (const CryptoKey& key : key_array) {
@@ -1099,15 +1117,19 @@ CdmResponseType CdmLicense::HandleContentKeyResponse(
}
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::vector<CryptoKey>& key_array,
bool is_restore, const std::string& session_key, const std::string& msg,
const std::string& core_message, const std::string& signature,
const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("No entitlement keys provided");
return CdmResponseType(NO_CONTENT_KEY);
}
const CdmResponseType resp = crypto_session_->LoadLicense(
msg, core_message, signature, kLicenseKeyTypeEntitlement);
protocol_version_ <= video_widevine::VERSION_2_1
? key_request_
: Sha512Hash(key_request_),
session_key, msg, core_message, signature, kLicenseKeyTypeEntitlement);
if (KEY_ADDED != resp) {
return resp;

View File

@@ -1097,7 +1097,10 @@ message ClientIdentification {
DRM_DEVICE_CERTIFICATE = 1;
REMOTE_ATTESTATION_CERTIFICATE = 2;
OEM_DEVICE_CERTIFICATE = 3;
// Boot certificate chain in CBOR format.
BOOT_CERTIFICATE_CHAIN = 4;
// Boot certificate chain in X509 format.
BOOT_CERTIFICATE_CHAIN_X509 = 5;
}
message NameValue {

View File

@@ -10,6 +10,7 @@
#include "log.h"
#include "odk_structs.h"
#include "oemcrypto_adapter.h"
#include "wv_attributes.h"
namespace wvcdm {
OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox(
@@ -45,11 +46,6 @@ OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow) {
(void)allow;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
RequestedSecurityLevel) {
return ::OEMCrypto_OpenSession(session);
@@ -223,4 +219,16 @@ OEMCryptoResult OEMCrypto_Generic_Verify(
signature_length);
}
OEMCryptoResult OEMCrypto_GetBCCType(RequestedSecurityLevel level,
OEMCrypto_BCCType* bcc_type) {
(void)level;
return ::OEMCrypto_GetBCCType(bcc_type);
}
} // namespace wvcdm
// Provide default implementation of L3-only functions. WEAK allows them to be
// replaced by the L3 if available.
WEAK OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION, bool) {
return OEMCrypto_SUCCESS;
}

View File

@@ -30,7 +30,7 @@ namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
WvCdmEventListener* event_listener,
CryptoSession* crypto_session)
: session_id_(session_id),
: session_id_(std::move(session_id)),
event_listener_(event_listener),
license_keys_(new LicenseKeys(crypto_session->GetSecurityLevel())),
clock_(new wvutil::Clock()) {
@@ -39,12 +39,9 @@ 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_) {
// Use V16 policy timers if getting version failed.
if (policy_timers_ == nullptr) {
policy_timers_.reset(new PolicyTimersV16());
}
InitDevice(crypto_session);

View File

@@ -391,4 +391,10 @@ std::string Sha256Hash(const std::string& data) {
return hash;
}
std::string Sha512Hash(const std::string& data) {
std::string hash(CC_SHA512_DIGEST_LENGTH, '\0');
CC_SHA512(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
} // namespace wvcdm

View File

@@ -417,4 +417,11 @@ std::string Sha256Hash(const std::string& data) {
return hash;
}
std::string Sha512Hash(const std::string& data) {
std::string hash(SHA512_DIGEST_LENGTH, '\0');
SHA512(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
} // namespace wvcdm

View File

@@ -13,6 +13,8 @@
# include <CommonCrypto/CommonDigest.h>
# define SHA256 CC_SHA256
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
# define SHA512 CC_SHA512
# define SHA512_DIGEST_LENGTH CC_SHA512_DIGEST_LENGTH
# define MD5 CC_MD5
# define MD5_DIGEST_LENGTH CC_MD5_DIGEST_LENGTH
#else
@@ -69,4 +71,10 @@ std::string Sha256Hash(const std::string& data) {
return hash;
}
std::string Sha512Hash(const std::string& data) {
std::string hash(SHA512_DIGEST_LENGTH, '\0');
SHA512(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
} // namespace wvcdm

View File

@@ -12,8 +12,6 @@ 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.
@@ -898,8 +896,6 @@ 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 */