Source release 18.5.0

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

View File

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