Update Unit tests and reference code

This patch adds a suite of tests for OEMCrypto that verifying buffer
overflow and off-by-one errors. The reference code has also been
updated to pass these tests.

The ODK library and the OEMCrypto API have not changed since the
release of version 16.4.
This commit is contained in:
Fred Gylys-Colwell
2021-01-25 19:51:10 -08:00
parent 7e3c282944
commit bb16924e69
45 changed files with 3119 additions and 489 deletions

View File

@@ -480,8 +480,8 @@ typedef enum OEMCrypto_ProvisioningMethod {
OEMCrypto_ProvisioningError = 0, // Device cannot be provisioned.
OEMCrypto_DrmCertificate = 1, // Device has baked in DRM certificate
// (level 3 only)
OEMCrypto_Keybox = 2, // Device has factory installed unique keybox.
OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate.
OEMCrypto_Keybox = 2, // Device has factory installed unique keybox.
OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate.
} OEMCrypto_ProvisioningMethod;
/**
@@ -489,7 +489,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
*/
#define OEMCrypto_Supports_RSA_2048bit 0x1
#define OEMCrypto_Supports_RSA_3072bit 0x2
#define OEMCrypto_Supports_RSA_CAST 0x10
#define OEMCrypto_Supports_RSA_CAST 0x10
#define OEMCrypto_Supports_ECC_secp256r1 0x100
#define OEMCrypto_Supports_ECC_secp384r1 0x200
#define OEMCrypto_Supports_ECC_secp521r1 0x400
@@ -504,12 +504,12 @@ typedef enum OEMCrypto_ProvisioningMethod {
/**
* Return values from OEMCrypto_GetAnalogOutputFlags.
*/
#define OEMCrypto_No_Analog_Output 0x0
#define OEMCrypto_Supports_Analog_Output 0x1
#define OEMCrypto_Can_Disable_Analog_Ouptput 0x2
#define OEMCrypto_Supports_CGMS_A 0x4
#define OEMCrypto_No_Analog_Output 0x0
#define OEMCrypto_Supports_Analog_Output 0x1
#define OEMCrypto_Can_Disable_Analog_Ouptput 0x2
#define OEMCrypto_Supports_CGMS_A 0x4
// Unknown_Analog_Output is used only for backwards compatibility.
#define OEMCrypto_Unknown_Analog_Output (1 << 31)
#define OEMCrypto_Unknown_Analog_Output (1<<31)
/// @}
@@ -622,8 +622,6 @@ typedef enum OEMCrypto_ProvisioningMethod {
#define OEMCrypto_DecryptCENC _oecc105
#define OEMCrypto_LoadDRMPrivateKey _oecc107
#define OEMCrypto_MinorAPIVersion _oecc108
#define OEMCrypto_AllocateSecureBuffer _oecc109
#define OEMCrypto_FreeSecureBuffer _oecc110
// clang-format on
/// @addtogroup initcontrol
@@ -4725,25 +4723,12 @@ OEMCryptoResult OEMCrypto_FreeSecureBuffer(
* current version of OEMCrypto. They are being declared here to help with
* backwards compatibility.
*/
/*
* OEMCrypto_GenerateSignature
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
/*
* OEMCrypto_RewrapDeviceRSAKey30
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
@@ -4751,12 +4736,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
/*
* OEMCrypto_RewrapDeviceRSAKey
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
@@ -4764,70 +4743,26 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length);
/*
* OEMCrypto_UpdateUsageTable
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_UpdateUsageTable(void);
/*
* OEMCrypto_DeleteUsageEntry
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_DeleteUsageEntry(
OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length,
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length);
OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION, const uint8_t*,
size_t, const uint8_t*, size_t,
const uint8_t*, size_t);
/*
* OEMCrypto_ForceDeleteUsageEntry
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst,
size_t pst_length);
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t*, size_t);
/*
* OEMCrypto_CopyOldUsageEntry
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length);
/*
* OEMCrypto_DeleteOldUsageTable
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
/*
* OEMCrypto_CreateOldUsageEntry
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
size_t pst_length);
/*
* OEMCrypto_GenerateDerivedKeys_V15
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_GenerateDerivedKeys_V15(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
@@ -4839,36 +4774,18 @@ typedef struct {
size_t offset; // offset into the pattern in blocks for this call.
} OEMCrypto_CENCEncryptPatternDesc_V15;
/*
* OEMCrypto_DecryptCENC_V15
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_DecryptCENC_V15(
OEMCrypto_SESSION session, const uint8_t* data_addr,
size_t data_addr_length, bool is_encrypted, const uint8_t* iv,
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv,
size_t block_offset, // used for CTR "cenc" mode only.
OEMCrypto_DestBufferDesc* out_buffer,
OEMCrypto_DestBufferDesc* out_buffer_descriptor,
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
uint8_t subsample_flags);
/*
* OEMCrypto_GetOEMPublicCertificate_V15
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate_V15(
OEMCrypto_SESSION session, uint8_t* public_cert,
size_t* public_cert_length);
/*
* OEMCrypto_LoadDeviceRSAKey
* @deprecated
* Not required for the current version of OEMCrypto. Declared here to
* help with backward compatibility.
*/
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);

View File

@@ -96,7 +96,6 @@ namespace wvoec3 {
#define Level3_AllocateSecureBuffer _lcc111
#define Level3_FreeSecureBuffer _lcc112
#else
#define Level3_IsInApp _oecc00
#define Level3_Initialize _oecc01
#define Level3_Terminate _oecc02
#define Level3_InstallKeyboxOrOEMCert _oecc03
@@ -169,11 +168,15 @@ namespace wvoec3 {
#define Level3_DecryptCENC _oecc105
#define Level3_LoadDRMPrivateKey _oecc107
#define Level3_MinorAPIVersion _oecc108
#define Level3_AllocateSecureBuffer _oecc111
#define Level3_FreeSecureBuffer _oecc112
// TODO(b/171121061): Renaming for oemcrypto_test to find the L3 implementation
// of the two functions below. This is to be fixed when
// OEMCrypto_AllocateSecureBuffer and OEMCrypto_FreeSecureBuffer are added to
// OEMCryptoCENC.h
#define Level3_AllocateSecureBuffer OEMCrypto_AllocateSecureBuffer
#define Level3_FreeSecureBuffer OEMCrypto_FreeSecureBuffer
#endif
#define Level3_GetInitializationState _oecl3o01
#define Level3_GetInitializationState _oecl3o01
// clang-format on
extern "C" {

View File

@@ -37,4 +37,12 @@
'../../util/libcrypto_dependency.gypi',
'../odk/src/odk.gypi',
],
'cflags': [
# TODO(b/172518513): Remove this
'-Wno-error=cast-qual',
],
'cflags_c': [
# TODO(b/159354894): Remove this
'-Wno-error=bad-function-cast',
],
}

View File

@@ -171,12 +171,12 @@ class CryptoEngine {
// If 0 no restriction, otherwise it's the max subsample size for
// DecryptCENC. This is not the same as the max sample or buffer size.
virtual size_t max_subsample_size() { return 1024 * 100; } // 100 KiB
virtual size_t max_subsample_size() { return 4 * 1024 * 1024; } // 4 MiB
// If 0 no restriction, otherwise it's the max sample size for DecryptCENC.
// This is the same as the max input and output buffer size for DecryptCENC
// and CopyBuffer. It is not the same as the max subsample size.
virtual size_t max_sample_size() { return 1024 * 1024; } // 1 MiB
virtual size_t max_sample_size() { return 16 * 1024 * 1024; } // 16 MiB
virtual bool srm_update_supported() { return false; }
@@ -193,7 +193,7 @@ class CryptoEngine {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual bool srm_blacklisted_device_attached() { return false; }
virtual bool srm_forbidden_device_attached() { return false; }
// Rate limit for nonce generation. Default to 200 nonce/second.
virtual int nonce_flood_count() { return 200; }

View File

@@ -32,9 +32,9 @@
#include "string_conversions.h"
#if defined(_WIN32)
# define OEMCRYPTO_API extern "C" __declspec(dllexport)
# define OEMCRYPTO_API extern "C" __declspec(dllexport)
#else // defined(_WIN32)
# define OEMCRYPTO_API extern "C" __attribute__((visibility("default")))
# define OEMCRYPTO_API extern "C" __attribute__((visibility("default")))
#endif // defined(_WIN32)
namespace {
@@ -42,6 +42,12 @@ const uint8_t kBakedInCertificateMagicBytes[] = {0xDE, 0xAD, 0xBE, 0xEF};
// Maximum context key length used for performance reasons, not mandated by
// specification.
const size_t kMaxContextKeyLength = 1024 * 1024;
// Maximum buffer length used by reference implementation for performance
// reasons. This is not mandated by specification.
const size_t kMaxInputMessageBuferLength = 1024 * 1024;
// Maximum signature length used by reference implementation for performance
// reasons. This is not mandated by specification.
const size_t kMaxInputSignatureLength = 10 * 1024;
// Return uint32 referenced through a potentially unaligned pointer.
// If the pointer is nullptr, return 0.
@@ -100,8 +106,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Terminate(void) {
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_OpenSession(
OEMCrypto_SESSION* session) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_OpenSession: OEMCrypto not initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -120,8 +126,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_OpenSession(
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CloseSession(
OEMCrypto_SESSION session) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_CloseSession: OEMCrypto not initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -209,7 +215,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
while (nonce_value == 0 || crypto_engine->NonceCollision(nonce_value)) {
// Generate 4 bytes of random data
if (!RAND_bytes(nonce_string, 4)) {
if (RAND_bytes(nonce_string, 4) != 1) {
LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -309,7 +315,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session,
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("ERROR_INVALID_SESSION sid=%d", session);
LOGE("ERROR_INVALID_SESSION sid=%u", session);
return OEMCrypto_ERROR_INVALID_SESSION;
}
return session_ctx->LoadLicense(message, message_length, core_message_length,
@@ -333,7 +339,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("ERROR_INVALID_SESSION sid=%d", session);
LOGE("ERROR_INVALID_SESSION sid=%u", session);
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == nullptr || message_length == 0 || signature == nullptr ||
@@ -347,33 +353,38 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
!RangeCheck(message_length, enc_mac_keys, true) ||
!RangeCheck(message_length, pst, true) ||
!RangeCheck(message_length, srm_restriction_data, true)) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check.]");
LOGE(
"[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check.]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (unsigned int i = 0; i < num_keys; i++) {
for (size_t i = 0; i < num_keys; i++) {
if (!RangeCheck(message_length, key_array[i].key_id, false) ||
!RangeCheck(message_length, key_array[i].key_data, false) ||
!RangeCheck(message_length, key_array[i].key_data_iv, false) ||
!RangeCheck(message_length, key_array[i].key_control, false) ||
!RangeCheck(message_length, key_array[i].key_control_iv, false)) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check %d]", i);
LOGE(
"[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - range "
"check %zu]",
i);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
if (enc_mac_keys.offset >= wvoec::KEY_IV_SIZE && enc_mac_keys.length > 0) {
if (enc_mac_keys_iv.offset + wvoec::KEY_IV_SIZE == enc_mac_keys.offset) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check iv]");
LOGE(
"[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check iv]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
} else {
if (CRYPTO_memcmp(message + enc_mac_keys.offset - wvoec::KEY_IV_SIZE,
message + enc_mac_keys_iv.offset,
wvoec::KEY_IV_SIZE) == 0) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"suspicious iv]");
LOGE(
"[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"suspicious iv]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
@@ -570,9 +581,9 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_QueryKeyControl(
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_SelectKey(
const OEMCrypto_SESSION session, const uint8_t* key_id,
size_t key_id_length, OEMCryptoCipherMode cipher_mode) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_SelectKey(const OEMCrypto_SESSION session, const uint8_t* key_id,
size_t key_id_length, OEMCryptoCipherMode cipher_mode) {
#ifndef NDEBUG
if (!crypto_engine->ValidRootOfTrust()) {
LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]");
@@ -619,7 +630,14 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
#endif
// The maximum subsample and sample sizes we use -- if 0, we just pick a
// very large size for a sanity check.
const size_t max_subsample_size = crypto_engine->max_subsample_size() > 0
? crypto_engine->max_subsample_size()
: 100 * 1024 * 1024;
const size_t max_sample_size = crypto_engine->max_subsample_size() > 0
? crypto_engine->max_subsample_size()
: 1000 * 1024 * 1024;
// Iterate through all the samples and validate them before doing any decrypt
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
const OEMCrypto_SampleDescription& sample = samples[sample_index];
@@ -636,17 +654,20 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
LOGE("[OEMCrypto_DecryptCENC(): Sample too large]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
// Iterate through all the subsamples and sum their lengths
size_t subsample_length_tally = 0;
for (size_t subsample_index = 0; subsample_index < sample.subsamples_length;
++subsample_index) {
const OEMCrypto_SubSampleDescription& subsample =
sample.subsamples[subsample_index];
// Compute the length now, but we check for possible overflow in the next
// if statement.
const size_t length =
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
if (crypto_engine->max_subsample_size() > 0 &&
length > crypto_engine->max_subsample_size()) {
if (subsample.num_bytes_clear > max_subsample_size ||
subsample.num_bytes_encrypted > max_subsample_size ||
length > max_subsample_size ||
subsample_length_tally > max_sample_size) {
// For testing reasons only, pretend that this integration only supports
// the given buffer size.
LOGE("[OEMCrypto_DecryptCENC(): Subsample too large]");
@@ -711,8 +732,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(
const uint8_t* keybox, size_t keyBoxLength) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox, size_t keyBoxLength) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_InstallKeyboxOrOEMCert: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -876,7 +897,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
if (!randomData) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (RAND_bytes(randomData, dataLength)) {
if (RAND_bytes(randomData, dataLength) == 1) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -944,11 +965,11 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
// Now we generate a wrapped keybox.
WrappedRSAKey* wrapped = reinterpret_cast<WrappedRSAKey*>(wrapped_rsa_key);
// Pick a random context and IV for generating keys.
if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) {
if (RAND_bytes(wrapped->context, sizeof(wrapped->context)) != 1) {
LOGE("[_RewrapDeviceRSAKey30(): RAND_bytes failed.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) {
if (RAND_bytes(wrapped->iv, sizeof(wrapped->iv)) != 1) {
LOGE("[_RewrapDeviceRSAKey30(): RAND_bytes failed.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -1045,10 +1066,10 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
// Now we generate a wrapped keybox.
WrappedRSAKey* wrapped = reinterpret_cast<WrappedRSAKey*>(wrapped_rsa_key);
// Pick a random context and IV for generating keys.
if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) {
if (RAND_bytes(wrapped->context, sizeof(wrapped->context)) != 1) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) {
if (RAND_bytes(wrapped->iv, sizeof(wrapped->iv)) != 1) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const std::vector<uint8_t> context(
@@ -1178,6 +1199,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(
return OEMCrypto_SUCCESS;
}
}
if (wrapped_rsa_key_length < sizeof(WrappedRSAKey)) {
LOGE("RSA Key has wrong size.");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
const WrappedRSAKey* wrapped =
reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
if (!crypto_engine->ValidRootOfTrust()) {
@@ -1393,8 +1418,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(
size_t* maximum) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_GetMaxNumberOfSessions(size_t* maximum) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_GetMaxNumberOfSessions: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1444,6 +1469,11 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Encrypt(
LOGE("[OEMCrypto_Generic_Encrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length > kMaxInputMessageBuferLength) {
LOGE("[OEMCrypto_Generic_Encrypt(): ERROR_BUFFER_TOO_LARGE]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
OEMCryptoResult sts = session_ctx->Generic_Encrypt(in_buffer, buffer_length,
iv, algorithm, out_buffer);
return sts;
@@ -1470,15 +1500,19 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Decrypt(
LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length > kMaxInputMessageBuferLength) {
LOGE("[OEMCrypto_Generic_Encrypt(): ERROR_BUFFER_TOO_LARGE]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
OEMCryptoResult sts = session_ctx->Generic_Decrypt(in_buffer, buffer_length,
iv, algorithm, out_buffer);
return sts;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Sign(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm, uint8_t* signature,
size_t* signature_length) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, const uint8_t* in_buffer,
size_t buffer_length, OEMCrypto_Algorithm algorithm,
uint8_t* signature, size_t* signature_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_Generic_Sign: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1500,15 +1534,19 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Sign(
LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length > kMaxInputMessageBuferLength) {
LOGE("[OEMCrypto_Generic_Sign(): ERROR_BUFFER_TOO_LARGE]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
OEMCryptoResult sts = session_ctx->Generic_Sign(
in_buffer, buffer_length, algorithm, signature, signature_length);
return sts;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Verify(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm, const uint8_t* signature,
size_t signature_length) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, const uint8_t* in_buffer,
size_t buffer_length, OEMCrypto_Algorithm algorithm,
const uint8_t* signature, size_t signature_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_Generic_Verify: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1525,10 +1563,15 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Verify(
if (signature_length != SHA256_DIGEST_LENGTH) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (in_buffer == nullptr || buffer_length == 0 || signature == nullptr) {
if (in_buffer == nullptr || buffer_length == 0 || signature == nullptr ||
signature_length > kMaxInputSignatureLength) {
LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length > kMaxInputMessageBuferLength) {
LOGE("[OEMCrypto_Generic_Verify(): ERROR_BUFFER_TOO_LARGE]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
return session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm,
signature, signature_length);
}
@@ -1585,8 +1628,8 @@ OEMCRYPTO_API bool OEMCrypto_IsSRMUpdateSupported() {
return result;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(
uint16_t* version) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_GetCurrentSRMVersion(uint16_t* version) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_GetCurrentSRMVersion: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1629,8 +1672,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateUsageTableHeader(
header_buffer, header_buffer_length);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadUsageTableHeader(
const uint8_t* buffer, size_t buffer_length) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_LoadUsageTableHeader: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1667,9 +1710,9 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateNewUsageEntry(
return sts;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadUsageEntry(
OEMCrypto_SESSION session, uint32_t index, const uint8_t* buffer,
size_t buffer_size) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, uint32_t index,
const uint8_t* buffer, size_t buffer_size) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_LoadUsageEntry: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1690,10 +1733,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadUsageEntry(
return session_ctx->LoadUsageEntry(index, bufferv);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_UpdateUsageEntry(
OEMCrypto_SESSION session, uint8_t* header_buffer,
size_t* header_buffer_length, uint8_t* entry_buffer,
size_t* entry_buffer_length) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, uint8_t* header_buffer,
size_t* header_buffer_length, uint8_t* entry_buffer,
size_t* entry_buffer_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_UpdateUsageEntry: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -1748,9 +1791,9 @@ OEMCRYPTO_API uint32_t OEMCrypto_SupportsDecryptHash() {
return OEMCrypto_CRC_Clear_Buffer;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_SetDecryptHash(
OEMCrypto_SESSION session, uint32_t frame_number, const uint8_t* hash,
size_t hash_length) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, uint32_t frame_number,
const uint8_t* hash, size_t hash_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_SetDecryptHash: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;

View File

@@ -303,9 +303,12 @@ bool SessionContext::RSADeriveKeys(
LOGE("[RSADeriveKeys(): no RSA key set]");
return false;
}
if (enc_session_key.size() != static_cast<size_t>(RSA_size(rsa_key()))) {
LOGE("[RSADeriveKeys(): encrypted session key wrong size:%zu, expected %d]",
enc_session_key.size(), RSA_size(rsa_key()));
const size_t actual_key_size = static_cast<size_t>(RSA_size(rsa_key()));
if (enc_session_key.size() != actual_key_size) {
LOGE(
"[RSADeriveKeys(): encrypted session key wrong size: %zu, expected "
"%zu]",
enc_session_key.size(), actual_key_size);
dump_boringssl_error();
return false;
}
@@ -806,8 +809,8 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature(
LOGW("[LoadKeys: SRM Version is too small %u, required: %u",
current_version, minimum_version);
srm_requirements_status_ = InvalidSRMVersion;
} else if (ce_->srm_blacklisted_device_attached()) {
LOGW("[LoadKeys: SRM blacklisted device attached]");
} else if (ce_->srm_forbidden_device_attached()) {
LOGW("[LoadKeys: SRM forbidden device attached]");
srm_requirements_status_ = InvalidSRMVersion;
} else {
LOGI("[LoadKeys: SRM Versions is %u, required: %u]", current_version,
@@ -1193,6 +1196,36 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string,
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
}
if (!ce_->config_local_display_only()) {
// Only look at HDCP restrictions if the display can be non-local.
if (control.control_bits() & wvoec::kControlHDCPRequired) {
uint8_t required_hdcp =
(control.control_bits() & wvoec::kControlHDCPVersionMask) >>
wvoec::kControlHDCPVersionShift;
if (ce_->srm_forbidden_device_attached()) {
required_hdcp = HDCP_NO_DIGITAL_OUTPUT;
}
// For reference implementation, we pretend we can handle the current
// HDCP version.
if (required_hdcp > ce_->config_current_hdcp_capability() ||
ce_->config_current_hdcp_capability() == 0) {
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
}
}
}
// Return an error if analog displays should be disabled.
if ((control.control_bits() & wvoec::kControlDisableAnalogOutput) &&
ce_->analog_display_active()) {
LOGE("[%s(): control bit says disable analog", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
// Check if CGMS is required.
if (control.control_bits() & wvoec::kControlCGMSMask) {
if (ce_->analog_display_active() && !ce_->cgms_a_active()) {
LOGE("[%s(): control bit says CGMS required", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
}
if (!decrypt_started_) {
// The reference implementation does not have a hardware timer.
uint64_t* timer_expiration = nullptr;
@@ -1207,44 +1240,6 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string,
if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED;
if (usage_entry_ != nullptr) usage_entry_->set_recent_decrypt(true);
}
if (!ce_->config_local_display_only()) {
// Only look at HDCP restrictions if the display can be non-local.
if (control.control_bits() & wvoec::kControlHDCPRequired) {
uint8_t required_hdcp =
(control.control_bits() & wvoec::kControlHDCPVersionMask) >>
wvoec::kControlHDCPVersionShift;
if (ce_->srm_blacklisted_device_attached()) {
required_hdcp = HDCP_NO_DIGITAL_OUTPUT;
}
// For reference implementation, we pretend we can handle the current
// HDCP version.
if (required_hdcp > ce_->config_current_hdcp_capability() ||
ce_->config_current_hdcp_capability() == 0) {
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
}
}
}
// If the output buffer is clear, then we cannot control whether the output is
// an active analog display. In that case, return an error if analog displays
// should be disabled.
if ((control.control_bits() & wvoec::kControlDisableAnalogOutput) &&
(ce_->analog_display_active() ||
(buffer_type == OEMCrypto_BufferType_Clear))) {
LOGE("[%s(): control bit says disable analog", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
// Check if CGMS is required.
if (control.control_bits() & wvoec::kControlCGMSMask) {
// We can't control CGMS for a clear buffer.
if (buffer_type == OEMCrypto_BufferType_Clear) {
LOGE("[%s(): CGMS required, but buffer is clear", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
if (ce_->analog_display_active() && !ce_->cgms_a_active()) {
LOGE("[%s(): control bit says CGMS required", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
}
decrypt_started_ = true; // First playback for session.
return OEMCrypto_SUCCESS;
}
@@ -1761,6 +1756,8 @@ OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8,
const uint8_t* cipher_data,
size_t cipher_data_length,
uint8_t* clear_data) {
if (block_offset >= AES_BLOCK_SIZE) return OEMCrypto_ERROR_INVALID_CONTEXT;
// Local copy (will be modified).
// Allocated as 64-bit ints to enforce 64-bit alignment for later access as a
// 64-bit value.

View File

@@ -209,7 +209,10 @@ OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce,
}
// Encrypt the entry.
RAND_bytes(encrypted->iv, wvoec::KEY_IV_SIZE);
if (RAND_bytes(encrypted->iv, wvoec::KEY_IV_SIZE) != 1) {
LOGE("SaveUsageEntry: Could not generate iv.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; // working iv buffer.
memcpy(iv_buffer, encrypted->iv, wvoec::KEY_IV_SIZE);
AES_KEY aes_key;
@@ -292,7 +295,7 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
// Check that the index is correct.
if (index != clear->data.index) {
LOGE("LoadUsageEntry: entry says index is %d, not %d", clear->data.index,
LOGE("LoadUsageEntry: entry says index is %u, not %u", clear->data.index,
index);
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -386,7 +389,7 @@ OEMCryptoResult UsageTable::LoadUsageEntry(
if (index >= generation_numbers_.size())
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (sessions_[index]) {
LOGE("LoadUsageEntry: index %d used by other session.", index);
LOGE("LoadUsageEntry: index %u used by other session.", index);
return OEMCrypto_ERROR_INVALID_SESSION;
}
const size_t max = ce_->max_usage_table_size();
@@ -479,7 +482,10 @@ OEMCryptoResult UsageTable::SaveUsageTableHeader(uint8_t* signed_buffer,
}
// Encrypt the entry.
RAND_bytes(encrypted->iv, wvoec::KEY_IV_SIZE);
if (RAND_bytes(encrypted->iv, wvoec::KEY_IV_SIZE) != 1) {
LOGE("SaveUsageHeader: Could not generate iv entry.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; // working iv buffer.
memcpy(iv_buffer, encrypted->iv, wvoec::KEY_IV_SIZE);
AES_KEY aes_key;
@@ -667,9 +673,8 @@ bool UsageTable::LoadGenerationNumber(bool or_make_new_one) {
auto file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly);
if (!file) {
if (or_make_new_one) {
RAND_bytes(reinterpret_cast<uint8_t*>(&master_generation_number_),
sizeof(int64_t));
return true;
return RAND_bytes(reinterpret_cast<uint8_t*>(&master_generation_number_),
sizeof(int64_t)) == 1;
}
LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str());
master_generation_number_ = 0;

73
oemcrypto/test/README.md Normal file
View File

@@ -0,0 +1,73 @@
# OEMCrypto Memory Unit Tests
## Objective
* Add OEMCrypto buffer overflow unit tests (indirect way of fuzzing) to verify
OEMCrypto API behavior when the parameters passed to the API are out of
range or not reasonable. The API can return an error code, but shouldn't
crash.
* A lot of OEMCrypto APIs take buffers and their length as inputs to the APIs
and we have added unit tests with buffers of varying lengths (small
to huge) to verify API behavior which is an indirect and simplest way of
fuzz testing to detect buffer overflows.
* Add the tests for OEMCrypto APIs with prefix `OEMCryptoMemory` in the
following format. Huge length is set at 100 MB as of now.
```cpp
for (size_t length=small_length; length<huge_length; length=length * 2) {
Create buffer of size length.
Call api expecting it not to crash or segfault.
}
```
* Add tests for OEMCrypto APIs with out of range values for length and offsets
of OEMCryptoSubstring struct. This length and offset fields are used to read
values from an input buffer in most of the APIs. This can cause buffer
overflows if the length and offset fields are not validated against the
input buffer.
## Background
* Security is the top priority for Widevine. We came up with a simple approach
to catch most common issues with widevine's implementations. A simplest
approach is to add OEMCrypto unit tests to verify OEMCrypto API behavior
when the parameters are out of range, meaning for an unreasonable length
which can cause buffer overflows. Most of the implementation either does not
validate input length parameters or copies data to secure buffers out of TA
space causing memory corruptions, buffer overflows. Partners who implement
OEMCrypto implementations will run OEMCrypto unit tests as part of the
process.
* We have added unit tests with parameters that can cause buffer overflows if
the parameters are not validated. This way partners can catch issues
earlier in the process when they run OEMCrypto unit tests. All the unit
tests with prefix `OEMCryptoMemory` are added to test the above scenario.
## What to expect from these tests
* `OEMCryptoMemory*` tests are designed to fail if API doesn't have enough
validations around input buffer lengths, parameters or OEMCryptoSubstring
struct. If the API doesn't have validations which might lead to a crash, the
test fails with a segfault or an appropriate crash message based on the API
implementation.
* Find out for what buffer length, the API is crashing and then debugging the
test against the OEMCrypto implementation should be able to provide
information about the error.
* Another way to debug would be to compile the tests with sanitizer flags,
which will be able to provide detailed information about the crash.
* Partners are expected to fix issues with the API so that the tests don't
fail.
* As these tests run for varying lengths from small to huge buffer lengths,
some of the tests might take longer to run(~3 minutes).
* `OEMCryptoMemoryInstallKeyboxForHugeKeyboxBuffer*` tests which tries to
call install keybox API with varying buffer lengths. This test by default
is not compiled as it overwrites the keybox on the device. Uncomment,
compile and run the tests only if you have ability to recover the keybox
on device where the test is ran.

View File

@@ -4,10 +4,10 @@
* Run fuzzing on OEMCrypto public APIs on linux using google supported
clusterfuzz infrastructure to find security vulnerabilities.
Design Document - https://docs.google.com/document/d/1mdSV2irJZz5Y9uYb5DmSIddBjrAIZU9q8G5Q_BGpA4I/edit?usp=sharing
Fuzzing at google -
Fuzzing at google -
[go/fuzzing](https://g3doc.corp.google.com/security/fuzzing/g3doc/fuzzing_resources.md?cl=head)
## Monitoring
### Cluster fuzz statistics
@@ -18,7 +18,7 @@
The options to select are `Job type: libfuzzer_asan_oemcrypto` and `Fuzzer:
fuzzer name you are looking for`
Example: [load_license_fuzz](https://clusterfuzz.corp.google.com/fuzzer-stats?group_by=by-day&date_start=2020-07-11&date_end=2020-07-17&fuzzer=libFuzzer_oemcrypto_load_license_fuzz&job=libfuzzer_asan_oemcrypto)
### Issues filed by clusterfuzz - Fixing those issues
@@ -104,17 +104,13 @@
oemcrypto_fuzztests.gypi cflags_cc in order to generate additional debug
information locally.
* Build and test fuzz scripts locally using:
* Build and test fuzz scripts locally using following commands. The build
script builds fuzz binaries for both oemcrypto reference implementation
as well as odkitee implementation.
```shell
$ export CXX=clang++
$ export CC=clang
$ export GYP_DEFINES="clang=1"
$ cd /path/to/cdm/repo
$ export PATH_TO_CDM_DIR=.
$ gyp --format=ninja --depth=$(pwd) \
oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp
$ ninja -C out/Default/
$ cd PATH_TO_CDM_DIR
$ ./oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests
$ mkdir /tmp/new_interesting_corpus
$ ./out/Default/fuzzer_binary /tmp/new_interesting_corpus \
/path/to/fuzz/seed/corpus/folder
@@ -127,8 +123,9 @@
$ ./out/Default/fuzzer_binary crash_input_file
```
## Adding a new OEMCrypto fuzz script
* In order to fuzz a new OEMCrypto API in future, a fuzz script can be added to
oemcrypto/test/fuzz_tests folder which ends with _fuzz.cc.
* In order to fuzz a new OEMCrypto API in future, a fuzz script can be added
to oemcrypto/test/fuzz_tests folder which starts with oemcrypto and ends
with fuzz.cc(GCB build script for oemcrypto fuzzers expects the format).
* In the program, define the function LLVMFuzzerTestOneInput with the following signature:
```
@@ -164,50 +161,14 @@
### Adding a new fuzz script to the build script:
* In order to update build script such as adding a new fuzzer to build script,
we need to update the build script in docker image from cloud repository.
[Build script.](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker
/cloud_build/oemcrypto/release/ubuntu/fuzz/build.sh)
* As long as a new fuzz script is added which starts with oemcrypto and ends
with fuzz, the build command can be added to build_oemcrypto_fuzztests.
GCB script uses build_oemcrypto_fuzztests script to build fuzz binaries
and make them available for clusterfuzz to run continuously.
Add the new fuzz script name to fuzzers variable and follow steps in README
to upload new docker image. Make sure you update the tag to be higher than
latest version in GCR.
Run the following command from your machine to update the docker image tag
in the git trigger.
```shell
stubby call --rpc_creds_file=/tmp/mint.txt \
blade:alphasource-ci-proctor-metadata-service-prod \
ProctorMetadataService.UpdateTrigger --proto2 <<EOF
trigger {
cloud_project_number: 257246079067
name: "cdm-git-trigger"
id: "e8939c9a-d971-4c05-91b5-e0544abf872b"
state: LIVE
git_trigger {
url: "https://widevine-internal.googlesource.com/cdm"
branch_name: "master"
}
build_configs {
build {
steps {
name: "gcr.io/google.com/blockbuster-1154/
cloud-build-oemcrypto-release-ubuntu-fuzz:LATEST_TAG_VERSION"
}
}
}
result_config {
email_config {
notify_condition {
condition: ON_FAILURE
}
to_address: "wideving-engprod@google.com"
}
}
}
EOF
```
* If the new fuzzer cannot follow the naming convention OR GCB script needs
to be updated for any other reason, refer to [this section](https://docs.google.com/document/d/1mdSV2irJZz5Y9uYb5DmSIddBjrAIZU9q8G5Q_BGpA4I/edit#heading=h.bu9yfftdonkg)
section.
## Generate code coverage reports locally
@@ -244,4 +205,4 @@
* Once the build job is successful, latest code coverage reports can be
downloaded from [GCS](https://pantheon.corp.google.com/storage/browser/oemcrypto_fuzzing_code_coverage_reports;tab=objects?forceOnBucketsSortingFiltering=false&project=google.com:blockbuster-1154&prefix=).
The coverage report folder uploaded to GCS is appended with timestamp.
The coverage report folder uploaded to GCS is appended with timestamp.

View File

@@ -0,0 +1,16 @@
#!/bin/bash
set -ex
export CXX=clang++
export CC=clang
export GYP_DEFINES="$GYP_DEFINES clang=1"
export PATH_TO_CDM_DIR=.
gyp --format=ninja --depth=$(pwd) oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp
ninja -C out/Default
# oemcrypto_odkitee_fuzztests.gypi has flags to instrument all the gyp targets
# with fuzzer flags.
gyp --format=ninja --depth=$(pwd) \
--include=oemcrypto/test/fuzz_tests/oemcrypto_odkitee_fuzztests.gypi \
oemcrypto/test/fuzz_tests/oemcrypto_odkitee_fuzztests.gyp
ninja -C out/Default

View File

@@ -7,6 +7,7 @@
#include "log.h"
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
#include "oemcrypto_overflow.h"
namespace wvoec {
const size_t MAX_FUZZ_SAMPLE_SIZE = 5 * MB;
@@ -137,13 +138,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Copy sub sample data.
sample_descriptions[i].subsamples = &subsamples[input_subsample_index];
input_subsample_index += sample_descriptions[i].subsamples_length;
if (AddOverflowUX(input_subsample_index,
sample_descriptions[i].subsamples_length,
&input_subsample_index)) {
return 0;
}
if (input_subsample_index > subsamples.size()) return 0;
} // Sample loop.
// Allocate input/output buffers for each sample description.
vector<OEMCrypto_SharedMemory> input_buffer(total_input_data_length);
RAND_bytes(input_buffer.data(), total_input_data_length);
size_t input_buffer_index = 0;
for (size_t i = 0; i < samples_length; i++) {
sample_descriptions[i].buffers.input_data =

View File

@@ -5,22 +5,65 @@
# Builds under the CDM ./build.py (target platform) build system
# Refer to the distribution package's README for details.
{
'target_defaults': {
'type': 'executable',
'includes': [
'oemcrypto_fuzztests.gypi',
],
},
'variables': {
'oemcrypto_lib%': '',
'openssl_config%': 'system',
'openssl_target%': '',
# Flag to select appropriate underlying oemcrypto implementation when
# buiding fuzz binaries.
'oemcrypto_implementation_version%': 'reference',
},
'targets': [
{
'target_name': 'wv_ce_cdm_oemcrypto_generate_signature_fuzz_test',
'type': 'executable',
'target_name': 'oemcrypto_load_license_fuzz',
'sources': [
# The test runner and the testing device certificate.
'oemcrypto_generate_signature.cc',
'oemcrypto_load_license_fuzz.cc',
],
'includes': [
'oemcrypto_fuzztests.gypi',
},
{
'target_name': 'oemcrypto_load_provisioning_fuzz',
'sources': [
'oemcrypto_load_provisioning_fuzz.cc',
],
},
},
{
'target_name': 'oemcrypto_load_renewal_fuzz',
'sources': [
'oemcrypto_load_renewal_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_license_request_fuzz',
'sources': [
'oemcrypto_license_request_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_provisioning_request_fuzz',
'sources': [
'oemcrypto_provisioning_request_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_renewal_request_fuzz',
'sources': [
'oemcrypto_renewal_request_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_decrypt_cenc_fuzz',
'sources': [
'oemcrypto_decrypt_cenc_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_load_entitled_content_keys_fuzz',
'sources': [
'oemcrypto_load_entitled_content_keys_fuzz.cc',
],
},
],
}

View File

@@ -1,48 +1,101 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
#source code may only be used and distributed under the Widevine Master License
#Agreement.
#
# Include this in any custom unit test targets.
# Does not include the test runner main.
{
'variables': {
'boringssl_libcrypto_path%': '../../../third_party/boringssl/boringssl.gyp:crypto',
'boringssl_libssl_path%': '../../../third_party/boringssl/boringssl.gyp:ssl',
'oemcrypto_dir': '../..',
'platform_specific_dir': '../../../linux/src',
'privacy_crypto_impl%': 'boringssl',
# Flag used to generate source based code coverage reports.
'generate_code_coverage_report%': 'false',
'util_dir': '../../../util',
},
'sources': [
'../../odk/src/core_message_deserialize.cpp',
'../../odk/src/core_message_serialize.cpp',
'../oec_device_features.cpp',
'../oec_key_deriver.cpp',
'../oemcrypto_corpus_generator_helper.cpp',
'../oec_session_util.cpp',
'../oemcrypto_corpus_generator_helper.cpp',
'oemcrypto_fuzz_helper.cc',
'../oemcrypto_session_tests_helper.cpp',
'../oemcrypto_session_tests_helper.h',
'../../../cdm/test/device_cert.cpp',
'../../../cdm/test/device_cert.h',
'<(platform_specific_dir)/file_store.cpp',
'<(platform_specific_dir)/log.cpp',
'<(util_dir)/src/platform.cpp',
'<(util_dir)/src/rw_lock.cpp',
'<(util_dir)/src/string_conversions.cpp',
'<(util_dir)/test/test_sleep.cpp',
'<(util_dir)/test/test_clock.cpp',
],
'include_dirs': [
'../../../core/include', # log.h
'../../include',
'../../ref/src', # oemcrypto_key_ref.h
'../',
'../../../cdm/test',
'../../../third_party/fuzz',
'<(util_dir)/include',
'<(util_dir)/test',
'<(oemcrypto_dir)/include',
'<(oemcrypto_dir)/test',
'<(oemcrypto_dir)/test/fuzz_tests',
'<(oemcrypto_dir)/odk/include',
'<(oemcrypto_dir)/odk/src',
'<(oemcrypto_dir)/odkitee/oemcrypto_ta',
],
'defines': [
'OEMCRYPTO_TESTS',
'OEMCRYPTO_FUZZ_TESTS',
],
'libraries': [
'../../../third_party/fuzz/platforms/x86-64/libFuzzer.a',
'includes': [
'../../../util/libssl_dependency.gypi',
],
'dependencies': [
'../../../cdm/cdm.gyp:widevine_ce_cdm_shared',
'../../../third_party/gmock.gyp:gmock',
'../../../third_party/gmock.gyp:gtest',
'../../../third_party/gmock.gyp:gmock',
],
'defines': [
'OEMCRYPTO_FUZZ_TESTS',
],
'conditions': [
['oemcrypto_lib==""', {
'includes': [
'../../ref/oec_ref.gypi',
],
}, {
'libraries': [
'../../../third_party/fuzz/platforms/x86-64/libFuzzer.a',
'<(oemcrypto_lib)',
],
['generate_code_coverage_report=="false"', {
# Include flags to build fuzzer binaries for cluster fuzz.
'cflags_cc': [
'-std=c++11',
'-fsanitize=fuzzer,address,undefined',
# Need -g flag to include source line numbers in error stack trace.
'-g',
],
'ldflags': [
'-fPIC',
'-fsanitize=fuzzer,address,undefined',
],
}],
['generate_code_coverage_report=="true"', {
# Include flags to build fuzzer binaries to generate source based code coverage reports.
'cflags_cc': [
'-std=c++11',
'-fprofile-instr-generate',
'-fcoverage-mapping',
],
'ldflags': [
'-fPIC',
'-fsanitize=fuzzer,address,undefined',
'-fprofile-instr-generate',
'-fcoverage-mapping',
],
}],
['oemcrypto_implementation_version=="reference"', {
# Include oemcrypto reference implementation code for building reference
# implementation fuzz binaries.
'includes': [
'../../ref/oec_ref.gypi',
],
}],
['oemcrypto_implementation_version=="odkitee"', {
# Include oemcrypto odkitee implementation code for building odkitee
# implementation fuzz binaries.
'dependencies': [
'../../odkitee/oemcrypto_ta/oemcrypto_ta.gyp:oemcrypto_ta',
],
}],
], # conditions
'libraries': [
'-lpthread',
],
}

View File

@@ -0,0 +1,77 @@
#include <stdio.h>
#include "dispatcher.h"
#include "marshaller_base.h"
#include "transport_interface.h"
namespace wvoec {
void InitializeODKMessage(ODK_Message* message, uint8_t* data, size_t size) {
ODK_Message_Impl* impl = (ODK_Message_Impl*)message;
impl->base = data;
impl->size = size;
impl->capacity = size;
impl->read_offset = 0;
impl->status = MESSAGE_STATUS_OK;
}
void OpenOEMCryptoTASession() {
ODK_Message request;
ODK_Message* response = NULL;
uint8_t response_buffer[0x1000];
uint8_t request_body[] = {
0x06, // TAG_UINT32
0x09, 0x00, 0x00, 0x00, // API value (0x09)
0x01, // TAG_BOOL
0x00, // value (false)
0x0a // TAG_EOM
};
InitializeODKMessage(&request, request_body, sizeof(request_body));
ODK_DispatchMessage(&request, &response);
if (response != NULL) ODK_Transport_DeallocateMessage(response);
}
void InitializeOEMCryptoTA() {
ODK_Message init_request;
ODK_Message* init_response = NULL;
uint8_t response_buffer[0x1000];
uint8_t init_request_body[] = {
0x06, // TAG_UINT32
0x01, 0x00, 0x00, 0x00, // API value(0x01)
0x0a // TAG_EOM
};
InitializeODKMessage(&init_request, init_request_body,
sizeof(init_request_body));
ODK_DispatchMessage(&init_request, &init_response);
if (init_response != NULL) ODK_Transport_DeallocateMessage(init_response);
}
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
ODK_InitializeDispatcher();
InitializeOEMCryptoTA();
OpenOEMCryptoTASession();
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ODK_Message request;
ODK_Message* response = NULL;
unsigned char response_buffer[0x1000];
uint8_t* input = new uint8_t[size];
memcpy(input, data, size);
InitializeODKMessage(&request, input, size);
ODK_DispatchMessage(&request, &response);
if (response != NULL) ODK_Transport_DeallocateMessage(response);
delete[] input;
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,85 @@
# Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master
# License Agreement.
{
'target_defaults': {
'type': 'executable',
'includes': [
'oemcrypto_fuzztests.gypi',
],
},
'variables': {
# Flag to select appropriate underlying oemcrypto implementation when
# buiding fuzz binaries.
'oemcrypto_implementation_version%': 'odkitee',
'oemcrypto_dir': '../..',
},
'targets': [
{
'target_name': 'oemcrypto_odkitee_load_license_fuzz',
'sources': [
'oemcrypto_load_license_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_load_provisioning_fuzz',
'sources': [
'oemcrypto_load_provisioning_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_load_renewal_fuzz',
'sources': [
'oemcrypto_load_renewal_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_license_request_fuzz',
'sources': [
'oemcrypto_license_request_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_provisioning_request_fuzz',
'sources': [
'oemcrypto_provisioning_request_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_renewal_request_fuzz',
'sources': [
'oemcrypto_renewal_request_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_decrypt_cenc_fuzz',
'sources': [
'oemcrypto_decrypt_cenc_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_load_entitled_content_keys_fuzz',
'sources': [
'oemcrypto_load_entitled_content_keys_fuzz.cc',
],
},
{
'target_name': 'oemcrypto_odkitee_dispatcher_fuzz',
'include_dirs': [
'<(oemcrypto_dir)/odkitee/serialization/common',
'<(oemcrypto_dir)/odkitee/serialization/os_interfaces',
'<(oemcrypto_dir)/odkitee/serialization/tee',
'<(oemcrypto_dir)/odkitee/serialization/tee/include',
'<(oemcrypto_dir)/odkitee/serialization/ports/trusty/include/',
],
'dependencies': [
'<(oemcrypto_dir)/odkitee/serialization/tee/tee.gyp:odkitee_tee',
],
'sources': [
'oemcrypto_odkitee_dispatcher_fuzz.cc',
'<(oemcrypto_dir)/odkitee/serialization/test/transport_interface.c',
'<(oemcrypto_dir)/odkitee/serialization/ports/trusty/serialization_adapter/shared_memory.c',
],
},
],
}

View File

@@ -0,0 +1,42 @@
# Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master
# License Agreement.
# gypi file to be included using --includes option while building oemcrypto
# odkitee fuzz binaries. Odkitee classes needs to be instrumented with fuzzer
# but only when being built for fuzzing. Instead of directly updating
# oemcrypto_ta.gyp target, we use gypi in build_oemcrypto_fuzztests script.
{
'target_defaults': {
'variables': {
# Flag used to generate source based code coverage reports.
'generate_code_coverage_report%': 'false',
# Flag to indicate that the code is being built with libFuzzer
# instrumentation enabled.
'enable_fuzzing_instrumentation': 'true',
},
# Include flags to build fuzzer binaries to generate source based code coverage reports.
'cflags': [
'-fsanitize=fuzzer,address,undefined',
# Need -g flag to include source line numbers in error stack trace.
'-g',
],
'ldflags': [
'-fPIC',
'-fsanitize=fuzzer,address,undefined',
],
'conditions': [
['generate_code_coverage_report=="true"', {
# Include flags to build fuzzer binaries to generate source based code coverage reports.
'cflags': [
'-fprofile-instr-generate',
'-fcoverage-mapping',
],
'ldflags': [
'-fprofile-instr-generate',
'-fcoverage-mapping',
],
}],
],
},
}

View File

@@ -0,0 +1,78 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master
# License Agreement.
{
# Here you can set platform-specific compiler settings.
'target_defaults': {
# These are flags passed to the compiler for all C & C++ files.
'cflags': [
'-fsanitize=address',
'-fsanitize-coverage=trace-pc-guard',
'-fPIC',
],
# These are flags passed to the compiler for plain C only.
'cflags_c': [
'-fsanitize-coverage=trace-pc-guard',
'-fsanitize=address',
'-fPIC',
],
# These are flags passed to the compiler for C++ only.
'cflags_cc': [
'-fsanitize-coverage=trace-pc-guard',
'-fsanitize=address',
'-fPIC',
],
# These are flags passed to the linker.
'ldflags': [
'-fsanitize=address',
],
# These are macros set by the compiler.
'defines': [
#'EXAMPLE_MACRO_WITH_NO_VALUE',
#'EXAMPLE_KEY=EXAMPLE_VALUE',
],
# These are additional include paths to search for headers.
'include_dirs': [
],
'target_conditions': [
['_toolset == "host"', {
# These are settings specifically for the host toolchain.
# The extra equals sign in the key name instructs gyp to replace
# the generic settings above rather than append to them.
'cflags=': [
'-fsanitize-coverage=trace-pc-guard',
'-fsanitize=address',
'-fPIC',
],
'cflags_c=': [
'-fsanitize-coverage=trace-pc-guard',
'-fsanitize=address',
'-fPIC',
],
'cflags_cc=': [
'-fsanitize-coverage=trace-pc-guard',
'-fsanitize=address',
'-fPIC',
],
'ldflags=': [
'-fsanitize=address',
],
'defines=': [
],
'include_dirs=': [
],
}], # end _toolset == "host" condition
], # end target_conditions
}, # end target_defaults
}

View File

@@ -144,26 +144,28 @@ Test_PST_Report::Test_PST_Report(const std::string& pst_in,
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
class CoreResponse, class ResponseData>
void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
ResponseData>::SignAndVerifyRequest() {
OEMCryptoResult
RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse, ResponseData>::
SignAndCreateRequestWithCustomBufferLengths(bool verify_request) {
// In the real world, a message should be signed by the client and
// verified by the server. This simulates that.
size_t gen_signature_length = 0;
size_t core_message_length = 0;
constexpr size_t small_size = 42; // arbitrary.
size_t message_size =
std::max(required_message_size_, core_message_length + small_size);
vector<uint8_t> data(message_size, 0);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
ASSERT_EQ(
PrepAndSignRequest(session()->session_id(), data.data(), data.size(),
&core_message_length, nullptr, &gen_signature_length),
OEMCrypto_ERROR_SHORT_BUFFER);
uint32_t session_id = session()->session_id();
GetDefaultRequestSignatureAndCoreMessageLengths<PrepAndSignRequest>(
session_id, small_size, &gen_signature_length, &core_message_length);
// Used to test request APIs with varying lengths of core message.
core_message_length =
std::max(core_message_length, required_core_message_size_);
// Used to test request APIs with varying lengths of signature.
gen_signature_length =
std::max(gen_signature_length, required_request_signature_size_);
// Make the message buffer a little bigger than the core message, or the
// required size, whichever is larger.
message_size =
size_t message_size =
std::max(required_message_size_, core_message_length + small_size);
data.resize(message_size);
vector<uint8_t> data(message_size);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
if (ShouldGenerateCorpus()) {
WriteRequestApiCorpus<CoreRequest>(gen_signature_length,
@@ -171,17 +173,32 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
}
vector<uint8_t> gen_signature(gen_signature_length);
ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(),
data.size(), &core_message_length,
gen_signature.data(), &gen_signature_length),
OEMCrypto_SUCCESS);
OEMCryptoResult result = PrepAndSignRequest(
session()->session_id(), data.data(), data.size(), &core_message_length,
gen_signature.data(), &gen_signature_length);
// We need to fill in core request and verify signature only for calls other
// than OEMCryptoMemory buffer overflow test. Any test other than buffer
// overflow will pass true.
if (!verify_request || result != OEMCrypto_SUCCESS) return result;
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_GT(data.size(), core_message_length);
std::string core_message(reinterpret_cast<char*>(data.data()),
core_message_length);
FillAndVerifyCoreRequest(core_message);
}
VerifyRequestSignature(data, gen_signature, core_message_length);
return result;
}
template <PrepAndSignRequest_t PrepAndSignRequest>
void GetDefaultRequestSignatureAndCoreMessageLengths(
uint32_t& session_id, const size_t& small_size,
size_t* gen_signature_length, size_t* core_message_length) {
vector<uint8_t> data(small_size);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
ASSERT_EQ(
PrepAndSignRequest(session_id, data.data(), data.size(),
core_message_length, nullptr, gen_signature_length),
OEMCrypto_ERROR_SHORT_BUFFER);
}
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
@@ -306,10 +323,26 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
&encrypted_response_data_);
core_response_.enc_private_key.length =
encrypted_response_data_.rsa_key_length;
SignResponse();
}
// We need this for provisioning response out of range tests where
// core response substring lengths are modified.
void ProvisioningRoundTrip::
EncryptAndSignResponseWithoutUpdatingEncPrivateKeyLength() {
encryptor_.PadAndEncryptProvisioningMessage(&response_data_,
&encrypted_response_data_);
SignResponse();
}
void ProvisioningRoundTrip::SignResponse() {
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_TRUE(
oemcrypto_core_message::serialize::CreateCoreProvisioningResponse(
core_response_, core_request_, &serialized_core_message_));
// Resizing for huge core message length unit tests.
serialized_core_message_.resize(
std::max(required_core_message_size_, serialized_core_message_.size()));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
@@ -689,6 +722,10 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse(
core_response_, core_request_, request_hash_string,
&serialized_core_message_));
// Resize serialize core message to be just big enough or required core
// message size, whichever is larger.
serialized_core_message_.resize(
std::max(required_core_message_size_, serialized_core_message_.size()));
}
// Make the message buffer a just big enough, or the
@@ -701,7 +738,6 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100;
}
ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(),
serialized_core_message_.size());
@@ -716,6 +752,11 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
}
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
return LoadResponse(session, true);
}
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session,
bool verify_keys) {
EXPECT_NE(session, nullptr);
// Write corpus for oemcrypto_load_license_fuzz. Fuzz script expects
// unecnrypted response from license server as input corpus data.
@@ -762,7 +803,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size());
}
if (result == OEMCrypto_SUCCESS) {
if (verify_keys && result == OEMCrypto_SUCCESS) {
// Give the session object a copy of the license truth data so that it can
// call SelectKey, use key control information, and so that it has key data
// to verify decrypt operations.
@@ -861,6 +902,18 @@ void EntitledMessage::MakeOneKey(size_t entitlement_key_index) {
key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv));
}
OEMCrypto_EntitledContentKeyObject* EntitledMessage::entitled_key_array() {
return entitled_key_array_;
}
EntitledContentKeyData* EntitledMessage::entitled_key_data() {
return entitled_key_data_;
}
size_t EntitledMessage::entitled_key_data_size() {
return sizeof(entitled_key_data_);
}
void EntitledMessage::SetEntitlementKeyId(unsigned int index,
const std::string& key_id) {
ASSERT_LT(index, num_keys_);
@@ -884,6 +937,32 @@ OEMCrypto_Substring EntitledMessage::FindSubstring(const void* ptr,
}
void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) {
EncryptContentKey();
ASSERT_EQ(expected_sts,
OEMCrypto_LoadEntitledContentKeys(
license_messages_->session()->session_id(),
reinterpret_cast<const uint8_t*>(entitled_key_data_),
sizeof(entitled_key_data_), num_keys_, entitled_key_array_));
if (expected_sts != OEMCrypto_SUCCESS) {
return;
}
VerifyEntitlementTestKeys();
}
OEMCryptoResult EntitledMessage::LoadKeys(const vector<uint8_t>& message) {
return OEMCrypto_LoadEntitledContentKeys(
license_messages_->session()->session_id(), message.data(),
message.size(), num_keys_, entitled_key_array_);
}
OEMCryptoResult EntitledMessage::LoadKeys() {
return OEMCrypto_LoadEntitledContentKeys(
license_messages_->session()->session_id(),
reinterpret_cast<const uint8_t*>(entitled_key_data_),
sizeof(entitled_key_data_), num_keys_, entitled_key_array_);
}
void EntitledMessage::EncryptContentKey() {
for (size_t i = 0; i < num_keys_; ++i) {
EntitledContentKeyData* key_data = &entitled_key_data_[i];
const size_t entitlement_key_index = key_data->key_index;
@@ -912,15 +991,6 @@ void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) {
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_array_),
num_keys_);
}
ASSERT_EQ(expected_sts,
OEMCrypto_LoadEntitledContentKeys(
license_messages_->session()->session_id(),
reinterpret_cast<const uint8_t*>(entitled_key_data_),
sizeof(entitled_key_data_), num_keys_, entitled_key_array_));
if (expected_sts != OEMCrypto_SUCCESS) {
return;
}
VerifyEntitlementTestKeys();
}
// This function verifies that the key control block reported by OEMCrypto agree
@@ -1035,6 +1105,10 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
} else {
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
core_request_, renewal_duration_seconds_, &serialized_core_message_));
// Resize serialize core message to be just big enough or required core
// message size, whichever is larger.
serialized_core_message_.resize(
std::max(required_core_message_size_, serialized_core_message_.size()));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
@@ -1047,7 +1121,6 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
encrypted_response_[i] = i % 0x100;
}
// Concatenate the core message and the response.
ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(),
serialized_core_message_.size());

View File

@@ -155,12 +155,25 @@ class RoundTrip {
core_response_(),
response_data_(),
encrypted_response_data_(),
required_message_size_(0) {}
required_message_size_(0),
required_core_message_size_(0),
required_request_signature_size_(0) {}
virtual ~RoundTrip() {}
// Have OEMCrypto sign a request message and then verify the signature and the
// core message.
virtual void SignAndVerifyRequest();
virtual void SignAndVerifyRequest() {
// Boolean true generates core request and verifies the request.
// Custom message sizes are 0 by default, so the behavior of following
// functions will be sign and verify request without any custom buffers
// sizes.
ASSERT_EQ(SignAndCreateRequestWithCustomBufferLengths(true),
OEMCrypto_SUCCESS);
}
// Have OEMCrypto sign and call create request APIs. Buffer parameters in API
// can be set to custom values to test with varying lengths of buffers.
virtual OEMCryptoResult SignAndCreateRequestWithCustomBufferLengths(
bool verify_request = false);
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
// License/Provisioning/Renwal request data that can be serialized.
virtual void InjectFuzzedRequestData(uint8_t* data, size_t size);
@@ -189,6 +202,16 @@ class RoundTrip {
// Set the size of the buffer used the encrypted license.
void set_message_size(size_t size) { required_message_size_ = size; }
// Set core message size to test OEMCrypto request APIs for varying core
// message lengths.
void set_core_message_size(size_t size) {
required_core_message_size_ = size;
}
// Set signature size to test OEMCrypto request APIs for varying signature
// lengths.
void set_request_signature_size(size_t size) {
required_request_signature_size_ = size;
}
std::vector<uint8_t>& response_signature() { return response_signature_; }
const std::string& serialized_core_message() const {
return serialized_core_message_;
@@ -217,6 +240,8 @@ class RoundTrip {
// Message buffers will be at least this big. Tests for loading and signing
// messages will increase all buffers to this size.
size_t required_message_size_;
size_t required_core_message_size_;
size_t required_request_signature_size_;
std::vector<uint8_t> response_signature_;
std::string serialized_core_message_;
std::vector<uint8_t> encrypted_response_;
@@ -239,6 +264,8 @@ class ProvisioningRoundTrip
virtual void PrepareSession(const wvoec::WidevineKeybox& keybox);
void CreateDefaultResponse() override;
void EncryptAndSignResponse() override;
void EncryptAndSignResponseWithoutUpdatingEncPrivateKeyLength();
void SignResponse();
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
OEMCryptoResult LoadResponse(Session* session) override;
void VerifyLoadFailed();
@@ -318,6 +345,7 @@ class LicenseRoundTrip
void EncryptAndSignResponse() override;
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
OEMCryptoResult LoadResponse(Session* session) override;
OEMCryptoResult LoadResponse(Session* session, bool verify_keys);
// Reload an offline license into a different session. This derives new mac
// keys and then calls LoadResponse.
OEMCryptoResult ReloadResponse(Session* session);
@@ -442,11 +470,19 @@ class EntitledMessage {
void FillKeyArray();
void MakeOneKey(size_t entitlement_key_index);
void LoadKeys(OEMCryptoResult expected_sts);
OEMCryptoResult LoadKeys(const vector<uint8_t>& message);
OEMCryptoResult LoadKeys();
void EncryptContentKey();
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
uint32_t num_keys() const { return num_keys_; }
void SetEntitlementKeyId(unsigned int index, const std::string& key_id);
// Verify that key control blocks of the loaded keys.
void VerifyEntitlementTestKeys();
OEMCrypto_EntitledContentKeyObject* entitled_key_array();
// Returns entitled_key_data_ which is used as input message buffer to
// load entitled content keys API.
EntitledContentKeyData* entitled_key_data();
size_t entitled_key_data_size();
private:
// Find the offset of the give pointer, relative to |entitled_key_data_|.
@@ -631,6 +667,10 @@ bool ConvertByteToValidBoolean(const bool* in);
template <class CoreRequest>
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
vector<uint8_t>& data);
template <PrepAndSignRequest_t PrepAndSignRequest>
void GetDefaultRequestSignatureAndCoreMessageLengths(
uint32_t& session_id, const size_t& small_size,
size_t* gen_signature_length, size_t* core_message_length);
} // namespace wvoec
#endif // CDM_OEC_SESSION_UTIL_H_

File diff suppressed because it is too large Load Diff