OEMCrypto v16.2
Merge from Widevine repo of http://go/wvgerrit/93404 This is the unit tests, reference code, and documentation for OEMCrypto v16.2. Backwards compatibility should work for a v15 OEMCrypto. Some review comments will be addressed in future CLs. Bug: 141247171 Test: Unit tests Test: Media GTS tests on bonito Change-Id: I9d427c07580e180c0a4cfdc4a68f538d351c0ddd
This commit is contained in:
@@ -8,7 +8,8 @@ METRICS_SRC_DIR = "metrics/src"
|
||||
cc_library_static {
|
||||
name: "libcdm",
|
||||
|
||||
cflags: ["-DDYNAMIC_ADAPTER"],
|
||||
cflags: ["-DDYNAMIC_ADAPTER",
|
||||
"-DTEST_OEMCRYPTO_V15"],
|
||||
|
||||
include_dirs: [
|
||||
"vendor/widevine/libwvdrmengine/cdm/core/include",
|
||||
|
||||
@@ -51,8 +51,8 @@ class ContentKeySession : public KeySession {
|
||||
// Decrypt for ContentKeySession
|
||||
OEMCryptoResult Decrypt(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor,
|
||||
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) override;
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor, size_t additional_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) override;
|
||||
|
||||
protected:
|
||||
virtual OEMCryptoResult LoadKeysAsLicenseType(
|
||||
|
||||
@@ -40,8 +40,8 @@ class KeySession {
|
||||
CdmCipherMode cipher_mode) = 0;
|
||||
virtual OEMCryptoResult Decrypt(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor,
|
||||
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0;
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor, size_t additional_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0;
|
||||
|
||||
protected:
|
||||
metrics::CryptoMetrics* metrics_;
|
||||
|
||||
@@ -107,18 +107,34 @@ OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id,
|
||||
// Decrypt for ContentKeySession
|
||||
OEMCryptoResult ContentKeySession::Decrypt(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor,
|
||||
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
#if 0 // TODO(b/135285640): fix this.
|
||||
OEMCrypto_DestBufferDesc& buffer_descriptor, size_t additional_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
|
||||
// TODO(b/135285640): fix this. At the moment, all this does is send one
|
||||
// subsample. No checks are in place.
|
||||
|
||||
OEMCryptoResult sts;
|
||||
M_TIME(sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
|
||||
OEMCrypto_SampleDescription sample; // Just one for now.
|
||||
OEMCrypto_SubSampleDescription subsample;
|
||||
sample.buffers.input_data = params.encrypt_buffer + additional_offset;
|
||||
sample.buffers.input_data_length = params.encrypt_length;
|
||||
sample.buffers.output_descriptor = buffer_descriptor;
|
||||
memcpy(sample.iv, params.iv->data(), KEY_IV_SIZE);
|
||||
sample.subsamples = &subsample;
|
||||
sample.subsamples_length = 1;
|
||||
if (params.is_encrypted) {
|
||||
subsample.num_bytes_clear = 0;
|
||||
subsample.num_bytes_encrypted = params.encrypt_length;
|
||||
} else {
|
||||
subsample.num_bytes_clear = params.encrypt_length;
|
||||
subsample.num_bytes_encrypted = 0;
|
||||
}
|
||||
subsample.subsample_flags = params.subsample_flags;
|
||||
subsample.block_offset = params.block_offset;
|
||||
|
||||
M_TIME(sts = OEMCrypto_DecryptCENC(oec_session_id_, &sample, 1,
|
||||
&pattern_descriptor),
|
||||
metrics_, oemcrypto_decrypt_cenc_, sts,
|
||||
metrics::Pow2Bucket(params.encrypt_length));
|
||||
#endif
|
||||
return sts;
|
||||
}
|
||||
|
||||
|
||||
@@ -541,6 +541,8 @@ bool CryptoSession::GetApiMinorVersion(SecurityLevel security_level,
|
||||
WithOecReadLock("GetApiMinorVersion", [&] {
|
||||
*minor_version = OEMCrypto_MinorAPIVersion(security_level);
|
||||
});
|
||||
// Record the minor version into the metrics.
|
||||
metrics_->oemcrypto_minor_api_version_.Record(*minor_version);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -811,10 +813,12 @@ CdmResponseType CryptoSession::PrepareAndSignLicenseRequest(
|
||||
// First call is intended to determine the required size of the
|
||||
// output buffers.
|
||||
WithOecSessionLock("PrepareAndSignLicenseRequest", [&] {
|
||||
M_TIME(sts = OEMCrypto_PrepAndSignLicenseRequest(
|
||||
oec_session_id_, nullptr, message.size(), &core_message_length,
|
||||
nullptr, &signature_length),
|
||||
metrics_, oemcrypto_prep_and_sign_license_request_, sts);
|
||||
M_TIME(
|
||||
sts = OEMCrypto_PrepAndSignLicenseRequest(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(message.data())),
|
||||
message.size(), &core_message_length, nullptr, &signature_length),
|
||||
metrics_, oemcrypto_prep_and_sign_license_request_, sts);
|
||||
});
|
||||
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
@@ -939,10 +943,12 @@ CdmResponseType CryptoSession::PrepareAndSignRenewalRequest(
|
||||
// First call is intended to determine the required size of the
|
||||
// output buffers.
|
||||
WithOecSessionLock("PrepareAndSignRenewalRequest", [&] {
|
||||
M_TIME(sts = OEMCrypto_PrepAndSignRenewalRequest(
|
||||
oec_session_id_, nullptr, message.size(), &core_message_length,
|
||||
nullptr, &signature_length),
|
||||
metrics_, oemcrypto_prep_and_sign_renewal_request_, sts);
|
||||
M_TIME(
|
||||
sts = OEMCrypto_PrepAndSignRenewalRequest(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(message.data())),
|
||||
message.size(), &core_message_length, nullptr, &signature_length),
|
||||
metrics_, oemcrypto_prep_and_sign_renewal_request_, sts);
|
||||
});
|
||||
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
@@ -1056,10 +1062,12 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
|
||||
// First call is intended to determine the required size of the
|
||||
// output buffers.
|
||||
WithOecSessionLock("PrepareAndSignProvisioningRequest", [&] {
|
||||
M_TIME(sts = OEMCrypto_PrepAndSignProvisioningRequest(
|
||||
oec_session_id_, nullptr, message.size(), &core_message_length,
|
||||
nullptr, &signature_length),
|
||||
metrics_, oemcrypto_prep_and_sign_provisioning_request_, sts);
|
||||
M_TIME(
|
||||
sts = OEMCrypto_PrepAndSignProvisioningRequest(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(message.data())),
|
||||
message.size(), &core_message_length, nullptr, &signature_length),
|
||||
metrics_, oemcrypto_prep_and_sign_provisioning_request_, sts);
|
||||
});
|
||||
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
@@ -1318,8 +1326,8 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
}
|
||||
|
||||
WithOecSessionLock("Decrypt() calling key_session_->Decrypt()", [&] {
|
||||
sts =
|
||||
key_session_->Decrypt(params, buffer_descriptor, pattern_descriptor);
|
||||
sts = key_session_->Decrypt(params, buffer_descriptor, 0,
|
||||
pattern_descriptor);
|
||||
});
|
||||
|
||||
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
||||
@@ -1817,6 +1825,9 @@ bool CryptoSession::GetMaximumUsageTableEntries(SecurityLevel security_level,
|
||||
WithOecReadLock("GetMaxUsageTableEntries", [&] {
|
||||
*number_of_entries = OEMCrypto_MaximumUsageTableHeaderSize(security_level);
|
||||
});
|
||||
// Record the number of entries into the metrics.
|
||||
metrics_->oemcrypto_maximum_usage_table_header_size_.Record(
|
||||
*number_of_entries);
|
||||
|
||||
return *number_of_entries >= kMinimumUsageTableEntriesSupported;
|
||||
}
|
||||
@@ -2562,18 +2573,12 @@ OEMCryptoResult CryptoSession::DecryptInChunks(
|
||||
// of AES blocks long.
|
||||
OEMCryptoResult sts;
|
||||
sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
#if 0 // TODO(b/135285640): fix this.
|
||||
WithOecSessionLock("DecryptInChunks", [&] {
|
||||
M_TIME(
|
||||
sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer + additional_offset,
|
||||
chunk_size, params.is_encrypted, &iv.front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, subsample_flags),
|
||||
metrics_, oemcrypto_decrypt_cenc_, sts,
|
||||
metrics::Pow2Bucket(chunk_size));
|
||||
M_TIME(sts = key_session_->Decrypt(params, buffer_descriptor,
|
||||
additional_offset, pattern_descriptor),
|
||||
metrics_, oemcrypto_decrypt_cenc_, sts,
|
||||
metrics::Pow2Bucket(chunk_size));
|
||||
});
|
||||
#endif
|
||||
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "file_store.h"
|
||||
#include "level3.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "metrics_collections.h"
|
||||
#include "odk.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
using namespace wvoec3;
|
||||
using video_widevine::ProvisioningResponse;
|
||||
using wvcdm::kLevel3;
|
||||
using wvcdm::kLevelDefault;
|
||||
|
||||
@@ -410,6 +412,16 @@ struct FunctionPointers {
|
||||
L1_RefreshKeys_V14_t RefreshKeys_V14;
|
||||
};
|
||||
|
||||
size_t GetOffset(const std::string& message, const std::string& field) {
|
||||
size_t pos = message.find(field);
|
||||
if (pos == std::string::npos) {
|
||||
LOGE("Cannot find the |field| offset in message: field = %s",
|
||||
field.c_str());
|
||||
pos = 0;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// The WatchDog looks after a worker thread that is trying to initialize L3.
|
||||
// Once in a rare while, the L3 init does not finish and eats up CPU cycles.
|
||||
// If that happens, the watchdog thread will give up and return an error.
|
||||
@@ -952,7 +964,7 @@ class Adapter {
|
||||
// TODO(b/139814713): implement V16 DecryptCENC for Haystack L3
|
||||
// level3_.LoadLicense = Level3_LoadLicense;
|
||||
level3_.LoadEntitledContentKeys = Level3_LoadEntitledContentKeys;
|
||||
// TODO(b/135285640): fix this.
|
||||
// TODO(b/139814713): fix this.
|
||||
// level3_.LoadRenewal = Level3_LoadRenewal;
|
||||
level3_.RefreshKeys = Level3_RefreshKeys;
|
||||
level3_.QueryKeyControl = Level3_QueryKeyControl;
|
||||
@@ -2085,12 +2097,14 @@ extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer,
|
||||
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
|
||||
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
if (fcn->version < 10) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (fcn->LoadTestKeybox != nullptr) {
|
||||
return fcn->LoadTestKeybox(buffer, length);
|
||||
}
|
||||
if (fcn->LoadTestKeybox_V13 != nullptr) {
|
||||
// Old versions might use wrong keybox, but this is the best we can do.
|
||||
return fcn->LoadTestKeybox_V13();
|
||||
}
|
||||
if (fcn->LoadTestKeybox == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
return fcn->LoadTestKeybox(buffer, length);
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid() {
|
||||
@@ -2164,57 +2178,55 @@ extern "C" OEMCryptoResult OEMCrypto_LoadProvisioning(
|
||||
pair.session, message, message_length, core_message_length, signature,
|
||||
signature_length, wrapped_private_key, wrapped_private_key_length);
|
||||
}
|
||||
if (pair.fcn->GetDeviceID == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
std::vector<uint8_t> device_id(ODK_DEVICE_ID_LEN_MAX);
|
||||
size_t device_id_length = device_id.size();
|
||||
if (pair.fcn->GetDeviceID(device_id.data(), &device_id_length) !=
|
||||
OEMCrypto_SUCCESS) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
ODK_ParsedProvisioning parsed_response;
|
||||
ODK_NonceValues nonce_values;
|
||||
nonce_values.nonce = pair.nonce;
|
||||
nonce_values.api_version = pair.fcn->version;
|
||||
nonce_values.session_id = pair.session;
|
||||
const OEMCryptoResult result = ODK_ParseProvisioning(
|
||||
message, message_length, core_message_length, &nonce_values,
|
||||
device_id.data(), device_id_length, &parsed_response);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("ODK Error %d", result);
|
||||
return result;
|
||||
}
|
||||
if (pair.fcn->GetProvisioningMethod == nullptr) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
const OEMCrypto_ProvisioningMethod method = pair.fcn->GetProvisioningMethod();
|
||||
|
||||
const std::string signed_message(
|
||||
reinterpret_cast<const char*>(message),
|
||||
reinterpret_cast<const char*>(message + message_length));
|
||||
ProvisioningResponse provisioning_response;
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("Failed to parse signed provisioining response");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
const std::string& iv = provisioning_response.device_rsa_key_iv();
|
||||
const std::string& nonce = provisioning_response.nonce();
|
||||
const uint8_t* msg_rsa_key = message + GetOffset(signed_message, enc_rsa_key);
|
||||
const uint8_t* msg_rsa_key_iv = message + GetOffset(signed_message, iv);
|
||||
const uint32_t* msg_nonce = reinterpret_cast<const uint32_t*>(
|
||||
message + GetOffset(signed_message, nonce));
|
||||
if (method == OEMCrypto_Keybox) {
|
||||
if (pair.fcn->RewrapDeviceRSAKey == nullptr) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return pair.fcn->RewrapDeviceRSAKey(
|
||||
pair.session, message, message_length, signature, signature_length,
|
||||
&pair.nonce, message + parsed_response.enc_private_key.offset,
|
||||
parsed_response.enc_private_key.length,
|
||||
message + parsed_response.enc_private_key_iv.offset,
|
||||
msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv,
|
||||
wrapped_private_key, wrapped_private_key_length);
|
||||
} else if (method == OEMCrypto_OEMCertificate) {
|
||||
if (pair.fcn->RewrapDeviceRSAKey30 == nullptr) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
const std::string& wrapping_key = (provisioning_response.has_wrapping_key())
|
||||
? provisioning_response.wrapping_key()
|
||||
: std::string();
|
||||
const uint8_t* msg_wrapping_key =
|
||||
message + GetOffset(signed_message, wrapping_key);
|
||||
return pair.fcn->RewrapDeviceRSAKey30(
|
||||
pair.session, &pair.nonce,
|
||||
message + parsed_response.encrypted_message_key.offset,
|
||||
parsed_response.encrypted_message_key.length,
|
||||
message + parsed_response.enc_private_key.offset,
|
||||
parsed_response.enc_private_key.length,
|
||||
message + parsed_response.enc_private_key_iv.offset,
|
||||
wrapped_private_key, wrapped_private_key_length);
|
||||
pair.session, msg_nonce, msg_wrapping_key, wrapping_key.size(),
|
||||
msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, wrapped_private_key,
|
||||
wrapped_private_key_length);
|
||||
} else { // Otherwise, provisioning method does not support LoadProvisioning.
|
||||
LOGE("Backwards compatibility code invalid: %d", method);
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
// This function is still used by the unit tests when driving a v15 OEMCrypto.
|
||||
// This is needed on Android because some devices have not upgraded to v16.
|
||||
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
||||
OEMCrypto_SESSION session, const uint32_t* nonce,
|
||||
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
|
||||
@@ -2233,6 +2245,8 @@ extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
||||
wrapped_rsa_key_length);
|
||||
}
|
||||
|
||||
// This function is still used by the unit tests when driving a v15 OEMCrypto.
|
||||
// This is needed on Android because some devices have not upgraded to v16.
|
||||
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length, const uint32_t* nonce,
|
||||
|
||||
@@ -291,6 +291,8 @@ class CryptoMetrics {
|
||||
oemcrypto_load_renewal_;
|
||||
EventMetric<kOemCryptoResultFieldNumber, OEMCryptoResult>
|
||||
oemcrypto_load_provisioning_;
|
||||
ValueMetric<uint32_t> oemcrypto_minor_api_version_;
|
||||
ValueMetric<uint32_t> oemcrypto_maximum_usage_table_header_size_;
|
||||
};
|
||||
|
||||
// This class contains session-scoped metrics. All properties and
|
||||
|
||||
@@ -93,7 +93,7 @@ message WvCdmMetrics {
|
||||
|
||||
// This contains metrics that were captured at the CryptoSession level. These
|
||||
// include CryptoSession metrics and most OEMCrypto metrics.
|
||||
// next id: 83
|
||||
// next id: 85
|
||||
message CryptoMetrics {
|
||||
// Crypto Session Metrics.
|
||||
optional ValueMetric crypto_session_security_level = 1;
|
||||
@@ -190,6 +190,8 @@ message WvCdmMetrics {
|
||||
repeated DistributionMetric oemcrypto_load_license_time_us = 80;
|
||||
repeated DistributionMetric oemcrypto_load_renewal_time_us = 81;
|
||||
repeated DistributionMetric oemcrypto_load_provisioning_time_us = 82;
|
||||
optional ValueMetric oemcrypto_minor_api_version = 83;
|
||||
optional ValueMetric oemcrypto_maximum_usage_table_header_size = 84;
|
||||
}
|
||||
|
||||
// This contains metrics that were captured within a CdmSession. This contains
|
||||
|
||||
@@ -209,6 +209,10 @@ void CryptoMetrics::Serialize(WvCdmMetrics::CryptoMetrics *crypto_metrics)
|
||||
crypto_metrics->mutable_oemcrypto_load_renewal_time_us());
|
||||
oemcrypto_load_provisioning_.ToProto(
|
||||
crypto_metrics->mutable_oemcrypto_load_provisioning_time_us());
|
||||
crypto_metrics->set_allocated_oemcrypto_minor_api_version(
|
||||
oemcrypto_minor_api_version_.ToProto());
|
||||
crypto_metrics->set_allocated_oemcrypto_maximum_usage_table_header_size(
|
||||
oemcrypto_maximum_usage_table_header_size_.ToProto());
|
||||
}
|
||||
|
||||
SessionMetrics::SessionMetrics() : session_id_(""), completed_(false) {}
|
||||
|
||||
@@ -439,6 +439,8 @@ TEST_F(CryptoMetricsTest, AllCryptoMetrics) {
|
||||
crypto_metrics.oemcrypto_set_decrypt_hash_
|
||||
.Increment(OEMCrypto_ERROR_INIT_FAILED);
|
||||
crypto_metrics.oemcrypto_resource_rating_tier_.Record(123);
|
||||
crypto_metrics.oemcrypto_minor_api_version_.Record(234);
|
||||
crypto_metrics.oemcrypto_maximum_usage_table_header_size_.Record(321);
|
||||
|
||||
WvCdmMetrics::CryptoMetrics actual;
|
||||
crypto_metrics.Serialize(&actual);
|
||||
@@ -527,6 +529,9 @@ TEST_F(CryptoMetricsTest, AllCryptoMetrics) {
|
||||
EXPECT_EQ("sandbox", actual.oemcrypto_set_sandbox().string_value());
|
||||
EXPECT_GT(actual.oemcrypto_set_decrypt_hash_size(), 0);
|
||||
EXPECT_EQ(123, actual.oemcrypto_resource_rating_tier().int_value());
|
||||
EXPECT_EQ(234, actual.oemcrypto_minor_api_version().int_value());
|
||||
EXPECT_EQ(321,
|
||||
actual.oemcrypto_maximum_usage_table_header_size().int_value());
|
||||
}
|
||||
|
||||
} // namespace metrics
|
||||
|
||||
Reference in New Issue
Block a user