Combine Decrypt Unit Tests.
This commit is contained in:
@@ -71,6 +71,7 @@ static const size_t KEY_ID_SIZE = 16;
|
|||||||
static const size_t KEY_IV_SIZE = 16;
|
static const size_t KEY_IV_SIZE = 16;
|
||||||
static const size_t KEY_PAD_SIZE = 16;
|
static const size_t KEY_PAD_SIZE = 16;
|
||||||
static const size_t KEY_SIZE = 16;
|
static const size_t KEY_SIZE = 16;
|
||||||
|
static const size_t AES_128_BLOCK_SIZE = 16;
|
||||||
static const size_t MAC_KEY_SIZE = 32;
|
static const size_t MAC_KEY_SIZE = 32;
|
||||||
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
|
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
|
||||||
static const size_t SRM_REQUIREMENT_SIZE = 12;
|
static const size_t SRM_REQUIREMENT_SIZE = 12;
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ OEMCryptoResult CryptoEngine::SetDestination(
|
|||||||
default:
|
default:
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
size_t max_allowed = max_output_size();
|
size_t max_allowed = max_sample_size();
|
||||||
if (max_allowed > 0 &&
|
if (max_allowed > 0 &&
|
||||||
(max_allowed < max_length || max_allowed < data_length)) {
|
(max_allowed < max_length || max_allowed < data_length)) {
|
||||||
LOGE("Output too large (or buffer too small).");
|
LOGE("Output too large (or buffer too small).");
|
||||||
|
|||||||
@@ -154,14 +154,14 @@ class CryptoEngine {
|
|||||||
// been applied to the device that fixes a security bug.
|
// been applied to the device that fixes a security bug.
|
||||||
virtual uint8_t config_security_patch_level() { return 0; }
|
virtual uint8_t config_security_patch_level() { return 0; }
|
||||||
|
|
||||||
// If 0 no restriction, otherwise it's the max buffer for DecryptCENC.
|
// If 0 no restriction, otherwise it's the max subsample size for
|
||||||
// This is the same as the max subsample size, not the sample or frame size.
|
// DecryptCENC. This is not the same as the max sample or buffer size.
|
||||||
virtual size_t max_buffer_size() { return 1024 * 100; } // 100 KiB.
|
virtual size_t max_subsample_size() { return 1024 * 100; } // 100 KiB
|
||||||
|
|
||||||
// If 0 no restriction, otherwise it's the max output buffer for DecryptCENC
|
// If 0 no restriction, otherwise it's the max sample size for DecryptCENC.
|
||||||
// and CopyBuffer. This is the same as the max frame or sample size, not the
|
// This is the same as the max input and output buffer size for DecryptCENC
|
||||||
// subsample size.
|
// and CopyBuffer. It is not the same as the max subsample size.
|
||||||
virtual size_t max_output_size() { return 0; }
|
virtual size_t max_sample_size() { return 1024 * 1024; } // 1 MiB
|
||||||
|
|
||||||
virtual bool srm_update_supported() { return false; }
|
virtual bool srm_update_supported() { return false; }
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
#include "OEMCryptoCENC.h"
|
#include "OEMCryptoCENC.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <openssl/cmac.h>
|
#include <openssl/cmac.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include <openssl/hmac.h>
|
#include <openssl/hmac.h>
|
||||||
@@ -51,6 +52,41 @@ uint32_t unaligned_dereference_uint32(const void* unaligned_ptr) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
||||||
|
switch (dest_buffer->type) {
|
||||||
|
case OEMCrypto_BufferType_Clear:
|
||||||
|
dest_buffer->buffer.clear.address += bytes;
|
||||||
|
dest_buffer->buffer.clear.address_length -= bytes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OEMCrypto_BufferType_Secure:
|
||||||
|
dest_buffer->buffer.secure.offset += bytes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OEMCrypto_BufferType_Direct:
|
||||||
|
// Nothing to do for this buffer type.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is
|
||||||
|
// split off and treated as an unsigned 64-bit integer, then incremented by the
|
||||||
|
// number of complete crypto blocks decrypted. The resulting value is then
|
||||||
|
// copied back into the IV over the previous lower half.
|
||||||
|
void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
||||||
|
uint64_t counter;
|
||||||
|
assert(sizeof(*subsample_iv) == wvoec::KEY_IV_SIZE);
|
||||||
|
assert(sizeof(counter) * 2 == sizeof(*subsample_iv));
|
||||||
|
static const size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
||||||
|
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
||||||
|
|
||||||
|
size_t increment =
|
||||||
|
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
||||||
|
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
||||||
|
|
||||||
|
memcpy(&(*subsample_iv)[half_iv_size], &counter, half_iv_size);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvoec_ref {
|
namespace wvoec_ref {
|
||||||
@@ -579,40 +615,21 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_SelectKey(
|
|||||||
return session_ctx->SelectContentKey(key_id_str, cipher_mode);
|
return session_ctx->SelectContentKey(key_id_str, cipher_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC_V15(
|
OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
|
||||||
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
|
OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples,
|
||||||
bool is_encrypted, const uint8_t* iv, size_t block_offset,
|
size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||||
OEMCrypto_DestBufferDesc* out_buffer_descriptor,
|
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
|
||||||
uint8_t subsample_flags) {
|
|
||||||
if (crypto_engine == nullptr) {
|
if (crypto_engine == nullptr) {
|
||||||
LOGE("OEMCrypto_DecryptCENC: OEMCrypto Not Initialized.");
|
LOGE("OEMCrypto_DecryptCENC: OEMCrypto Not Initialized.");
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
}
|
}
|
||||||
if (data_addr == nullptr || data_length == 0 || iv == nullptr ||
|
if (samples == nullptr || samples_length == 0) {
|
||||||
out_buffer_descriptor == nullptr) {
|
LOGE("[OEMCrypto_DecryptCENC(): No samples]");
|
||||||
LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
if (crypto_engine->max_buffer_size() > 0 &&
|
if (pattern == nullptr) {
|
||||||
data_length > crypto_engine->max_buffer_size()) {
|
LOGE("[OEMCrypto_DecryptCENC(): No pattern]");
|
||||||
// For testing reasons only, pretend that this integration only supports
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
// the minimum possible buffer size.
|
|
||||||
LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_BUFFER_TOO_LARGE]");
|
|
||||||
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
|
|
||||||
}
|
}
|
||||||
OEMCryptoResult status = crypto_engine->SetDestination(
|
|
||||||
*out_buffer_descriptor, data_length, subsample_flags);
|
|
||||||
if (status != OEMCrypto_SUCCESS) {
|
|
||||||
LOGE("[OEMCrypto_DecryptCENC(): destination status: %d]", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
#ifndef NDEBUG
|
|
||||||
if (!crypto_engine->ValidRootOfTrust()) {
|
|
||||||
LOGE("[OEMCrypto_DecryptCENC(): ERROR_KEYBOX_INVALID]");
|
|
||||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||||
if (session_ctx == nullptr || !session_ctx->isValid()) {
|
if (session_ctx == nullptr || !session_ctx->isValid()) {
|
||||||
@@ -620,13 +637,107 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC_V15(
|
|||||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult result = session_ctx->DecryptCENC_V15(
|
// Iterate through all the samples and validate them before doing any decrypt
|
||||||
iv, block_offset, pattern, data_addr, data_length, is_encrypted,
|
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
|
||||||
crypto_engine->destination(), out_buffer_descriptor->type,
|
const OEMCrypto_SampleDescription& sample = samples[sample_index];
|
||||||
subsample_flags);
|
|
||||||
if (result != OEMCrypto_SUCCESS) return result;
|
if (sample.buffers.input_data == nullptr ||
|
||||||
return crypto_engine->PushDestination(*out_buffer_descriptor,
|
sample.buffers.input_data_length == 0) {
|
||||||
subsample_flags);
|
LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (crypto_engine->max_sample_size() > 0 &&
|
||||||
|
sample.buffers.input_data_length > crypto_engine->max_sample_size()) {
|
||||||
|
// For testing reasons only, pretend that this integration only supports
|
||||||
|
// the given buffer size.
|
||||||
|
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];
|
||||||
|
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()) {
|
||||||
|
// For testing reasons only, pretend that this integration only supports
|
||||||
|
// the given buffer size.
|
||||||
|
LOGE("[OEMCrypto_DecryptCENC(): Subsample too large]");
|
||||||
|
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
|
||||||
|
}
|
||||||
|
subsample_length_tally += length;
|
||||||
|
}
|
||||||
|
if (subsample_length_tally != sample.buffers.input_data_length) {
|
||||||
|
LOGE(
|
||||||
|
"[OEMCrypto_DecryptCENC(): Sample and subsample lengths do not "
|
||||||
|
"match.]");
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through all the samples and decrypt each one
|
||||||
|
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
|
||||||
|
const OEMCrypto_SampleDescription& sample = samples[sample_index];
|
||||||
|
|
||||||
|
// Iterate through all the subsamples and decrypt each one
|
||||||
|
const uint8_t* subsample_source = sample.buffers.input_data;
|
||||||
|
OEMCrypto_DestBufferDesc subsample_dest = sample.buffers.output_descriptor;
|
||||||
|
uint8_t subsample_iv[wvoec::KEY_IV_SIZE];
|
||||||
|
assert(sizeof(sample.iv) == wvoec::KEY_IV_SIZE);
|
||||||
|
assert(sizeof(subsample_iv) == wvoec::KEY_IV_SIZE);
|
||||||
|
memcpy(&subsample_iv[0], &sample.iv[0], wvoec::KEY_IV_SIZE);
|
||||||
|
for (size_t subsample_index = 0; subsample_index < sample.subsamples_length;
|
||||||
|
++subsample_index) {
|
||||||
|
const OEMCrypto_SubSampleDescription& subsample =
|
||||||
|
sample.subsamples[subsample_index];
|
||||||
|
const size_t subsample_length =
|
||||||
|
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||||
|
|
||||||
|
OEMCryptoResult result = crypto_engine->SetDestination(
|
||||||
|
subsample_dest, subsample_length, subsample.subsample_flags);
|
||||||
|
if (result != OEMCrypto_SUCCESS) {
|
||||||
|
LOGE("[OEMCrypto_DecryptCENC(): SetDestination status: %d]", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (!crypto_engine->ValidRootOfTrust()) {
|
||||||
|
LOGE("[OEMCrypto_DecryptCENC(): ERROR_KEYBOX_INVALID]");
|
||||||
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
result = session_ctx->DecryptSubsample(
|
||||||
|
subsample, subsample_source, crypto_engine->destination(),
|
||||||
|
subsample_dest.type, subsample_iv, pattern);
|
||||||
|
if (result != OEMCrypto_SUCCESS) {
|
||||||
|
LOGE("[OEMCrypto_DecryptCENC(): DecryptSubsample status: %d]", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = crypto_engine->PushDestination(subsample_dest,
|
||||||
|
subsample.subsample_flags);
|
||||||
|
if (result != OEMCrypto_SUCCESS) {
|
||||||
|
LOGE("[OEMCrypto_DecryptCENC(): PushDestination status: %d]", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the source buffer, the dest buffer, and (if necessary) the IV
|
||||||
|
subsample_source += subsample_length;
|
||||||
|
advance_dest_buffer(&subsample_dest, subsample_length);
|
||||||
|
if (subsample.num_bytes_encrypted > 0 &&
|
||||||
|
session_ctx->current_content_key()->ctr_mode()) {
|
||||||
|
advance_iv_ctr(&subsample_iv,
|
||||||
|
subsample.block_offset + subsample.num_bytes_encrypted);
|
||||||
|
}
|
||||||
|
} // Subsample loop
|
||||||
|
} // Sample loop
|
||||||
|
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
|
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||||
@@ -641,8 +752,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
|
|||||||
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
if (crypto_engine->max_buffer_size() > 0 &&
|
if (crypto_engine->max_sample_size() > 0 &&
|
||||||
data_length > crypto_engine->max_buffer_size()) {
|
data_length > crypto_engine->max_sample_size()) {
|
||||||
// For testing reasons only, pretend that this integration only supports
|
// For testing reasons only, pretend that this integration only supports
|
||||||
// the minimum possible buffer size.
|
// the minimum possible buffer size.
|
||||||
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_BUFFER_TOO_LARGE]");
|
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_BUFFER_TOO_LARGE]");
|
||||||
|
|||||||
@@ -1484,15 +1484,29 @@ bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult SessionContext::DecryptCENC_V15(
|
OEMCryptoResult SessionContext::DecryptSubsample(
|
||||||
const uint8_t* iv, size_t block_offset,
|
const OEMCrypto_SubSampleDescription& subsample, const uint8_t* cipher_data,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted,
|
|
||||||
uint8_t* clear_data, OEMCryptoBufferType buffer_type,
|
uint8_t* clear_data, OEMCryptoBufferType buffer_type,
|
||||||
uint8_t subsample_flags) {
|
const uint8_t iv[wvoec::KEY_IV_SIZE],
|
||||||
OEMCryptoResult result =
|
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||||
ChooseDecrypt(iv, block_offset, pattern, cipher_data, cipher_data_length,
|
// Handle the clear portion of the subsample.
|
||||||
is_encrypted, clear_data, buffer_type);
|
if (subsample.num_bytes_clear > 0) {
|
||||||
|
if (buffer_type != OEMCrypto_BufferType_Direct) {
|
||||||
|
memmove(clear_data, cipher_data, subsample.num_bytes_clear);
|
||||||
|
}
|
||||||
|
// For the reference implementation, we quietly drop the clear direct video.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the encrypted portion of the subsample.
|
||||||
|
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||||
|
if (subsample.num_bytes_encrypted > 0) {
|
||||||
|
const uint8_t* source = cipher_data + subsample.num_bytes_clear;
|
||||||
|
uint8_t* dest = clear_data + subsample.num_bytes_clear;
|
||||||
|
result = ChooseDecrypt(iv, subsample.block_offset, pattern, source,
|
||||||
|
subsample.num_bytes_encrypted, dest, buffer_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute hash for FDPT.
|
||||||
if (compute_hash_) {
|
if (compute_hash_) {
|
||||||
if (current_content_key() == nullptr ||
|
if (current_content_key() == nullptr ||
|
||||||
(current_content_key()->control().control_bits() &
|
(current_content_key()->control().control_bits() &
|
||||||
@@ -1503,12 +1517,13 @@ OEMCryptoResult SessionContext::DecryptCENC_V15(
|
|||||||
current_hash_ = 0;
|
current_hash_ = 0;
|
||||||
current_frame_number_ = 0;
|
current_frame_number_ = 0;
|
||||||
} else {
|
} else {
|
||||||
if (OEMCrypto_FirstSubsample & subsample_flags) {
|
if (OEMCrypto_FirstSubsample & subsample.subsample_flags) {
|
||||||
current_hash_ = wvcrc32Init();
|
current_hash_ = wvcrc32Init();
|
||||||
}
|
}
|
||||||
current_hash_ =
|
current_hash_ = wvcrc32Cont(
|
||||||
wvcrc32Cont(clear_data, cipher_data_length, current_hash_);
|
clear_data, subsample.num_bytes_clear + subsample.num_bytes_encrypted,
|
||||||
if (OEMCrypto_LastSubsample & subsample_flags) {
|
current_hash_);
|
||||||
|
if (OEMCrypto_LastSubsample & subsample.subsample_flags) {
|
||||||
if (current_hash_ != given_hash_) {
|
if (current_hash_ != given_hash_) {
|
||||||
LOGE("CRC for frame %d is %08x, should be %08x\n",
|
LOGE("CRC for frame %d is %08x, should be %08x\n",
|
||||||
current_frame_number_, current_hash_, given_hash_);
|
current_frame_number_, current_hash_, given_hash_);
|
||||||
@@ -1522,25 +1537,17 @@ OEMCryptoResult SessionContext::DecryptCENC_V15(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the result of the previous ChooseDecrypt() call after computing the
|
||||||
|
// hash.
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult SessionContext::ChooseDecrypt(
|
OEMCryptoResult SessionContext::ChooseDecrypt(
|
||||||
const uint8_t* iv, size_t block_offset,
|
const uint8_t* iv, size_t block_offset,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data,
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted,
|
size_t cipher_data_length, uint8_t* clear_data,
|
||||||
uint8_t* clear_data, OEMCryptoBufferType buffer_type) {
|
OEMCryptoBufferType buffer_type) {
|
||||||
// If the data is clear, we do not need a current key selected.
|
|
||||||
if (!is_encrypted) {
|
|
||||||
if (buffer_type != OEMCrypto_BufferType_Direct) {
|
|
||||||
memmove(reinterpret_cast<uint8_t*>(clear_data), cipher_data,
|
|
||||||
cipher_data_length);
|
|
||||||
return OEMCrypto_SUCCESS;
|
|
||||||
}
|
|
||||||
// For reference implementation, we quietly drop the clear direct video.
|
|
||||||
return OEMCrypto_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check there is a content key
|
// Check there is a content key
|
||||||
if (current_content_key() == nullptr) {
|
if (current_content_key() == nullptr) {
|
||||||
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||||
@@ -1565,41 +1572,42 @@ OEMCryptoResult SessionContext::ChooseDecrypt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!current_content_key()->ctr_mode()) {
|
if (!current_content_key()->ctr_mode()) {
|
||||||
if (block_offset > 0) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
if (block_offset > 0 || pattern->encrypt == 0) {
|
||||||
return DecryptCBC(key_u8, iv, pattern, cipher_data, cipher_data_length,
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
clear_data);
|
|
||||||
}
|
}
|
||||||
if (pattern->skip > 0) {
|
return PatternDecryptCBC(key_u8, iv, pattern, cipher_data,
|
||||||
return PatternDecryptCTR(key_u8, iv, block_offset, pattern, cipher_data,
|
|
||||||
cipher_data_length, clear_data);
|
cipher_data_length, clear_data);
|
||||||
|
} else {
|
||||||
|
if (pattern->skip != 0 || pattern->encrypt != 0) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
return DecryptCTR(key_u8, iv, block_offset, cipher_data, cipher_data_length,
|
return DecryptCTR(key_u8, iv, block_offset, cipher_data, cipher_data_length,
|
||||||
clear_data);
|
clear_data);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OEMCryptoResult SessionContext::DecryptCBC(
|
OEMCryptoResult SessionContext::PatternDecryptCBC(
|
||||||
const uint8_t* key, const uint8_t* initial_iv,
|
const uint8_t* key, const uint8_t* initial_iv,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data,
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length,
|
size_t cipher_data_length, uint8_t* clear_data) {
|
||||||
uint8_t* clear_data) {
|
|
||||||
AES_KEY aes_key;
|
AES_KEY aes_key;
|
||||||
AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key);
|
AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key);
|
||||||
uint8_t iv[AES_BLOCK_SIZE];
|
uint8_t iv[AES_BLOCK_SIZE];
|
||||||
uint8_t next_iv[AES_BLOCK_SIZE];
|
uint8_t next_iv[AES_BLOCK_SIZE];
|
||||||
memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE);
|
memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
const size_t pattern_length = pattern->encrypt + pattern->skip;
|
||||||
|
if (pattern_length <= 0) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
|
||||||
size_t l = 0;
|
size_t l = 0;
|
||||||
// TODO(b/135285640): remove this.
|
|
||||||
size_t pattern_offset = 0;
|
size_t pattern_offset = 0;
|
||||||
while (l < cipher_data_length) {
|
while (l < cipher_data_length) {
|
||||||
size_t size =
|
size_t size =
|
||||||
std::min(cipher_data_length - l, static_cast<size_t>(AES_BLOCK_SIZE));
|
std::min(cipher_data_length - l, static_cast<size_t>(AES_BLOCK_SIZE));
|
||||||
size_t pattern_length = pattern->encrypt + pattern->skip;
|
bool skip_block = (pattern_offset >= pattern->encrypt);
|
||||||
bool skip_block =
|
|
||||||
(pattern_offset >= pattern->encrypt) && (pattern_length > 0);
|
|
||||||
if (pattern_length > 0) {
|
|
||||||
pattern_offset = (pattern_offset + 1) % pattern_length;
|
pattern_offset = (pattern_offset + 1) % pattern_length;
|
||||||
}
|
// TODO(b/140503351): The (size < AES_BLOCK_SIZE) check is not correct for
|
||||||
|
// patterns where (pattern.encrypt > 1).
|
||||||
if (skip_block || (size < AES_BLOCK_SIZE)) {
|
if (skip_block || (size < AES_BLOCK_SIZE)) {
|
||||||
memmove(&clear_data[l], &cipher_data[l], size);
|
memmove(&clear_data[l], &cipher_data[l], size);
|
||||||
} else {
|
} else {
|
||||||
@@ -1618,46 +1626,6 @@ OEMCryptoResult SessionContext::DecryptCBC(
|
|||||||
return OEMCrypto_SUCCESS;
|
return OEMCrypto_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult SessionContext::PatternDecryptCTR(
|
|
||||||
const uint8_t* key, const uint8_t* initial_iv, size_t block_offset,
|
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length,
|
|
||||||
uint8_t* clear_data) {
|
|
||||||
AES_KEY aes_key;
|
|
||||||
AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key);
|
|
||||||
uint8_t iv[AES_BLOCK_SIZE];
|
|
||||||
memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE);
|
|
||||||
|
|
||||||
size_t l = 0;
|
|
||||||
// TODO(b/135285640): remove this.
|
|
||||||
size_t pattern_offset = 0;
|
|
||||||
while (l < cipher_data_length) {
|
|
||||||
size_t size =
|
|
||||||
std::min(cipher_data_length - l, AES_BLOCK_SIZE - block_offset);
|
|
||||||
size_t pattern_length = pattern->encrypt + pattern->skip;
|
|
||||||
bool skip_block =
|
|
||||||
(pattern_offset >= pattern->encrypt) && (pattern_length > 0);
|
|
||||||
if (pattern_length > 0) {
|
|
||||||
pattern_offset = (pattern_offset + 1) % pattern_length;
|
|
||||||
}
|
|
||||||
if (skip_block) {
|
|
||||||
memmove(&clear_data[l], &cipher_data[l], size);
|
|
||||||
} else {
|
|
||||||
uint8_t aes_output[AES_BLOCK_SIZE];
|
|
||||||
AES_encrypt(iv, aes_output, &aes_key);
|
|
||||||
for (size_t n = 0; n < size; n++) {
|
|
||||||
clear_data[l + n] = aes_output[n + block_offset] ^ cipher_data[l + n];
|
|
||||||
}
|
|
||||||
ctr128_inc64(iv);
|
|
||||||
}
|
|
||||||
l += size;
|
|
||||||
block_offset = 0;
|
|
||||||
}
|
|
||||||
return OEMCrypto_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a special case of PatternDecryptCTR with no skip pattern. It uses
|
|
||||||
// more optimized versions of openssl's implementation of AES CTR mode.
|
|
||||||
OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8,
|
OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8,
|
||||||
const uint8_t* iv,
|
const uint8_t* iv,
|
||||||
size_t block_offset,
|
size_t block_offset,
|
||||||
|
|||||||
@@ -96,12 +96,11 @@ class SessionContext {
|
|||||||
virtual bool ValidateMessage(const uint8_t* message, size_t message_length,
|
virtual bool ValidateMessage(const uint8_t* message, size_t message_length,
|
||||||
const uint8_t* signature,
|
const uint8_t* signature,
|
||||||
size_t signature_length);
|
size_t signature_length);
|
||||||
OEMCryptoResult DecryptCENC_V15(
|
OEMCryptoResult DecryptSubsample(
|
||||||
const uint8_t* iv, size_t block_offset,
|
const OEMCrypto_SubSampleDescription& subsample,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
const uint8_t* cipher_data, uint8_t* clear_data,
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted,
|
OEMCryptoBufferType buffer_type, const uint8_t iv[wvoec::KEY_IV_SIZE],
|
||||||
uint8_t* clear_data, OEMCryptoBufferType buffer_type,
|
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||||
uint8_t subsample_flags);
|
|
||||||
|
|
||||||
OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer,
|
OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer,
|
||||||
size_t buffer_length, const uint8_t* iv,
|
size_t buffer_length, const uint8_t* iv,
|
||||||
@@ -237,19 +236,14 @@ class SessionContext {
|
|||||||
OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control);
|
OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control);
|
||||||
// Check that the usage entry status is valid for offline use.
|
// Check that the usage entry status is valid for offline use.
|
||||||
OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control);
|
OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control);
|
||||||
OEMCryptoResult ChooseDecrypt(
|
OEMCryptoResult ChooseDecrypt(const uint8_t* iv, size_t block_offset,
|
||||||
const uint8_t* iv, size_t block_offset,
|
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
const uint8_t* cipher_data,
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted,
|
size_t cipher_data_length, uint8_t* clear_data,
|
||||||
uint8_t* clear_data, OEMCryptoBufferType buffer_type);
|
OEMCryptoBufferType buffer_type);
|
||||||
OEMCryptoResult DecryptCBC(
|
OEMCryptoResult PatternDecryptCBC(
|
||||||
const uint8_t* key, const uint8_t* iv,
|
const uint8_t* key, const uint8_t* iv,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length,
|
|
||||||
uint8_t* clear_data);
|
|
||||||
OEMCryptoResult PatternDecryptCTR(
|
|
||||||
const uint8_t* key, const uint8_t* iv, size_t block_offset,
|
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
|
||||||
const uint8_t* cipher_data, size_t cipher_data_length,
|
const uint8_t* cipher_data, size_t cipher_data_length,
|
||||||
uint8_t* clear_data);
|
uint8_t* clear_data);
|
||||||
OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv,
|
OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv,
|
||||||
|
|||||||
177
oemcrypto/test/oec_decrypt_fallback_chain.cpp
Normal file
177
oemcrypto/test/oec_decrypt_fallback_chain.cpp
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#include "oec_decrypt_fallback_chain.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "oemcrypto_types.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
||||||
|
switch (dest_buffer->type) {
|
||||||
|
case OEMCrypto_BufferType_Clear:
|
||||||
|
dest_buffer->buffer.clear.address += bytes;
|
||||||
|
dest_buffer->buffer.clear.address_length -= bytes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OEMCrypto_BufferType_Secure:
|
||||||
|
dest_buffer->buffer.secure.offset += bytes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OEMCrypto_BufferType_Direct:
|
||||||
|
// Nothing to do for this buffer type.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
||||||
|
uint64_t counter;
|
||||||
|
constexpr size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
||||||
|
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
||||||
|
|
||||||
|
size_t increment =
|
||||||
|
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
||||||
|
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
||||||
|
|
||||||
|
memcpy(&(*subsample_iv)[half_iv_size], &counter, half_iv_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace wvoec {
|
||||||
|
|
||||||
|
// Decrypts the given array of samples. Handles fallback behavior correctly if
|
||||||
|
// the OEMCrypto implementation does not accept multiple samples.
|
||||||
|
OEMCryptoResult DecryptFallbackChain::Decrypt(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription* samples,
|
||||||
|
size_t samples_length, OEMCryptoCipherMode cipher_mode,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||||
|
OEMCryptoResult sts =
|
||||||
|
OEMCrypto_DecryptCENC(session_id, samples, samples_length, pattern);
|
||||||
|
|
||||||
|
// No need for a fallback. Abort early.
|
||||||
|
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||||
|
|
||||||
|
// Fall back to decrypting individual samples.
|
||||||
|
for (size_t i = 0; i < samples_length; ++i) {
|
||||||
|
sts = DecryptSample(session_id, samples[i], cipher_mode, pattern);
|
||||||
|
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypts the given sample. Handles fallback behavior correctly if the
|
||||||
|
// OEMCrypto implementation does not accept full samples.
|
||||||
|
OEMCryptoResult DecryptFallbackChain::DecryptSample(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||||
|
OEMCryptoCipherMode cipher_mode,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||||
|
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||||
|
|
||||||
|
// No need for a fallback. Abort early.
|
||||||
|
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||||
|
|
||||||
|
// Fall back to decrypting individual subsamples.
|
||||||
|
OEMCrypto_SampleDescription fake_sample = sample;
|
||||||
|
for (size_t i = 0; i < sample.subsamples_length; ++i) {
|
||||||
|
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[i];
|
||||||
|
|
||||||
|
size_t length = subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||||
|
fake_sample.buffers.input_data_length = length;
|
||||||
|
fake_sample.subsamples = &subsample;
|
||||||
|
fake_sample.subsamples_length = 1;
|
||||||
|
|
||||||
|
sts = DecryptSubsample(session_id, fake_sample, pattern);
|
||||||
|
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||||
|
|
||||||
|
fake_sample.buffers.input_data += length;
|
||||||
|
advance_dest_buffer(&fake_sample.buffers.output_descriptor, length);
|
||||||
|
if (cipher_mode == OEMCrypto_CipherMode_CTR) {
|
||||||
|
advance_iv_ctr(&fake_sample.iv,
|
||||||
|
subsample.block_offset + subsample.num_bytes_encrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypts the given subsample. Handles fallback behavior correctly if the
|
||||||
|
// OEMCrypto implementation does not accept full subsamples.
|
||||||
|
OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||||
|
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||||
|
|
||||||
|
// No need for a fallback. Abort early.
|
||||||
|
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||||
|
|
||||||
|
// Fall back to decrypting individual subsample halves.
|
||||||
|
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
|
||||||
|
OEMCrypto_SampleDescription fake_sample = sample;
|
||||||
|
OEMCrypto_SubSampleDescription fake_subsample;
|
||||||
|
fake_sample.subsamples = &fake_subsample;
|
||||||
|
fake_sample.subsamples_length = 1;
|
||||||
|
|
||||||
|
if (subsample.num_bytes_clear > 0) {
|
||||||
|
fake_sample.buffers.input_data_length = subsample.num_bytes_clear;
|
||||||
|
fake_subsample.num_bytes_clear = subsample.num_bytes_clear;
|
||||||
|
fake_subsample.num_bytes_encrypted = 0;
|
||||||
|
fake_subsample.block_offset = 0;
|
||||||
|
|
||||||
|
fake_subsample.subsample_flags = 0;
|
||||||
|
if (subsample.subsample_flags & OEMCrypto_FirstSubsample)
|
||||||
|
fake_subsample.subsample_flags |= OEMCrypto_FirstSubsample;
|
||||||
|
if (subsample.subsample_flags & OEMCrypto_LastSubsample &&
|
||||||
|
subsample.num_bytes_encrypted == 0)
|
||||||
|
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
||||||
|
|
||||||
|
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
|
||||||
|
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||||
|
|
||||||
|
// Advance the buffers for the other half, in case they're needed.
|
||||||
|
fake_sample.buffers.input_data += subsample.num_bytes_clear;
|
||||||
|
advance_dest_buffer(&fake_sample.buffers.output_descriptor,
|
||||||
|
subsample.num_bytes_clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subsample.num_bytes_encrypted > 0) {
|
||||||
|
fake_sample.buffers.input_data_length = subsample.num_bytes_encrypted;
|
||||||
|
fake_subsample.num_bytes_clear = 0;
|
||||||
|
fake_subsample.num_bytes_encrypted = subsample.num_bytes_encrypted;
|
||||||
|
fake_subsample.block_offset = subsample.block_offset;
|
||||||
|
|
||||||
|
fake_subsample.subsample_flags = 0;
|
||||||
|
if (subsample.subsample_flags & OEMCrypto_FirstSubsample &&
|
||||||
|
subsample.num_bytes_clear == 0)
|
||||||
|
fake_subsample.subsample_flags |= OEMCrypto_FirstSubsample;
|
||||||
|
if (subsample.subsample_flags & OEMCrypto_LastSubsample)
|
||||||
|
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
||||||
|
|
||||||
|
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
|
||||||
|
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypts the given subsample half. There is no fallback behavior after this;
|
||||||
|
// an OEMCrypto_ERROR_BUFFER_TOO_LARGE produced here will be returned to the
|
||||||
|
// caller.
|
||||||
|
OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||||
|
return OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||||
|
// In a real CDM, you would want some fallback here to handle the case where
|
||||||
|
// the buffer is too big for the OEMCrypto implementation. But in the case of
|
||||||
|
// the tests, we won't be passing a buffer that's too big unless we are trying
|
||||||
|
// to test that failure condition, so there's no need to handle that case
|
||||||
|
// here.
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wvoec
|
||||||
59
oemcrypto/test/oec_decrypt_fallback_chain.h
Normal file
59
oemcrypto/test/oec_decrypt_fallback_chain.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||||
|
// source code may only be used and distributed under the Widevine Master
|
||||||
|
// License Agreement.
|
||||||
|
|
||||||
|
#ifndef CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||||
|
#define CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||||
|
|
||||||
|
#include "OEMCryptoCENC.h"
|
||||||
|
#include "disallow_copy_and_assign.h"
|
||||||
|
|
||||||
|
namespace wvoec {
|
||||||
|
|
||||||
|
// This class groups static methods relating to providing proper fallback
|
||||||
|
// behavior when calling DecryptCENC in OEMCrypto v16. Outside code can leverage
|
||||||
|
// this behavior by passing the samples to be decrypted to Decrypt(), which will
|
||||||
|
// set off the chain of fallback functions as needed.
|
||||||
|
//
|
||||||
|
// The behavior of this class is pathological. For each block of data, it will
|
||||||
|
// greedily try every possible way of passing data to OEMCrypto until one works.
|
||||||
|
// In the order tried, the ways to send data are:
|
||||||
|
// 1) Multiple Samples at once
|
||||||
|
// 2) Individual Samples one at a time
|
||||||
|
// 3) Individual Subsamples one at a time
|
||||||
|
// 4) Individual Half-Subsamples one at a time
|
||||||
|
// On a device that only accepts half-subsamples, the way OEMCrypto v15 did,
|
||||||
|
// this results in many needless roundtrips to OEMCrypto. This would be
|
||||||
|
// inefficient behavior for a real CDM, but for the sake of testing, we want to
|
||||||
|
// use the maximal way the OEMCrypto implementation will accept the data. And,
|
||||||
|
// for implementations that do not accept multiple samples or subsamples per
|
||||||
|
// call, we want to test that they correctly reject larger calls.
|
||||||
|
class DecryptFallbackChain {
|
||||||
|
public:
|
||||||
|
static OEMCryptoResult Decrypt(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription* samples,
|
||||||
|
size_t samples_length, OEMCryptoCipherMode cipher_mode,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static OEMCryptoResult DecryptSample(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||||
|
OEMCryptoCipherMode cipher_mode,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||||
|
|
||||||
|
static OEMCryptoResult DecryptSubsample(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||||
|
|
||||||
|
static OEMCryptoResult DecryptSubsampleHalf(
|
||||||
|
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||||
|
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||||
|
|
||||||
|
// There is no reason to have an instance of this class.
|
||||||
|
DecryptFallbackChain() = delete;
|
||||||
|
CORE_DISALLOW_COPY_AND_ASSIGN(DecryptFallbackChain);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wvoec
|
||||||
|
|
||||||
|
#endif // CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||||
@@ -51,6 +51,8 @@ void DeleteX509Stack(STACK_OF(X509)* stack) {
|
|||||||
sk_X509_pop_free(stack, X509_free);
|
sk_X509_pop_free(stack, X509_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr size_t kTestSubsampleSectionSize = 256;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvoec {
|
namespace wvoec {
|
||||||
@@ -60,6 +62,39 @@ int GetRandBytes(unsigned char* buf, int num) {
|
|||||||
return RAND_bytes(buf, num);
|
return RAND_bytes(buf, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Does the boilerplate to fill out sample and subsample descriptions for
|
||||||
|
// decrypting a single contiguous block of encrypted data to clear memory, which
|
||||||
|
// is a common operation for tests. Generates a random IV which can be used to
|
||||||
|
// encrypt the input buffer.
|
||||||
|
void GenerateSimpleSampleDescription(
|
||||||
|
const uint8_t* input_data, size_t input_data_length, uint8_t* output_buffer,
|
||||||
|
size_t output_buffer_length, OEMCrypto_SampleDescription* sample,
|
||||||
|
OEMCrypto_SubSampleDescription* subsample) {
|
||||||
|
ASSERT_NE(nullptr, sample);
|
||||||
|
ASSERT_NE(nullptr, subsample);
|
||||||
|
|
||||||
|
// Generate test data
|
||||||
|
EXPECT_EQ(1, GetRandBytes(&sample->iv[0], KEY_IV_SIZE));
|
||||||
|
|
||||||
|
// Describe the test data
|
||||||
|
sample->buffers.input_data = input_data;
|
||||||
|
sample->buffers.input_data_length = input_data_length;
|
||||||
|
subsample->num_bytes_clear = 0;
|
||||||
|
subsample->num_bytes_encrypted = input_data_length;
|
||||||
|
subsample->subsample_flags =
|
||||||
|
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
|
||||||
|
subsample->block_offset = 0;
|
||||||
|
sample->subsamples = subsample;
|
||||||
|
sample->subsamples_length = 1;
|
||||||
|
|
||||||
|
// Describe the output
|
||||||
|
OEMCrypto_DestBufferDesc& out_buffer_descriptor =
|
||||||
|
sample->buffers.output_descriptor;
|
||||||
|
out_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
|
||||||
|
out_buffer_descriptor.buffer.clear.address = output_buffer;
|
||||||
|
out_buffer_descriptor.buffer.clear.address_length = output_buffer_length;
|
||||||
|
}
|
||||||
|
|
||||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||||
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
||||||
// is different from the BoringSSL implementation, so we implement the CTR loop
|
// is different from the BoringSSL implementation, so we implement the CTR loop
|
||||||
@@ -1445,43 +1480,35 @@ void Session::TestDecryptCTR(bool select_key_first,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<uint8_t> unencryptedData(256);
|
// Create test sample description
|
||||||
for (size_t i = 0; i < unencryptedData.size(); i++)
|
vector<uint8_t> unencrypted_data(kTestSubsampleSectionSize);
|
||||||
unencryptedData[i] = i % 256;
|
vector<uint8_t> encrypted_data(unencrypted_data.size());
|
||||||
EXPECT_EQ(1, GetRandBytes(unencryptedData.data(), unencryptedData.size()));
|
vector<uint8_t> output_buffer(unencrypted_data.size());
|
||||||
vector<uint8_t> encryptionIv(KEY_IV_SIZE);
|
OEMCrypto_SampleDescription sample_description;
|
||||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), KEY_IV_SIZE));
|
OEMCrypto_SubSampleDescription subsample_description;
|
||||||
vector<uint8_t> encryptedData(unencryptedData.size());
|
|
||||||
EncryptCTR(unencryptedData, license_.keys[key_index].key_data,
|
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
|
||||||
encryptionIv.data(), &encryptedData);
|
encrypted_data.data(), encrypted_data.size(), output_buffer.data(),
|
||||||
|
output_buffer.size(), &sample_description, &subsample_description));
|
||||||
|
|
||||||
|
// Generate test data
|
||||||
|
EXPECT_EQ(1, GetRandBytes(unencrypted_data.data(), unencrypted_data.size()));
|
||||||
|
EncryptCTR(unencrypted_data, license_.keys[key_index].key_data,
|
||||||
|
&sample_description.iv[0], &encrypted_data);
|
||||||
|
|
||||||
|
// Create the pattern description (always 0,0 for CTR)
|
||||||
|
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
|
||||||
|
|
||||||
// Describe the output
|
|
||||||
vector<uint8_t> outputBuffer(256);
|
|
||||||
OEMCrypto_DestBufferDesc out_buffer_descriptor;
|
|
||||||
out_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
|
|
||||||
out_buffer_descriptor.buffer.clear.address = outputBuffer.data();
|
|
||||||
out_buffer_descriptor.buffer.clear.address_length = outputBuffer.size();
|
|
||||||
OEMCrypto_CENCEncryptPatternDesc pattern;
|
|
||||||
pattern.encrypt = 0;
|
|
||||||
pattern.skip = 0;
|
|
||||||
// Decrypt the data
|
// Decrypt the data
|
||||||
#if 1 // TODO(b/135285640): Until the DecryptCENC is fixed, we
|
sts = OEMCrypto_DecryptCENC(session_id(), &sample_description, 1, &pattern);
|
||||||
// just copy the truth data to the outputBuffer, and claim success.
|
|
||||||
sts = expected_result;
|
|
||||||
if (expected_result == OEMCrypto_SUCCESS) outputBuffer = unencryptedData;
|
|
||||||
#else
|
|
||||||
sts = OEMCrypto_DecryptCENC(
|
|
||||||
session_id(), encryptedData.data(), encryptedData.size(), true,
|
|
||||||
encryptionIv.data(), 0, &out_buffer_descriptor, &pattern,
|
|
||||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
|
||||||
#endif
|
|
||||||
// We only have a few errors that we test are reported.
|
// We only have a few errors that we test are reported.
|
||||||
if (expected_result == OEMCrypto_SUCCESS) { // No error.
|
if (expected_result == OEMCrypto_SUCCESS) { // No error.
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
ASSERT_EQ(unencryptedData, outputBuffer);
|
ASSERT_EQ(unencrypted_data, output_buffer);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_NO_FATAL_FAILURE(TestDecryptResult(expected_result, sts));
|
ASSERT_NO_FATAL_FAILURE(TestDecryptResult(expected_result, sts));
|
||||||
ASSERT_NE(unencryptedData, outputBuffer);
|
ASSERT_NE(unencrypted_data, output_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,13 @@ struct EntitledContentKeyData {
|
|||||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||||
int GetRandBytes(unsigned char* buf, int num);
|
int GetRandBytes(unsigned char* buf, int num);
|
||||||
|
|
||||||
|
void GenerateSimpleSampleDescription(const uint8_t* input_data,
|
||||||
|
size_t input_data_length,
|
||||||
|
uint8_t* output_buffer,
|
||||||
|
size_t output_buffer_length,
|
||||||
|
OEMCrypto_SampleDescription* sample,
|
||||||
|
OEMCrypto_SubSampleDescription* subsample);
|
||||||
|
|
||||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||||
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
||||||
// is different from the OpenSSL implementation, so we implement the CTR loop
|
// is different from the OpenSSL implementation, so we implement the CTR loop
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "OEMCryptoCENC.h"
|
#include "OEMCryptoCENC.h"
|
||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "oec_decrypt_fallback_chain.h"
|
||||||
#include "oec_device_features.h"
|
#include "oec_device_features.h"
|
||||||
#include "oec_session_util.h"
|
#include "oec_session_util.h"
|
||||||
#include "oec_test_data.h"
|
#include "oec_test_data.h"
|
||||||
@@ -95,12 +96,15 @@ T GetResourceValue(T (&resource_values)[N]) {
|
|||||||
if (global_features.resource_rating > N) return resource_values[N-1];
|
if (global_features.resource_rating > N) return resource_values[N-1];
|
||||||
return resource_values[global_features.resource_rating-1];
|
return resource_values[global_features.resource_rating-1];
|
||||||
}
|
}
|
||||||
const size_t kMaxSampleSize[] = { 1000*KiB, 2*MiB, 4*MiB};
|
// clang-format off
|
||||||
|
const size_t kMaxSampleSize[] = { 1*MiB, 2*MiB, 4*MiB};
|
||||||
const size_t kMaxNumberSubsamples[] = { 10, 16, 32};
|
const size_t kMaxNumberSubsamples[] = { 10, 16, 32};
|
||||||
const size_t kMaxSubsampleSize[] = { 100*KiB, 500*KiB, 1*MiB};
|
const size_t kMaxSubsampleSize[] = { 100*KiB, 500*KiB, 1*MiB};
|
||||||
const size_t kMaxGenericBuffer[] = { 10*KiB, 100*KiB, 500*KiB};
|
const size_t kMaxGenericBuffer[] = { 10*KiB, 100*KiB, 500*KiB};
|
||||||
const size_t kMaxConcurrentSession[] = { 10, 20, 20};
|
const size_t kMaxConcurrentSession[] = { 10, 20, 20};
|
||||||
const size_t kMaxKeysPerSession[] = { 4, 20, 20};
|
const size_t kMaxKeysPerSession[] = { 4, 20, 20};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Note: Frame rate and simultaneous playback are specified by resource rating,
|
// Note: Frame rate and simultaneous playback are specified by resource rating,
|
||||||
// but are tested at the system level, so there are no unit tests for frame
|
// but are tested at the system level, so there are no unit tests for frame
|
||||||
// rate.
|
// rate.
|
||||||
@@ -1342,30 +1346,123 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI15) {
|
|||||||
// Delayed error code. If select key was a success, then we should
|
// Delayed error code. If select key was a success, then we should
|
||||||
// eventually see the error when we decrypt.
|
// eventually see the error when we decrypt.
|
||||||
vector<uint8_t> in_buffer(256);
|
vector<uint8_t> in_buffer(256);
|
||||||
for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256;
|
|
||||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
|
||||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
|
||||||
// Describe the output
|
|
||||||
vector<uint8_t> out_buffer(in_buffer.size());
|
vector<uint8_t> out_buffer(in_buffer.size());
|
||||||
OEMCrypto_DestBufferDesc destBuffer;
|
OEMCrypto_SampleDescription sample_description;
|
||||||
destBuffer.type = OEMCrypto_BufferType_Clear;
|
OEMCrypto_SubSampleDescription subsample_description;
|
||||||
destBuffer.buffer.clear.address = out_buffer.data();
|
|
||||||
destBuffer.buffer.clear.address_length = out_buffer.size();
|
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
|
||||||
OEMCrypto_CENCEncryptPatternDesc pattern;
|
in_buffer.data(), in_buffer.size(), out_buffer.data(),
|
||||||
pattern.encrypt = 0;
|
out_buffer.size(), &sample_description, &subsample_description));
|
||||||
pattern.skip = 0;
|
|
||||||
// Decrypt the data
|
// Generate test data
|
||||||
#if 0 // TODO(b/135285640): fix this.
|
for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256;
|
||||||
const bool is_encrypted = true;
|
|
||||||
sts = OEMCrypto_DecryptCENC(
|
// Create the pattern description (always 0,0 for CTR)
|
||||||
s.session_id(), in_buffer.data(), in_buffer.size(), is_encrypted,
|
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
|
||||||
encryptionIv.data(), 0, &destBuffer, &pattern,
|
|
||||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
// Try to decrypt the data
|
||||||
#endif
|
sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
|
||||||
|
&pattern);
|
||||||
EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts);
|
EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'cens' mode is no longer supported in v16
|
||||||
|
TEST_F(OEMCryptoSessionTests, RejectCensAPI16) {
|
||||||
|
Session s;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||||
|
|
||||||
|
OEMCryptoResult sts;
|
||||||
|
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id,
|
||||||
|
s.license().keys[0].key_id_length,
|
||||||
|
OEMCrypto_CipherMode_CTR);
|
||||||
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
|
|
||||||
|
vector<uint8_t> in_buffer(256);
|
||||||
|
vector<uint8_t> out_buffer(in_buffer.size());
|
||||||
|
OEMCrypto_SampleDescription sample_description;
|
||||||
|
OEMCrypto_SubSampleDescription subsample_description;
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
|
||||||
|
in_buffer.data(), in_buffer.size(), out_buffer.data(), out_buffer.size(),
|
||||||
|
&sample_description, &subsample_description));
|
||||||
|
|
||||||
|
// Create a non-zero pattern to indicate this is 'cens'
|
||||||
|
OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9};
|
||||||
|
|
||||||
|
// Try to decrypt the data
|
||||||
|
sts = OEMCrypto_DecryptCENC(s.session_id(), &sample_description, 1, &pattern);
|
||||||
|
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'cbc1' mode is no longer supported in v16
|
||||||
|
TEST_F(OEMCryptoSessionTests, RejectCbc1API16) {
|
||||||
|
Session s;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||||
|
|
||||||
|
OEMCryptoResult sts;
|
||||||
|
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id,
|
||||||
|
s.license().keys[0].key_id_length,
|
||||||
|
OEMCrypto_CipherMode_CBC);
|
||||||
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
|
|
||||||
|
vector<uint8_t> in_buffer(256);
|
||||||
|
vector<uint8_t> out_buffer(in_buffer.size());
|
||||||
|
OEMCrypto_SampleDescription sample_description;
|
||||||
|
OEMCrypto_SubSampleDescription subsample_description;
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
|
||||||
|
in_buffer.data(), in_buffer.size(), out_buffer.data(), out_buffer.size(),
|
||||||
|
&sample_description, &subsample_description));
|
||||||
|
|
||||||
|
// Create a zero pattern to indicate this is 'cbc1'
|
||||||
|
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
|
||||||
|
|
||||||
|
// Try to decrypt the data
|
||||||
|
sts = OEMCrypto_DecryptCENC(s.session_id(), &sample_description, 1, &pattern);
|
||||||
|
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OEMCryptoSessionTests, RejectCbcWithBlockOffset) {
|
||||||
|
Session s;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||||
|
|
||||||
|
OEMCryptoResult sts;
|
||||||
|
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id,
|
||||||
|
s.license().keys[0].key_id_length,
|
||||||
|
OEMCrypto_CipherMode_CBC);
|
||||||
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
|
|
||||||
|
vector<uint8_t> in_buffer(256);
|
||||||
|
vector<uint8_t> out_buffer(in_buffer.size());
|
||||||
|
OEMCrypto_SampleDescription sample_description;
|
||||||
|
OEMCrypto_SubSampleDescription subsample_description;
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
|
||||||
|
in_buffer.data(), in_buffer.size(), out_buffer.data(), out_buffer.size(),
|
||||||
|
&sample_description, &subsample_description));
|
||||||
|
subsample_description.block_offset = 5; // Any value 1-15 will do.
|
||||||
|
|
||||||
|
// Create a non-zero pattern to indicate this is 'cbcs'.
|
||||||
|
OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9};
|
||||||
|
|
||||||
|
// Try to decrypt the data
|
||||||
|
sts = OEMCrypto_DecryptCENC(s.session_id(), &sample_description, 1, &pattern);
|
||||||
|
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
|
||||||
|
}
|
||||||
|
|
||||||
// After loading keys, we should be able to query the key control block. If we
|
// After loading keys, we should be able to query the key control block. If we
|
||||||
// attempt to query a key that has not been loaded, the error should be
|
// attempt to query a key that has not been loaded, the error should be
|
||||||
// NO_CONTENT_KEY.
|
// NO_CONTENT_KEY.
|
||||||
@@ -1766,16 +1863,23 @@ TEST_F(OEMCryptoSessionTests, SimultaneousDecrypt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SampleSize {
|
struct SubsampleSize {
|
||||||
size_t clear_size;
|
size_t clear_size;
|
||||||
size_t encrypted_size;
|
size_t encrypted_size;
|
||||||
SampleSize(size_t clear, size_t encrypted)
|
SubsampleSize(size_t clear, size_t encrypted)
|
||||||
: clear_size(clear), encrypted_size(encrypted) {}
|
: clear_size(clear), encrypted_size(encrypted) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SampleInitData {
|
// Struct for holding the data for one test sample in the decrypt tests.
|
||||||
uint8_t iv[AES_BLOCK_SIZE];
|
struct TestSample {
|
||||||
size_t block_offset;
|
// Encrypted data -- this is input to OEMCrypto, and output from EncryptData.
|
||||||
|
std::vector<uint8_t> encrypted_buffer;
|
||||||
|
std::vector<uint8_t> clear_buffer; // OEMCrypto store clear output here.
|
||||||
|
std::vector<uint8_t> truth_buffer; // Truth data for clear text.
|
||||||
|
|
||||||
|
OEMCrypto_SampleDescription description;
|
||||||
|
std::vector<OEMCrypto_SubSampleDescription> subsamples;
|
||||||
|
int secure_buffer_fid;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A class of tests that test decryption for a variety of patterns and modes.
|
// A class of tests that test decryption for a variety of patterns and modes.
|
||||||
@@ -1798,11 +1902,9 @@ class OEMCryptoSessionTestsDecryptTests
|
|||||||
output_buffer_type_ = ::testing::get<2>(GetParam()).type;
|
output_buffer_type_ = ::testing::get<2>(GetParam()).type;
|
||||||
verify_crc_ = global_features.supports_crc;
|
verify_crc_ = global_features.supports_crc;
|
||||||
// Pick a random key.
|
// Pick a random key.
|
||||||
EXPECT_EQ(1, GetRandBytes(key_, AES_BLOCK_SIZE));
|
EXPECT_EQ(1, GetRandBytes(&key_[0], sizeof(key_)));
|
||||||
// Pick a random starting iv. Some tests override this before using it.
|
// Pick a random starting iv. Some tests override this before using it.
|
||||||
starting_iv_.resize(AES_BLOCK_SIZE);
|
EXPECT_EQ(1, GetRandBytes(&initial_iv_[0], sizeof(initial_iv_)));
|
||||||
EXPECT_EQ(1, GetRandBytes(starting_iv_.data(), starting_iv_.size()));
|
|
||||||
total_size_ = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
@@ -1811,83 +1913,109 @@ class OEMCryptoSessionTestsDecryptTests
|
|||||||
OEMCryptoSessionTests::TearDown();
|
OEMCryptoSessionTests::TearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindTotalSize() {
|
void SetSubsampleSizes(std::vector<SubsampleSize> subsample_sizes) {
|
||||||
total_size_ = 0;
|
// This is just sugar for having one sample with the given subsamples in it.
|
||||||
for (size_t i = 0; i < subsample_size_.size(); i++) {
|
SetSampleSizes({subsample_sizes});
|
||||||
total_size_ +=
|
}
|
||||||
subsample_size_[i].clear_size + subsample_size_[i].encrypted_size;
|
|
||||||
|
void SetSampleSizes(std::vector<std::vector<SubsampleSize>> sample_sizes) {
|
||||||
|
ASSERT_GT(sample_sizes.size(), 0u);
|
||||||
|
samples_.reserve(sample_sizes.size());
|
||||||
|
|
||||||
|
// Convert all the size arrays to TestSample structs
|
||||||
|
for (const std::vector<SubsampleSize>& subsample_sizes : sample_sizes) {
|
||||||
|
// This could be one line if we had C++17
|
||||||
|
samples_.emplace_back();
|
||||||
|
TestSample& sample = samples_.back();
|
||||||
|
|
||||||
|
ASSERT_GT(subsample_sizes.size(), 0u);
|
||||||
|
sample.subsamples.reserve(subsample_sizes.size());
|
||||||
|
|
||||||
|
// Convert all the sizes to subsample descriptions and tally the total
|
||||||
|
// size
|
||||||
|
size_t total_size = 0;
|
||||||
|
size_t current_block_offset = 0;
|
||||||
|
for (const SubsampleSize& size : subsample_sizes) {
|
||||||
|
sample.subsamples.push_back(OEMCrypto_SubSampleDescription{
|
||||||
|
size.clear_size, size.encrypted_size,
|
||||||
|
0, // Subsample Flags, to be filled in after the loop
|
||||||
|
current_block_offset});
|
||||||
|
|
||||||
|
// Update the rolling variables
|
||||||
|
total_size += size.clear_size + size.encrypted_size;
|
||||||
|
if (cipher_mode_ == OEMCrypto_CipherMode_CTR) {
|
||||||
|
current_block_offset =
|
||||||
|
(current_block_offset + size.encrypted_size) % AES_BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the input buffer and either a clear or secure output buffer.
|
// Set the subsample flags now that all the subsamples are processed
|
||||||
// This should be called after FindTotalSize().
|
sample.subsamples.front().subsample_flags |= OEMCrypto_FirstSubsample;
|
||||||
|
sample.subsamples.back().subsample_flags |= OEMCrypto_LastSubsample;
|
||||||
|
|
||||||
|
// Set related information on the sample description
|
||||||
|
sample.description.subsamples = sample.subsamples.data();
|
||||||
|
sample.description.subsamples_length = sample.subsamples.size();
|
||||||
|
sample.description.buffers.input_data_length = total_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the input buffer and either a clear or secure output buffer for each
|
||||||
|
// test sample. This should be called after SetSubsampleSizes().
|
||||||
void MakeBuffers() {
|
void MakeBuffers() {
|
||||||
ASSERT_GT(total_size_, 0u);
|
for (TestSample& sample : samples_) {
|
||||||
encrypted_buffer_.resize(total_size_);
|
const size_t total_size = sample.description.buffers.input_data_length;
|
||||||
truth_buffer_.resize(total_size_);
|
ASSERT_GT(total_size, 0u);
|
||||||
for (size_t i = 0; i < total_size_; i++) truth_buffer_[i] = i % 256;
|
sample.encrypted_buffer.resize(total_size);
|
||||||
output_descriptor_.type = output_buffer_type_;
|
sample.truth_buffer.resize(total_size);
|
||||||
switch (output_descriptor_.type) {
|
for (size_t i = 0; i < total_size; i++) sample.truth_buffer[i] = i % 256;
|
||||||
|
|
||||||
|
OEMCrypto_DestBufferDesc& output_descriptor =
|
||||||
|
sample.description.buffers.output_descriptor;
|
||||||
|
output_descriptor.type = output_buffer_type_;
|
||||||
|
switch (output_descriptor.type) {
|
||||||
case OEMCrypto_BufferType_Clear:
|
case OEMCrypto_BufferType_Clear:
|
||||||
if (decrypt_inplace_) {
|
if (decrypt_inplace_) {
|
||||||
output_descriptor_.buffer.clear.address = encrypted_buffer_.data();
|
output_descriptor.buffer.clear.address =
|
||||||
|
sample.encrypted_buffer.data();
|
||||||
} else {
|
} else {
|
||||||
// Add some padding to verify there is no overrun.
|
// Add some padding to verify there is no overrun.
|
||||||
clear_buffer_.resize(total_size_ + 16, 0xaa);
|
sample.clear_buffer.resize(total_size + 16, 0xaa);
|
||||||
output_descriptor_.buffer.clear.address = clear_buffer_.data();
|
output_descriptor.buffer.clear.address = sample.clear_buffer.data();
|
||||||
}
|
}
|
||||||
output_descriptor_.buffer.clear.address_length = total_size_;
|
output_descriptor.buffer.clear.address_length = total_size;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OEMCrypto_BufferType_Secure:
|
case OEMCrypto_BufferType_Secure:
|
||||||
output_descriptor_.buffer.secure.handle_length = total_size_;
|
output_descriptor.buffer.secure.handle_length = total_size;
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_AllocateSecureBuffer(
|
OEMCrypto_AllocateSecureBuffer(
|
||||||
session_.session_id(), total_size_, &output_descriptor_,
|
session_.session_id(), total_size, &output_descriptor,
|
||||||
&secure_buffer_fid_));
|
&sample.secure_buffer_fid));
|
||||||
ASSERT_NE(nullptr, output_descriptor_.buffer.secure.handle);
|
ASSERT_NE(nullptr, output_descriptor.buffer.secure.handle);
|
||||||
// It is OK if OEMCrypto changes the maximum size, but there must still
|
// It is OK if OEMCrypto changes the maximum size, but there must
|
||||||
// be enough room for our data.
|
// still be enough room for our data.
|
||||||
ASSERT_GE(output_descriptor_.buffer.secure.handle_length, total_size_);
|
ASSERT_GE(output_descriptor.buffer.secure.handle_length, total_size);
|
||||||
output_descriptor_.buffer.secure.offset = 0;
|
output_descriptor.buffer.secure.offset = 0;
|
||||||
break;
|
break;
|
||||||
case OEMCrypto_BufferType_Direct:
|
|
||||||
output_descriptor_.buffer.direct.is_video = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ASSERT_TRUE(false) << "Invalid buffer type.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateOutputOffset(size_t offset) {
|
|
||||||
switch (output_descriptor_.type) {
|
|
||||||
case OEMCrypto_BufferType_Clear:
|
|
||||||
if (decrypt_inplace_) {
|
|
||||||
output_descriptor_.buffer.clear.address =
|
|
||||||
encrypted_buffer_.data() + offset;
|
|
||||||
} else {
|
|
||||||
output_descriptor_.buffer.clear.address =
|
|
||||||
clear_buffer_.data() + offset;
|
|
||||||
}
|
|
||||||
output_descriptor_.buffer.clear.address_length = total_size_ - offset;
|
|
||||||
break;
|
|
||||||
case OEMCrypto_BufferType_Secure:
|
|
||||||
ASSERT_NE(nullptr, output_descriptor_.buffer.secure.handle);
|
|
||||||
ASSERT_GE(output_descriptor_.buffer.secure.handle_length, total_size_);
|
|
||||||
output_descriptor_.buffer.secure.offset = offset;
|
|
||||||
break;
|
|
||||||
case OEMCrypto_BufferType_Direct:
|
case OEMCrypto_BufferType_Direct:
|
||||||
|
output_descriptor.buffer.direct.is_video = false;
|
||||||
break;
|
break;
|
||||||
default:
|
} // switch (output_descriptor.type)
|
||||||
ASSERT_TRUE(false) << "Invalid buffer type.";
|
} // sample loop
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeBuffers() {
|
void FreeBuffers() {
|
||||||
if (output_descriptor_.type == OEMCrypto_BufferType_Secure) {
|
for (TestSample& sample : samples_) {
|
||||||
ASSERT_EQ(
|
OEMCrypto_DestBufferDesc& output_descriptor =
|
||||||
OEMCrypto_SUCCESS,
|
sample.description.buffers.output_descriptor;
|
||||||
OEMCrypto_FreeSecureBuffer(session_.session_id(), &output_descriptor_,
|
if (output_descriptor.type == OEMCrypto_BufferType_Secure) {
|
||||||
secure_buffer_fid_));
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
|
OEMCrypto_FreeSecureBuffer(session_.session_id(),
|
||||||
|
&output_descriptor,
|
||||||
|
sample.secure_buffer_fid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1895,27 +2023,29 @@ class OEMCryptoSessionTestsDecryptTests
|
|||||||
AES_KEY aes_key;
|
AES_KEY aes_key;
|
||||||
AES_set_encrypt_key(key_, AES_BLOCK_SIZE * 8, &aes_key);
|
AES_set_encrypt_key(key_, AES_BLOCK_SIZE * 8, &aes_key);
|
||||||
|
|
||||||
uint8_t iv[AES_BLOCK_SIZE]; // Current iv.
|
for (TestSample& sample : samples_) {
|
||||||
memcpy(iv, starting_iv_.data(), AES_BLOCK_SIZE);
|
uint8_t iv[KEY_IV_SIZE]; // Current IV
|
||||||
|
memcpy(&iv[0], &initial_iv_[0], KEY_IV_SIZE);
|
||||||
|
memcpy(&sample.description.iv[0], &initial_iv_[0], KEY_IV_SIZE);
|
||||||
|
|
||||||
size_t buffer_index = 0; // byte index into in and out.
|
size_t buffer_index = 0; // byte index into in and out.
|
||||||
size_t block_offset = 0; // byte index into current block.
|
size_t block_offset = 0; // byte index into current block.
|
||||||
for (size_t i = 0; i < subsample_size_.size(); i++) {
|
for (const OEMCrypto_SubSampleDescription& subsample :
|
||||||
|
sample.subsamples) {
|
||||||
// Copy clear content.
|
// Copy clear content.
|
||||||
if (subsample_size_[i].clear_size > 0) {
|
if (subsample.num_bytes_clear > 0) {
|
||||||
memcpy(&encrypted_buffer_[buffer_index], &truth_buffer_[buffer_index],
|
memcpy(&sample.encrypted_buffer[buffer_index],
|
||||||
subsample_size_[i].clear_size);
|
&sample.truth_buffer[buffer_index], subsample.num_bytes_clear);
|
||||||
buffer_index += subsample_size_[i].clear_size;
|
buffer_index += subsample.num_bytes_clear;
|
||||||
}
|
}
|
||||||
// Save the current iv and offsets for call to DecryptCENC.
|
|
||||||
sample_init_data_.push_back(SampleInitData());
|
|
||||||
memcpy(sample_init_data_[i].iv, iv, AES_BLOCK_SIZE);
|
|
||||||
// Note: final CENC spec specifies the pattern_offset = 0 at the
|
|
||||||
// start of each subsample.
|
|
||||||
size_t pattern_offset = 0;
|
|
||||||
sample_init_data_[i].block_offset = block_offset;
|
|
||||||
|
|
||||||
size_t subsample_end = buffer_index + subsample_size_[i].encrypted_size;
|
// The IV resets at the start of each subsample in the 'cbcs' schema.
|
||||||
|
if (cipher_mode_ == OEMCrypto_CipherMode_CBC) {
|
||||||
|
memcpy(&iv[0], &initial_iv_[0], KEY_IV_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pattern_offset = 0;
|
||||||
|
size_t subsample_end = buffer_index + subsample.num_bytes_encrypted;
|
||||||
while (buffer_index < subsample_end) {
|
while (buffer_index < subsample_end) {
|
||||||
size_t size =
|
size_t size =
|
||||||
min(subsample_end - buffer_index, AES_BLOCK_SIZE - block_offset);
|
min(subsample_end - buffer_index, AES_BLOCK_SIZE - block_offset);
|
||||||
@@ -1928,44 +2058,52 @@ class OEMCryptoSessionTestsDecryptTests
|
|||||||
// CBC mode should just copy a partial block at the end. If there
|
// CBC mode should just copy a partial block at the end. If there
|
||||||
// is a partial block at the beginning, an error is returned, so we
|
// is a partial block at the beginning, an error is returned, so we
|
||||||
// can put whatever we want in the output buffer.
|
// can put whatever we want in the output buffer.
|
||||||
|
//
|
||||||
|
// TODO(b/140503351): The (size < AES_BLOCK_SIZE) check is not correct
|
||||||
|
// for patterns where (pattern.encrypt > 1).
|
||||||
if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBC) &&
|
if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBC) &&
|
||||||
(size < AES_BLOCK_SIZE))) {
|
(size < AES_BLOCK_SIZE))) {
|
||||||
memcpy(&encrypted_buffer_[buffer_index], &truth_buffer_[buffer_index],
|
memcpy(&sample.encrypted_buffer[buffer_index],
|
||||||
size);
|
&sample.truth_buffer[buffer_index], size);
|
||||||
block_offset = 0; // Next block should be complete.
|
block_offset = 0; // Next block should be complete.
|
||||||
} else {
|
} else {
|
||||||
if (cipher_mode_ == OEMCrypto_CipherMode_CTR) {
|
if (cipher_mode_ == OEMCrypto_CipherMode_CTR) {
|
||||||
uint8_t aes_output[AES_BLOCK_SIZE];
|
uint8_t aes_output[AES_BLOCK_SIZE];
|
||||||
AES_encrypt(iv, aes_output, &aes_key);
|
AES_encrypt(iv, aes_output, &aes_key);
|
||||||
for (size_t n = 0; n < size; n++) {
|
for (size_t n = 0; n < size; n++) {
|
||||||
encrypted_buffer_[buffer_index + n] =
|
sample.encrypted_buffer[buffer_index + n] =
|
||||||
aes_output[n + block_offset] ^
|
aes_output[n + block_offset] ^
|
||||||
truth_buffer_[buffer_index + n];
|
sample.truth_buffer[buffer_index + n];
|
||||||
}
|
}
|
||||||
if (size + block_offset < AES_BLOCK_SIZE) {
|
if (size + block_offset < AES_BLOCK_SIZE) {
|
||||||
// Partial block. Don't increment iv. Compute next block offset.
|
// Partial block. Don't increment iv. Compute next block
|
||||||
|
// offset.
|
||||||
block_offset = block_offset + size;
|
block_offset = block_offset + size;
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ(static_cast<size_t>(AES_BLOCK_SIZE),
|
EXPECT_EQ(static_cast<size_t>(AES_BLOCK_SIZE),
|
||||||
block_offset + size);
|
block_offset + size);
|
||||||
// Full block. Increment iv, and set offset to 0 for next block.
|
// Full block. Increment iv, and set offset to 0 for next
|
||||||
|
// block.
|
||||||
ctr128_inc64(1, iv);
|
ctr128_inc64(1, iv);
|
||||||
block_offset = 0;
|
block_offset = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint8_t aes_input[AES_BLOCK_SIZE];
|
uint8_t aes_input[AES_BLOCK_SIZE];
|
||||||
for (size_t n = 0; n < size; n++) {
|
for (size_t n = 0; n < size; n++) {
|
||||||
aes_input[n] = truth_buffer_[buffer_index + n] ^ iv[n];
|
aes_input[n] = sample.truth_buffer[buffer_index + n] ^ iv[n];
|
||||||
}
|
}
|
||||||
AES_encrypt(aes_input, &encrypted_buffer_[buffer_index], &aes_key);
|
AES_encrypt(aes_input, &sample.encrypted_buffer[buffer_index],
|
||||||
memcpy(iv, &encrypted_buffer_[buffer_index], AES_BLOCK_SIZE);
|
&aes_key);
|
||||||
|
memcpy(iv, &sample.encrypted_buffer[buffer_index],
|
||||||
|
AES_BLOCK_SIZE);
|
||||||
// CBC mode should always start on block boundary.
|
// CBC mode should always start on block boundary.
|
||||||
block_offset = 0;
|
block_offset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer_index += size;
|
buffer_index += size;
|
||||||
}
|
} // encryption loop
|
||||||
}
|
} // per-subsample loop
|
||||||
|
} // per-sample loop
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLicense() {
|
void LoadLicense() {
|
||||||
@@ -1989,76 +2127,55 @@ class OEMCryptoSessionTestsDecryptTests
|
|||||||
|
|
||||||
void TestDecryptCENC() {
|
void TestDecryptCENC() {
|
||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
// If supported, initialize the decrypt hash.
|
|
||||||
|
// If supported, check the decrypt hashes.
|
||||||
if (verify_crc_) {
|
if (verify_crc_) {
|
||||||
uint32_t hash = wvcrc32(truth_buffer_.data(), truth_buffer_.size());
|
// OEMCrypto only supports providing a decrypt hash for the first sample
|
||||||
|
// in the sample array.
|
||||||
|
const TestSample& sample = samples_[0];
|
||||||
|
|
||||||
|
uint32_t hash =
|
||||||
|
wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_SetDecryptHash(
|
OEMCrypto_SetDecryptHash(
|
||||||
session_.session_id(), 1,
|
session_.session_id(), 1,
|
||||||
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)));
|
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)));
|
||||||
}
|
}
|
||||||
size_t buffer_offset = 0;
|
|
||||||
for (size_t i = 0; i < subsample_size_.size(); i++) {
|
// Build an array of just the sample descriptions.
|
||||||
// TODO(b/135285640): OEMCrypto_CENCEncryptPatternDesc pattern = pattern_;
|
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
|
||||||
bool is_encrypted = false;
|
sample_descriptions.reserve(samples_.size());
|
||||||
size_t block_offset = 0;
|
for (TestSample& sample : samples_) {
|
||||||
uint8_t subsample_flags = 0;
|
// This must be deferred until this point in case the test modifies the
|
||||||
if (subsample_size_[i].clear_size > 0) {
|
// buffer before testing decrypt.
|
||||||
ASSERT_NO_FATAL_FAILURE(UpdateOutputOffset(buffer_offset));
|
sample.description.buffers.input_data = sample.encrypted_buffer.data();
|
||||||
if (i == 0) subsample_flags |= OEMCrypto_FirstSubsample;
|
// Append to the description array.
|
||||||
if ((i == subsample_size_.size() - 1) &&
|
sample_descriptions.push_back(sample.description);
|
||||||
(subsample_size_[i].encrypted_size == 0)) {
|
|
||||||
subsample_flags |= OEMCrypto_LastSubsample;
|
|
||||||
}
|
}
|
||||||
#if 0 // TODO(b/135285640): fix this.
|
|
||||||
sts = OEMCrypto_DecryptCENC(
|
// Perform decryption using the test data that was previously set up.
|
||||||
session_.session_id(), &encrypted_buffer_[buffer_offset],
|
sts = DecryptFallbackChain::Decrypt(
|
||||||
subsample_size_[i].clear_size, is_encrypted,
|
session_.session_id(), sample_descriptions.data(),
|
||||||
sample_init_data_[i].iv, block_offset, &output_descriptor_,
|
sample_descriptions.size(), cipher_mode_, &pattern_);
|
||||||
&pattern, subsample_flags);
|
|
||||||
#endif
|
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
buffer_offset += subsample_size_[i].clear_size;
|
|
||||||
}
|
// Validate the decrypted data.
|
||||||
if (subsample_size_[i].encrypted_size > 0) {
|
for (TestSample& sample : samples_) {
|
||||||
ASSERT_NO_FATAL_FAILURE(UpdateOutputOffset(buffer_offset));
|
if (sample.description.buffers.output_descriptor.type ==
|
||||||
is_encrypted = true;
|
OEMCrypto_BufferType_Clear) {
|
||||||
block_offset = sample_init_data_[i].block_offset;
|
|
||||||
subsample_flags = 0;
|
|
||||||
if ((i == 0) && (subsample_size_[i].clear_size == 0)) {
|
|
||||||
subsample_flags |= OEMCrypto_FirstSubsample;
|
|
||||||
}
|
|
||||||
if (i == subsample_size_.size() - 1) {
|
|
||||||
subsample_flags |= OEMCrypto_LastSubsample;
|
|
||||||
}
|
|
||||||
#if 0 // TODO(b/135285640): fix this.
|
|
||||||
sts = OEMCrypto_DecryptCENC(
|
|
||||||
session_.session_id(), &encrypted_buffer_[buffer_offset],
|
|
||||||
subsample_size_[i].encrypted_size, is_encrypted,
|
|
||||||
sample_init_data_[i].iv, block_offset, &output_descriptor_,
|
|
||||||
&pattern, subsample_flags);
|
|
||||||
#endif
|
|
||||||
// CBC mode should not accept a block offset.
|
|
||||||
if ((block_offset > 0) && (cipher_mode_ == OEMCrypto_CipherMode_CBC)) {
|
|
||||||
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts)
|
|
||||||
<< "CBC Mode should reject a non-zero block offset.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
|
||||||
buffer_offset += subsample_size_[i].encrypted_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (output_descriptor_.type == OEMCrypto_BufferType_Clear) {
|
|
||||||
if (decrypt_inplace_) {
|
if (decrypt_inplace_) {
|
||||||
// We expect encrypted buffer to have been changed by OEMCrypto.
|
// We expect encrypted buffer to have been changed by OEMCrypto.
|
||||||
EXPECT_EQ(encrypted_buffer_, truth_buffer_);
|
EXPECT_EQ(sample.encrypted_buffer, sample.truth_buffer);
|
||||||
} else {
|
} else {
|
||||||
// If we are not decrypting in place, then look at the one byte just
|
// If we are not decrypting in place, then look at the one byte just
|
||||||
// after the data that was written. It should not have changed from the
|
// after the data that was written. It should not have changed from
|
||||||
// original 0xaa that we set in MakeBuffersession_.
|
// the original 0xaa that we set in MakeBuffersession_.
|
||||||
EXPECT_EQ(0xaa, clear_buffer_[total_size_]) << "Buffer overrun.";
|
const size_t total_size =
|
||||||
clear_buffer_.resize(total_size_); // Remove padding.
|
sample.description.buffers.input_data_length;
|
||||||
EXPECT_EQ(clear_buffer_, truth_buffer_);
|
EXPECT_EQ(0xaa, sample.clear_buffer[total_size]) << "Buffer overrun.";
|
||||||
|
sample.clear_buffer.resize(total_size); // Remove padding.
|
||||||
|
EXPECT_EQ(sample.clear_buffer, sample.truth_buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (global_features.supports_crc) {
|
if (global_features.supports_crc) {
|
||||||
@@ -2068,36 +2185,26 @@ class OEMCryptoSessionTestsDecryptTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parameters of test case
|
||||||
OEMCrypto_CENCEncryptPatternDesc pattern_;
|
OEMCrypto_CENCEncryptPatternDesc pattern_;
|
||||||
OEMCryptoCipherMode cipher_mode_;
|
OEMCryptoCipherMode cipher_mode_;
|
||||||
bool decrypt_inplace_; // If true, input and output buffers are the same.
|
bool decrypt_inplace_; // If true, input and output buffers are the same.
|
||||||
OEMCryptoBufferType output_buffer_type_;
|
OEMCryptoBufferType output_buffer_type_;
|
||||||
vector<SampleSize> subsample_size_;
|
|
||||||
size_t total_size_;
|
|
||||||
bool verify_crc_;
|
bool verify_crc_;
|
||||||
vector<SampleInitData> sample_init_data_;
|
|
||||||
// Encrypted data -- this is input to OEMCrypto, and output from EncryptData.
|
|
||||||
std::vector<uint8_t> encrypted_buffer_;
|
|
||||||
std::vector<uint8_t> clear_buffer_; // OEMCrypto store clear output here.
|
|
||||||
std::vector<uint8_t> truth_buffer_; // Truth data for clear text.
|
|
||||||
OEMCrypto_DestBufferDesc output_descriptor_;
|
|
||||||
int secure_buffer_fid_;
|
|
||||||
uint8_t key_[AES_BLOCK_SIZE]; // Encryption Key.
|
uint8_t key_[AES_BLOCK_SIZE]; // Encryption Key.
|
||||||
std::vector<uint8_t> starting_iv_; // Starting IV.
|
uint8_t initial_iv_[KEY_IV_SIZE]; // Starting IV for every sample.
|
||||||
|
std::vector<TestSample> samples_;
|
||||||
Session session_;
|
Session session_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tests that generate partial ending blocks. These tests should not be used
|
|
||||||
// with CTR mode and pattern encrypt.
|
|
||||||
class OEMCryptoSessionTestsPartialBlockTests
|
|
||||||
: public OEMCryptoSessionTestsDecryptTests {};
|
|
||||||
|
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
|
||||||
// This subsample size is larger than a few encrypt/skip patterns. Most
|
// This subsample size is larger than a few encrypt/skip patterns. Most
|
||||||
// test cases use a pattern length of 160, so we'll run through at least two
|
// test cases use a pattern length of 160, so we'll run through at least two
|
||||||
// full patterns if we have more than 320 -- round up to 400.
|
// full patterns if we have more than 320 -- round up to 400.
|
||||||
subsample_size_.push_back(SampleSize(0, 400));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
FindTotalSize();
|
{0, 400},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2109,8 +2216,9 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
|
|||||||
// We require the CENC standard for OEMCrypto, and let a layer above us break
|
// We require the CENC standard for OEMCrypto, and let a layer above us break
|
||||||
// samples into pieces if they wish to use the HLS standard.
|
// samples into pieces if they wish to use the HLS standard.
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) {
|
||||||
subsample_size_.push_back(SampleSize(0, 160 + 16));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
FindTotalSize();
|
{0, 160 + 16},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2119,8 +2227,9 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) {
|
|||||||
|
|
||||||
// Test that a single block can be decrypted.
|
// Test that a single block can be decrypted.
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) {
|
||||||
subsample_size_.push_back(SampleSize(0, 16));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
FindTotalSize();
|
{0, 16},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2128,13 +2237,13 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This tests the ability to decrypt multiple subsamples with no offset.
|
// This tests the ability to decrypt multiple subsamples with no offset.
|
||||||
// There is no offset within the block, used by CTR mode. However, there might
|
// There is no offset within the block, used by CTR mode.
|
||||||
// be an offset in the encrypt/skip pattern.
|
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) {
|
||||||
subsample_size_.push_back(SampleSize(25, 160));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
subsample_size_.push_back(SampleSize(50, 256));
|
{25, 160},
|
||||||
subsample_size_.push_back(SampleSize(25, 160));
|
{50, 256},
|
||||||
FindTotalSize();
|
{25, 160},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2146,39 +2255,43 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) {
|
|||||||
// the decrypt step.
|
// the decrypt step.
|
||||||
// If this test fails for CTR mode, then it is probably handling the
|
// If this test fails for CTR mode, then it is probably handling the
|
||||||
// block_offset incorrectly.
|
// block_offset incorrectly.
|
||||||
TEST_P(OEMCryptoSessionTestsPartialBlockTests, EvenOffset) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, EvenOffset) {
|
||||||
subsample_size_.push_back(SampleSize(25, 8));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
subsample_size_.push_back(SampleSize(25, 32));
|
{25, 8},
|
||||||
subsample_size_.push_back(SampleSize(25, 50));
|
{25, 32},
|
||||||
FindTotalSize();
|
{25, 50},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
// CTR Mode is self-inverse -- i.e. We can pick the encrypted data and
|
// CTR Mode is self-inverse -- i.e. We can pick the encrypted data and
|
||||||
// compute the unencrypted data. By picking the encrypted data to be all 0,
|
// compute the unencrypted data. By picking the encrypted data to be all 0,
|
||||||
// it is easier to re-encrypt the data and debug problems. Similarly, we
|
// it is easier to re-encrypt the data and debug problems. Similarly, we
|
||||||
// pick an iv = 0.
|
// pick an iv = 0.
|
||||||
starting_iv_.assign(AES_BLOCK_SIZE, 0);
|
memset(&initial_iv_[0], 0, KEY_IV_SIZE);
|
||||||
truth_buffer_.assign(total_size_, 0);
|
TestSample& sample = samples_[0]; // There is only one sample in this test
|
||||||
|
sample.truth_buffer.assign(sample.description.buffers.input_data_length, 0);
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
truth_buffer_ = encrypted_buffer_; // truth_buffer_ = encrypted zero buffer.
|
sample.truth_buffer =
|
||||||
|
sample.encrypted_buffer; // truth_buffer_ = encrypted zero buffer.
|
||||||
// Run EncryptData to re-encrypt this buffer. For CTR mode, we should get
|
// Run EncryptData to re-encrypt this buffer. For CTR mode, we should get
|
||||||
// back to zeros.
|
// back to zeros.
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the EvenOffset test passes, but this one doesn't, then DecryptCTR might
|
// If the EvenOffset test passes, but this one doesn't, then DecryptCENC might
|
||||||
// be using the wrong definition of block offset. Adding the block offset to
|
// be using the wrong definition of block offset. Adding the block offset to
|
||||||
// the block boundary should give you the beginning of the encrypted data.
|
// the block boundary should give you the beginning of the encrypted data.
|
||||||
// This should only work for CTR mode, for CBC mode, the block offset must be
|
// This should only work for CTR mode, for CBC mode, the block offset must be
|
||||||
// 0, so an error is expected in the decrypt step.
|
// 0, so an error is expected in the decrypt step.
|
||||||
// Another way to view the block offset is with the formula:
|
// Another way to view the block offset is with the formula:
|
||||||
// block_boundary + block_offset = beginning of subsample.
|
// block_boundary + block_offset = beginning of subsample.
|
||||||
TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, OddOffset) {
|
||||||
subsample_size_.push_back(SampleSize(10, 50));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
subsample_size_.push_back(SampleSize(10, 75));
|
{10, 50},
|
||||||
subsample_size_.push_back(SampleSize(10, 25));
|
{10, 75},
|
||||||
FindTotalSize();
|
{10, 75},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2196,9 +2309,12 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) {
|
|||||||
// If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you
|
// If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you
|
||||||
// increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000.
|
// increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000.
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) {
|
||||||
starting_iv_ = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
|
memcpy(&initial_iv_[0],
|
||||||
subsample_size_.push_back(SampleSize(0, 256));
|
wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE").data(),
|
||||||
FindTotalSize();
|
KEY_IV_SIZE);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
|
{0, 256},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2208,44 +2324,68 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) {
|
|||||||
// This tests the case where an encrypted sample is not an even number of
|
// This tests the case where an encrypted sample is not an even number of
|
||||||
// blocks. For CTR mode, the partial block is encrypted. For CBC mode the
|
// blocks. For CTR mode, the partial block is encrypted. For CBC mode the
|
||||||
// partial block should be a copy of the clear data.
|
// partial block should be a copy of the clear data.
|
||||||
TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, PartialBlock) {
|
||||||
// Note: for more complete test coverage, we want a sample size that is in
|
// Note: for more complete test coverage, we want a sample size that is in
|
||||||
// the encrypted range for some tests, e.g. (3,7), and in the skip range for
|
// the encrypted range for some tests, e.g. (3,7), and in the skip range for
|
||||||
// other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50.
|
// other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50.
|
||||||
subsample_size_.push_back(SampleSize(0, 50));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
FindTotalSize();
|
{0, 50},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on the resource rating, oemcrypto should handle at least
|
// Based on the resource rating, OEMCrypto should be able to handle the maximum
|
||||||
// kMaxNumberSubsamples na kMaxSampleSize
|
// amount of data that can be passed to it. This is the lesser of:
|
||||||
|
//
|
||||||
|
// 1) The maximum total sample size
|
||||||
|
// 2) The maximum number of subsamples multiplied by the maximum subsample size
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) {
|
||||||
size_t max_size = GetResourceValue(kMaxSampleSize);
|
const size_t max_sample_size = GetResourceValue(kMaxSampleSize);
|
||||||
size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize);
|
const size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize);
|
||||||
size_t num_subsamples = GetResourceValue(kMaxNumberSubsamples);
|
const size_t max_num_subsamples = GetResourceValue(kMaxNumberSubsamples);
|
||||||
if (num_subsamples * max_subsample_size > max_size) {
|
|
||||||
max_subsample_size = max_size / num_subsamples;
|
// The +1 on this line ensures that, even in cases where max_sample_size is
|
||||||
|
// not evenly divisible by max_num_subsamples and thus the division gets
|
||||||
|
// truncated, (max_num_subsamples * subsample_size) will be greater than
|
||||||
|
// max_sample_size.
|
||||||
|
size_t subsample_size = max_sample_size / max_num_subsamples + 1;
|
||||||
|
if (subsample_size > max_subsample_size) {
|
||||||
|
subsample_size = max_subsample_size;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < num_subsamples / 2; i += 2) {
|
|
||||||
subsample_size_.push_back(SampleSize(max_subsample_size, 0));
|
size_t bytes_remaining = max_sample_size;
|
||||||
subsample_size_.push_back(SampleSize(0, max_subsample_size));
|
std::vector<SubsampleSize> subsample_sizes;
|
||||||
|
while (bytes_remaining > 0 && subsample_sizes.size() < max_num_subsamples) {
|
||||||
|
const size_t this_subsample_size =
|
||||||
|
(subsample_size <= bytes_remaining) ? subsample_size : bytes_remaining;
|
||||||
|
const size_t clear_size = this_subsample_size / 2;
|
||||||
|
const size_t encrypted_size = this_subsample_size - clear_size;
|
||||||
|
|
||||||
|
subsample_sizes.push_back({clear_size, encrypted_size});
|
||||||
|
bytes_remaining -= this_subsample_size;
|
||||||
}
|
}
|
||||||
FindTotalSize();
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes(subsample_sizes));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tests that we can decrypt the required maximum number of subsamples.
|
// Based on the resource rating, OEMCrypto should be able to handle the maximum
|
||||||
|
// subsample size.
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
|
||||||
size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize);
|
const size_t max = GetResourceValue(kMaxSubsampleSize);
|
||||||
subsample_size_.push_back(SampleSize(max_subsample_size, 0));
|
const size_t half_max = max / 2;
|
||||||
subsample_size_.push_back(SampleSize(0, max_subsample_size));
|
// This test assumes that the maximum sample size is always more than three
|
||||||
FindTotalSize();
|
// times the maximum subsample size.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
|
{max, 0},
|
||||||
|
{0, max},
|
||||||
|
{half_max, max - half_max},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2254,8 +2394,9 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
|
|||||||
|
|
||||||
// There are probably no frames this small, but we should handle them anyway.
|
// There are probably no frames this small, but we should handle them anyway.
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
|
||||||
subsample_size_.push_back(SampleSize(5, 5));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
FindTotalSize();
|
{5, 5},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2265,8 +2406,9 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
|
|||||||
// Test the case where there is only a clear subsample and no encrypted
|
// Test the case where there is only a clear subsample and no encrypted
|
||||||
// subsample.
|
// subsample.
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) {
|
||||||
subsample_size_.push_back(SampleSize(256, 0));
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
FindTotalSize();
|
{256, 0},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
@@ -2275,11 +2417,12 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) {
|
|||||||
|
|
||||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) {
|
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) {
|
||||||
ASSERT_NO_FATAL_FAILURE(session_.open());
|
ASSERT_NO_FATAL_FAILURE(session_.open());
|
||||||
// Single clear subsample
|
|
||||||
subsample_size_.push_back(SampleSize(400, 0));
|
|
||||||
// Do not try to compute the CRC because we have not loaded a license.
|
// Do not try to compute the CRC because we have not loaded a license.
|
||||||
verify_crc_ = false;
|
verify_crc_ = false;
|
||||||
FindTotalSize();
|
// Single clear subsample
|
||||||
|
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
|
||||||
|
{400, 0},
|
||||||
|
}));
|
||||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
// Clear data should be copied even if there is no key selected, and no
|
// Clear data should be copied even if there is no key selected, and no
|
||||||
// license loaded.
|
// license loaded.
|
||||||
@@ -2288,45 +2431,49 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This tests the ability to decrypt multiple samples at once.
|
||||||
|
TEST_P(OEMCryptoSessionTestsDecryptTests, MultipleSamples) {
|
||||||
|
ASSERT_NO_FATAL_FAILURE(SetSampleSizes({
|
||||||
|
{
|
||||||
|
{52, 160},
|
||||||
|
{25, 256},
|
||||||
|
{25, 320},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{300, 64},
|
||||||
|
{50, 160},
|
||||||
|
{2, 160},
|
||||||
|
{24, 160},
|
||||||
|
{128, 256},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{70, 320},
|
||||||
|
{160, 160},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||||
|
}
|
||||||
|
|
||||||
// Used to construct a specific pattern.
|
// Used to construct a specific pattern.
|
||||||
OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt, size_t skip) {
|
OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt, size_t skip) {
|
||||||
OEMCrypto_CENCEncryptPatternDesc pattern;
|
return {encrypt, skip};
|
||||||
pattern.encrypt = encrypt;
|
|
||||||
pattern.skip = skip;
|
|
||||||
return pattern;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
INSTANTIATE_TEST_CASE_P(
|
||||||
CTRTests, OEMCryptoSessionTestsPartialBlockTests,
|
CTRTests, OEMCryptoSessionTestsDecryptTests,
|
||||||
Combine(Values(MakePattern(0, 0)), Values(OEMCrypto_CipherMode_CTR),
|
Combine(Values(MakePattern(0, 0)), Values(OEMCrypto_CipherMode_CTR),
|
||||||
::testing::ValuesIn(global_features.GetOutputTypes())));
|
::testing::ValuesIn(global_features.GetOutputTypes())));
|
||||||
|
|
||||||
// Decrypt in place for CBC tests was only required in v13.
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
|
||||||
CBCTestsAPI14, OEMCryptoSessionTestsPartialBlockTests,
|
|
||||||
Combine(
|
|
||||||
Values(MakePattern(0, 0), MakePattern(3, 7),
|
|
||||||
// HLS Edge case. We should follow the CENC spec, not HLS spec.
|
|
||||||
MakePattern(9, 1), MakePattern(1, 9), MakePattern(1, 3),
|
|
||||||
MakePattern(2, 1)),
|
|
||||||
Values(OEMCrypto_CipherMode_CBC),
|
|
||||||
::testing::ValuesIn(global_features.GetOutputTypes())));
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
|
||||||
CTRTestsAPI11, OEMCryptoSessionTestsDecryptTests,
|
|
||||||
Combine(Values(MakePattern(0, 0), MakePattern(3, 7),
|
|
||||||
// Pattern length should be 10, but that is not guaranteed.
|
|
||||||
MakePattern(1, 3), MakePattern(2, 1)),
|
|
||||||
Values(OEMCrypto_CipherMode_CTR),
|
|
||||||
::testing::ValuesIn(global_features.GetOutputTypes())));
|
|
||||||
|
|
||||||
// Decrypt in place for CBC tests was only required in v13.
|
// Decrypt in place for CBC tests was only required in v13.
|
||||||
INSTANTIATE_TEST_CASE_P(
|
INSTANTIATE_TEST_CASE_P(
|
||||||
CBCTestsAPI14, OEMCryptoSessionTestsDecryptTests,
|
CBCTestsAPI14, OEMCryptoSessionTestsDecryptTests,
|
||||||
Combine(
|
Combine(
|
||||||
Values(MakePattern(0, 0), MakePattern(3, 7),
|
Values(MakePattern(3, 7), MakePattern(9, 1),
|
||||||
// HLS Edge case. We should follow the CENC spec, not HLS spec.
|
// HLS edge cases. We should follow the CENC spec, not HLS spec.
|
||||||
MakePattern(9, 1), MakePattern(1, 9),
|
MakePattern(1, 9), MakePattern(1, 0),
|
||||||
// Pattern length should be 10, but that is not guaranteed.
|
// Pattern length should be 10, but that is not guaranteed.
|
||||||
MakePattern(1, 3), MakePattern(2, 1)),
|
MakePattern(1, 3), MakePattern(2, 1)),
|
||||||
Values(OEMCrypto_CipherMode_CBC),
|
Values(OEMCrypto_CipherMode_CBC),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
{
|
{
|
||||||
'sources': [
|
'sources': [
|
||||||
'oec_device_features.cpp',
|
'oec_device_features.cpp',
|
||||||
|
'oec_decrypt_fallback_chain.cpp',
|
||||||
'oec_key_deriver.cpp',
|
'oec_key_deriver.cpp',
|
||||||
'oec_session_util.cpp',
|
'oec_session_util.cpp',
|
||||||
'oemcrypto_session_tests_helper.cpp',
|
'oemcrypto_session_tests_helper.cpp',
|
||||||
|
|||||||
Reference in New Issue
Block a user