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:
Fred Gylys-Colwell
2020-01-18 10:18:50 -08:00
parent 7665614b2e
commit db2050dff1
62 changed files with 2947 additions and 2286 deletions

View File

@@ -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",

View File

@@ -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(

View File

@@ -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_;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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) {}

View File

@@ -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