|
|
|
|
@@ -13,6 +13,8 @@
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#include "advance_iv_ctr.h"
|
|
|
|
|
#include "arraysize.h"
|
|
|
|
|
#include "content_key_session.h"
|
|
|
|
|
#include "crypto_key.h"
|
|
|
|
|
#include "entitlement_key_session.h"
|
|
|
|
|
@@ -50,20 +52,36 @@
|
|
|
|
|
namespace wvcdm {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
const uint32_t kRsaSignatureLength = 256;
|
|
|
|
|
// TODO(b/117112392): adjust chunk size based on resource rating.
|
|
|
|
|
const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB
|
|
|
|
|
const size_t kEstimatedInitialUsageTableHeader = 40;
|
|
|
|
|
// Ability to switch cipher modes in SelectKey() was introduced in this
|
|
|
|
|
// OEMCrypto version
|
|
|
|
|
const size_t kOemCryptoApiVersionSupportsSwitchingCipherMode = 14;
|
|
|
|
|
constexpr size_t KiB = 1024;
|
|
|
|
|
constexpr size_t MiB = 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
constexpr uint32_t kRsaSignatureLength = 256;
|
|
|
|
|
constexpr size_t kEstimatedInitialUsageTableHeader = 40;
|
|
|
|
|
|
|
|
|
|
// Constants and utility objects relating to OEM Certificates
|
|
|
|
|
const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
|
|
|
|
|
constexpr const char* kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
|
|
|
|
|
constexpr int kMaxTerminateCountDown = 5;
|
|
|
|
|
|
|
|
|
|
const std::string kStringNotAvailable = "NA";
|
|
|
|
|
|
|
|
|
|
// Constants relating to OEMCrypto resource rating tiers. These tables are
|
|
|
|
|
// ordered by resource rating tier from lowest to highest. These should be
|
|
|
|
|
// updated whenever the supported range of resource rating tiers changes.
|
|
|
|
|
constexpr size_t kMaxSubsampleRegionSizes[] = {
|
|
|
|
|
100 * KiB, // Tier 1 - Low
|
|
|
|
|
500 * KiB, // Tier 2 - Medium
|
|
|
|
|
1 * MiB, // Tier 3 - High
|
|
|
|
|
4 * MiB, // Tier 4 - Very High
|
|
|
|
|
};
|
|
|
|
|
// The +1 in this calculation is because the bounds RESOURCE_RATING_TIER_MAX and
|
|
|
|
|
// RESOURCE_RATING_TIER_MIN are inclusive.
|
|
|
|
|
static_assert(ArraySize(kMaxSubsampleRegionSizes) ==
|
|
|
|
|
RESOURCE_RATING_TIER_MAX - RESOURCE_RATING_TIER_MIN + 1,
|
|
|
|
|
"The kMaxSubsampleRegionSizes table needs to be updated to "
|
|
|
|
|
"reflect the supported range of resource rating tiers.");
|
|
|
|
|
|
|
|
|
|
constexpr size_t kDefaultMaximumChunkSize = 100 * KiB;
|
|
|
|
|
|
|
|
|
|
// This maps a few common OEMCryptoResult to CdmResponseType. Many mappings
|
|
|
|
|
// are not universal but are OEMCrypto method specific. Those will be
|
|
|
|
|
// specified in the CryptoSession method rather than here.
|
|
|
|
|
@@ -91,6 +109,25 @@ CdmResponseType MapOEMCryptoResult(OEMCryptoResult result,
|
|
|
|
|
return default_status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AdvanceDestBuffer(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;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case OEMCrypto_BufferType_Secure:
|
|
|
|
|
dest_buffer->buffer.secure.offset += bytes;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case OEMCrypto_BufferType_Direct:
|
|
|
|
|
// Nothing to do for this buffer type.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
LOGE("Unrecognized OEMCryptoBufferType %u - doing nothing",
|
|
|
|
|
dest_buffer->type);
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
shared_mutex CryptoSession::static_field_mutex_;
|
|
|
|
|
@@ -180,7 +217,6 @@ CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
|
|
|
|
|
requested_security_level_(kLevelDefault),
|
|
|
|
|
usage_support_type_(kUnknownUsageSupport),
|
|
|
|
|
usage_table_header_(nullptr),
|
|
|
|
|
cipher_mode_(kCipherModeCtr),
|
|
|
|
|
api_version_(0) {
|
|
|
|
|
assert(metrics);
|
|
|
|
|
Init();
|
|
|
|
|
@@ -901,8 +937,7 @@ CdmResponseType CryptoSession::LoadKeys(
|
|
|
|
|
|
|
|
|
|
LOGV("Loading key: id = %u", oec_session_id_);
|
|
|
|
|
sts = key_session_->LoadKeys(message, signature, mac_key_iv, mac_key, keys,
|
|
|
|
|
provider_session_token, &cipher_mode_,
|
|
|
|
|
srm_requirement);
|
|
|
|
|
provider_session_token, srm_requirement);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) {
|
|
|
|
|
@@ -1295,99 +1330,153 @@ CdmResponseType CryptoSession::GenerateRsaSignature(const std::string& message,
|
|
|
|
|
"OEMCrypto_GenerateRSASignature");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
|
|
|
|
bool CryptoSession::GetMaxSubsampleRegionSize(size_t* max) {
|
|
|
|
|
uint32_t tier = 0;
|
|
|
|
|
if (!GetResourceRatingTier(&tier)) return false;
|
|
|
|
|
// Subtract RESOURCE_RATING_TIER_MIN to get a 0-based index into the table.
|
|
|
|
|
const uint32_t index = tier - RESOURCE_RATING_TIER_MIN;
|
|
|
|
|
if (index >= ArraySize(kMaxSubsampleRegionSizes)) return false;
|
|
|
|
|
*max = kMaxSubsampleRegionSizes[index];
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CdmResponseType CryptoSession::Decrypt(
|
|
|
|
|
const CdmDecryptionParametersV16& params) {
|
|
|
|
|
if (!is_destination_buffer_type_valid_) {
|
|
|
|
|
if (!SetDestinationBufferType()) return UNKNOWN_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCrypto_DestBufferDesc buffer_descriptor;
|
|
|
|
|
buffer_descriptor.type =
|
|
|
|
|
OEMCryptoBufferType output_descriptor_type =
|
|
|
|
|
params.is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
|
|
|
|
|
|
|
|
|
|
if (params.is_secure &&
|
|
|
|
|
buffer_descriptor.type == OEMCrypto_BufferType_Clear) {
|
|
|
|
|
output_descriptor_type == OEMCrypto_BufferType_Clear) {
|
|
|
|
|
return SECURE_BUFFER_REQUIRED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (buffer_descriptor.type) {
|
|
|
|
|
case OEMCrypto_BufferType_Clear:
|
|
|
|
|
buffer_descriptor.buffer.clear.address =
|
|
|
|
|
static_cast<uint8_t*>(params.decrypt_buffer) +
|
|
|
|
|
params.decrypt_buffer_offset;
|
|
|
|
|
buffer_descriptor.buffer.clear.address_length =
|
|
|
|
|
params.decrypt_buffer_length - params.decrypt_buffer_offset;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Secure:
|
|
|
|
|
buffer_descriptor.buffer.secure.handle = params.decrypt_buffer;
|
|
|
|
|
buffer_descriptor.buffer.secure.offset = params.decrypt_buffer_offset;
|
|
|
|
|
buffer_descriptor.buffer.secure.handle_length =
|
|
|
|
|
params.decrypt_buffer_length;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Direct:
|
|
|
|
|
buffer_descriptor.type = OEMCrypto_BufferType_Direct;
|
|
|
|
|
buffer_descriptor.buffer.direct.is_video = params.is_video;
|
|
|
|
|
break;
|
|
|
|
|
if (params.samples.size() == 0) return CANNOT_DECRYPT_ZERO_SAMPLES;
|
|
|
|
|
if (std::any_of(std::begin(params.samples), std::end(params.samples),
|
|
|
|
|
[](const CdmDecryptionSample& sample) -> bool {
|
|
|
|
|
return sample.subsamples.size() == 0;
|
|
|
|
|
})) {
|
|
|
|
|
return CANNOT_DECRYPT_ZERO_SUBSAMPLES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
|
|
|
|
if (!params.is_encrypted &&
|
|
|
|
|
params.subsample_flags ==
|
|
|
|
|
(OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)) {
|
|
|
|
|
WithOecSessionLock("Decrypt() calling CopyBuffer", [&] {
|
|
|
|
|
M_TIME(sts = OEMCrypto_CopyBuffer(
|
|
|
|
|
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
|
|
|
|
&buffer_descriptor, params.subsample_flags),
|
|
|
|
|
metrics_, oemcrypto_copy_buffer_, sts,
|
|
|
|
|
metrics::Pow2Bucket(params.encrypt_length));
|
|
|
|
|
});
|
|
|
|
|
// Convert all the sample and subsample definitions to OEMCrypto structs.
|
|
|
|
|
// This code also caches whether any of the data is protected, to save later
|
|
|
|
|
// code the trouble of iterating over all the subsamples to check.
|
|
|
|
|
bool is_any_sample_protected = false;
|
|
|
|
|
std::vector<OEMCrypto_SampleDescription> oec_samples;
|
|
|
|
|
oec_samples.reserve(params.samples.size());
|
|
|
|
|
std::vector<std::vector<OEMCrypto_SubSampleDescription>>
|
|
|
|
|
oec_subsample_vectors;
|
|
|
|
|
oec_subsample_vectors.reserve(params.samples.size());
|
|
|
|
|
|
|
|
|
|
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE &&
|
|
|
|
|
params.encrypt_length > kMaximumChunkSize) {
|
|
|
|
|
// OEMCrypto_CopyBuffer rejected the buffer as too large, so chunk it up
|
|
|
|
|
// into 100 KiB sections.
|
|
|
|
|
sts = CopyBufferInChunks(params, buffer_descriptor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (api_version_ < kOemCryptoApiVersionSupportsSwitchingCipherMode) {
|
|
|
|
|
if (params.is_encrypted && params.cipher_mode != cipher_mode_) {
|
|
|
|
|
return INCORRECT_CRYPTO_MODE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
|
|
|
|
OEMCrypto_CENCEncryptPatternDesc pattern_descriptor;
|
|
|
|
|
pattern_descriptor.encrypt = params.pattern_descriptor.encrypt_blocks;
|
|
|
|
|
pattern_descriptor.skip = params.pattern_descriptor.skip_blocks;
|
|
|
|
|
// Check if key needs to be selected
|
|
|
|
|
if (params.is_encrypted) {
|
|
|
|
|
CdmResponseType result = SelectKey(*params.key_id, params.cipher_mode);
|
|
|
|
|
if (result != NO_ERROR) return result;
|
|
|
|
|
for (const CdmDecryptionSample& sample : params.samples) {
|
|
|
|
|
oec_samples.emplace_back();
|
|
|
|
|
OEMCrypto_SampleDescription& oec_sample = oec_samples.back();
|
|
|
|
|
|
|
|
|
|
// Set up the sample's input buffer
|
|
|
|
|
oec_sample.buffers.input_data = sample.encrypt_buffer;
|
|
|
|
|
oec_sample.buffers.input_data_length = sample.encrypt_buffer_length;
|
|
|
|
|
|
|
|
|
|
// Set up the sample's output buffer
|
|
|
|
|
OEMCrypto_DestBufferDesc& output_descriptor =
|
|
|
|
|
oec_sample.buffers.output_descriptor;
|
|
|
|
|
output_descriptor.type = output_descriptor_type;
|
|
|
|
|
switch (output_descriptor.type) {
|
|
|
|
|
case OEMCrypto_BufferType_Clear:
|
|
|
|
|
output_descriptor.buffer.clear.address =
|
|
|
|
|
static_cast<uint8_t*>(sample.decrypt_buffer) +
|
|
|
|
|
sample.decrypt_buffer_offset;
|
|
|
|
|
output_descriptor.buffer.clear.address_length =
|
|
|
|
|
sample.decrypt_buffer_size - sample.decrypt_buffer_offset;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Secure:
|
|
|
|
|
output_descriptor.buffer.secure.handle = sample.decrypt_buffer;
|
|
|
|
|
output_descriptor.buffer.secure.offset = sample.decrypt_buffer_offset;
|
|
|
|
|
output_descriptor.buffer.secure.handle_length =
|
|
|
|
|
sample.decrypt_buffer_size;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Direct:
|
|
|
|
|
output_descriptor.buffer.direct.is_video = params.is_video;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WithOecSessionLock("Decrypt() calling key_session_->Decrypt()", [&] {
|
|
|
|
|
sts = key_session_->Decrypt(params, buffer_descriptor, 0,
|
|
|
|
|
pattern_descriptor);
|
|
|
|
|
});
|
|
|
|
|
// Convert all the sample's subsamples. This code also tallies the total
|
|
|
|
|
// size as a sanity check.
|
|
|
|
|
oec_subsample_vectors.emplace_back();
|
|
|
|
|
std::vector<OEMCrypto_SubSampleDescription>& oec_subsamples =
|
|
|
|
|
oec_subsample_vectors.back();
|
|
|
|
|
oec_subsamples.reserve(sample.subsamples.size());
|
|
|
|
|
size_t sample_size = 0;
|
|
|
|
|
bool is_any_subsample_protected = false;
|
|
|
|
|
size_t current_block_offset = 0;
|
|
|
|
|
for (const CdmDecryptionSubsample& subsample : sample.subsamples) {
|
|
|
|
|
oec_subsamples.push_back(OEMCrypto_SubSampleDescription{
|
|
|
|
|
subsample.clear_bytes, subsample.protected_bytes,
|
|
|
|
|
0, // subsample_flags
|
|
|
|
|
current_block_offset});
|
|
|
|
|
|
|
|
|
|
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
|
|
|
|
// OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it
|
|
|
|
|
// up into sections no more than 100 KiB. The exact chunk size needs to
|
|
|
|
|
// be an even number of pattern repetitions long or else the pattern
|
|
|
|
|
// will get out of sync.
|
|
|
|
|
const size_t pattern_length =
|
|
|
|
|
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
|
|
|
|
|
kAes128BlockSize;
|
|
|
|
|
const size_t chunk_size =
|
|
|
|
|
pattern_length > 0
|
|
|
|
|
? kMaximumChunkSize - (kMaximumChunkSize % pattern_length)
|
|
|
|
|
: kMaximumChunkSize;
|
|
|
|
|
is_any_subsample_protected |= (subsample.protected_bytes > 0);
|
|
|
|
|
sample_size += subsample.clear_bytes + subsample.protected_bytes;
|
|
|
|
|
if (params.cipher_mode == kCipherModeCtr) {
|
|
|
|
|
current_block_offset =
|
|
|
|
|
(current_block_offset + subsample.protected_bytes) %
|
|
|
|
|
kAes128BlockSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (params.encrypt_length > chunk_size) {
|
|
|
|
|
sts = DecryptInChunks(params, buffer_descriptor, pattern_descriptor,
|
|
|
|
|
chunk_size);
|
|
|
|
|
// TODO(b/149524614): This block is not necessary except for
|
|
|
|
|
// backwards-compatibility while we are transitioning from the v15 API to
|
|
|
|
|
// the v16 API.
|
|
|
|
|
if (params.observe_legacy_fields) {
|
|
|
|
|
OEMCrypto_SubSampleDescription& oec_subsample = oec_subsamples.back();
|
|
|
|
|
oec_subsample.subsample_flags = subsample.flags;
|
|
|
|
|
oec_subsample.block_offset = subsample.block_offset;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is_any_sample_protected |= is_any_subsample_protected;
|
|
|
|
|
|
|
|
|
|
// TODO(b/149524614): This check is not necessary except for
|
|
|
|
|
// backwards-compatibility while we are transitioning from the v15 API to
|
|
|
|
|
// the v16 API.
|
|
|
|
|
if (!params.observe_legacy_fields) {
|
|
|
|
|
// Set the actual subsample_flags now that all the subsamples are
|
|
|
|
|
// converted.
|
|
|
|
|
oec_subsamples.front().subsample_flags |= OEMCrypto_FirstSubsample;
|
|
|
|
|
oec_subsamples.back().subsample_flags |= OEMCrypto_LastSubsample;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that the total size is valid
|
|
|
|
|
if (sample_size != oec_sample.buffers.input_data_length)
|
|
|
|
|
return SAMPLE_AND_SUBSAMPLE_SIZE_MISMATCH;
|
|
|
|
|
|
|
|
|
|
// Set up the sample's IV
|
|
|
|
|
if (is_any_subsample_protected) {
|
|
|
|
|
if (sizeof(oec_sample.iv) != sample.iv.size()) return INVALID_IV_SIZE;
|
|
|
|
|
memcpy(oec_sample.iv, sample.iv.data(), sizeof(oec_sample.iv));
|
|
|
|
|
} else {
|
|
|
|
|
memset(oec_sample.iv, 0, sizeof(oec_sample.iv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Attach the subsamples to the sample description
|
|
|
|
|
oec_sample.subsamples = oec_subsamples.data();
|
|
|
|
|
oec_sample.subsamples_length = oec_subsamples.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert the pattern descriptor
|
|
|
|
|
OEMCrypto_CENCEncryptPatternDesc oec_pattern{params.pattern.encrypt_blocks,
|
|
|
|
|
params.pattern.skip_blocks};
|
|
|
|
|
|
|
|
|
|
// Check if a key needs to be selected
|
|
|
|
|
if (is_any_sample_protected) {
|
|
|
|
|
CdmResponseType result = SelectKey(params.key_id, params.cipher_mode);
|
|
|
|
|
if (result != NO_ERROR) return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Perform decrypt
|
|
|
|
|
OEMCryptoResult sts =
|
|
|
|
|
DecryptMultipleSamples(oec_samples, params.cipher_mode, oec_pattern);
|
|
|
|
|
switch (sts) {
|
|
|
|
|
case OEMCrypto_SUCCESS:
|
|
|
|
|
return NO_ERROR;
|
|
|
|
|
@@ -2494,190 +2583,301 @@ size_t CryptoSession::GenericEncryptionBlockSize(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult CryptoSession::CopyBufferInChunks(
|
|
|
|
|
const CdmDecryptionParameters& params,
|
|
|
|
|
OEMCrypto_DestBufferDesc full_buffer_descriptor) {
|
|
|
|
|
size_t remaining_encrypt_length = params.encrypt_length;
|
|
|
|
|
uint8_t subsample_flags = OEMCrypto_FirstSubsample;
|
|
|
|
|
// OEMCryptoResult OEMCrypto_DecryptCENC(
|
|
|
|
|
// OEMCrypto_SESSION session,
|
|
|
|
|
// const OEMCrypto_SampleDescription* samples, // an array of samples.
|
|
|
|
|
// size_t samples_length, // the number of samples.
|
|
|
|
|
// const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
|
|
|
|
|
|
|
|
|
while (remaining_encrypt_length > 0) {
|
|
|
|
|
// Calculate the size of the next chunk and its offset into the original
|
|
|
|
|
// buffer.
|
|
|
|
|
const size_t chunk_size =
|
|
|
|
|
std::min(remaining_encrypt_length, kMaximumChunkSize);
|
|
|
|
|
const size_t additional_offset =
|
|
|
|
|
params.encrypt_length - remaining_encrypt_length;
|
|
|
|
|
OEMCryptoResult CryptoSession::DecryptMultipleSamples(
|
|
|
|
|
const std::vector<OEMCrypto_SampleDescription>& samples,
|
|
|
|
|
CdmCipherMode cipher_mode,
|
|
|
|
|
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
|
|
|
|
|
OEMCryptoResult sts = OEMCrypto_ERROR_BUFFER_TOO_LARGE;
|
|
|
|
|
|
|
|
|
|
// Update the remaining length of the original buffer only after
|
|
|
|
|
// calculating the new values.
|
|
|
|
|
remaining_encrypt_length -= chunk_size;
|
|
|
|
|
// If there's only one sample, automatically fall through to avoid a redundant
|
|
|
|
|
// roundtrip through OEMCrypto_DecryptCENC()
|
|
|
|
|
if (samples.size() > 1) {
|
|
|
|
|
WithOecSessionLock("DecryptMultipleSamples", [&] {
|
|
|
|
|
sts = key_session_->Decrypt(samples.data(), samples.size(), pattern);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the destination buffer with the new offset. Because OEMCrypto
|
|
|
|
|
// can modify the OEMCrypto_DestBufferDesc during the call to
|
|
|
|
|
// OEMCrypto_CopyBuffer, (and is known to do so on some platforms) a new
|
|
|
|
|
// OEMCrypto_DestBufferDesc must be allocated for each call.
|
|
|
|
|
OEMCrypto_DestBufferDesc buffer_descriptor = full_buffer_descriptor;
|
|
|
|
|
switch (buffer_descriptor.type) {
|
|
|
|
|
case OEMCrypto_BufferType_Clear:
|
|
|
|
|
buffer_descriptor.buffer.clear.address += additional_offset;
|
|
|
|
|
buffer_descriptor.buffer.clear.address_length -= additional_offset;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Secure:
|
|
|
|
|
buffer_descriptor.buffer.secure.offset += additional_offset;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Direct:
|
|
|
|
|
// OEMCrypto_BufferType_Direct does not need modification.
|
|
|
|
|
break;
|
|
|
|
|
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
|
|
|
|
// Fall back to sending each sample individually
|
|
|
|
|
for (const OEMCrypto_SampleDescription& sample : samples) {
|
|
|
|
|
sts = DecryptSample(sample, cipher_mode, pattern);
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult CryptoSession::DecryptSample(
|
|
|
|
|
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
|
|
|
|
|
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
|
|
|
|
|
OEMCryptoResult sts = OEMCrypto_ERROR_BUFFER_TOO_LARGE;
|
|
|
|
|
|
|
|
|
|
// If there's only one subsample and it contains only one type of region,
|
|
|
|
|
// automatically fall through to avoid a redundant roundtrip through
|
|
|
|
|
// OEMCrypto_DecryptCENC()
|
|
|
|
|
if (sample.subsamples_length > 1 ||
|
|
|
|
|
(sample.subsamples[0].num_bytes_clear > 0 &&
|
|
|
|
|
sample.subsamples[0].num_bytes_encrypted > 0)) {
|
|
|
|
|
WithOecSessionLock("DecryptSample", [&] {
|
|
|
|
|
sts = key_session_->Decrypt(&sample, 1, pattern);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
|
|
|
|
// Fall back to sending each subsample region individually
|
|
|
|
|
OEMCrypto_SampleDescription fake_sample = sample;
|
|
|
|
|
for (size_t i = 0; i < sample.subsamples_length; ++i) {
|
|
|
|
|
const OEMCrypto_SubSampleDescription& original_subsample =
|
|
|
|
|
sample.subsamples[i];
|
|
|
|
|
|
|
|
|
|
if (original_subsample.num_bytes_clear > 0) {
|
|
|
|
|
const size_t length = original_subsample.num_bytes_clear;
|
|
|
|
|
OEMCrypto_SubSampleDescription clear_subsample{
|
|
|
|
|
length,
|
|
|
|
|
0, // num_bytes_encrypted
|
|
|
|
|
0, // subsample_flags
|
|
|
|
|
0 // block_offset, not relevant for clear data
|
|
|
|
|
};
|
|
|
|
|
if (original_subsample.subsample_flags & OEMCrypto_FirstSubsample) {
|
|
|
|
|
clear_subsample.subsample_flags |= OEMCrypto_FirstSubsample;
|
|
|
|
|
}
|
|
|
|
|
if ((original_subsample.subsample_flags & OEMCrypto_LastSubsample) &&
|
|
|
|
|
original_subsample.num_bytes_encrypted == 0) {
|
|
|
|
|
clear_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fake_sample.buffers.input_data_length = length;
|
|
|
|
|
fake_sample.subsamples = &clear_subsample;
|
|
|
|
|
fake_sample.subsamples_length = 1;
|
|
|
|
|
|
|
|
|
|
sts = LegacyDecrypt(fake_sample, cipher_mode, pattern);
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) break;
|
|
|
|
|
|
|
|
|
|
fake_sample.buffers.input_data += length;
|
|
|
|
|
AdvanceDestBuffer(&fake_sample.buffers.output_descriptor, length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (original_subsample.num_bytes_encrypted > 0) {
|
|
|
|
|
const size_t length = original_subsample.num_bytes_encrypted;
|
|
|
|
|
OEMCrypto_SubSampleDescription encrypted_subsample{
|
|
|
|
|
0, // num_bytes_clear
|
|
|
|
|
length,
|
|
|
|
|
0, // subsample_flags
|
|
|
|
|
original_subsample.block_offset};
|
|
|
|
|
if ((original_subsample.subsample_flags & OEMCrypto_FirstSubsample) &&
|
|
|
|
|
original_subsample.num_bytes_clear == 0) {
|
|
|
|
|
encrypted_subsample.subsample_flags |= OEMCrypto_FirstSubsample;
|
|
|
|
|
}
|
|
|
|
|
if (original_subsample.subsample_flags & OEMCrypto_LastSubsample) {
|
|
|
|
|
encrypted_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fake_sample.buffers.input_data_length = length;
|
|
|
|
|
fake_sample.subsamples = &encrypted_subsample;
|
|
|
|
|
fake_sample.subsamples_length = 1;
|
|
|
|
|
|
|
|
|
|
sts = LegacyDecrypt(fake_sample, cipher_mode, pattern);
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) break;
|
|
|
|
|
|
|
|
|
|
fake_sample.buffers.input_data += length;
|
|
|
|
|
AdvanceDestBuffer(&fake_sample.buffers.output_descriptor, length);
|
|
|
|
|
if (cipher_mode == kCipherModeCtr) {
|
|
|
|
|
AdvanceIvCtr(&fake_sample.iv,
|
|
|
|
|
original_subsample.block_offset +
|
|
|
|
|
original_subsample.num_bytes_encrypted);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult CryptoSession::LegacyDecrypt(
|
|
|
|
|
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
|
|
|
|
|
const OEMCrypto_CENCEncryptPatternDesc& pattern) {
|
|
|
|
|
size_t max_chunk_size;
|
|
|
|
|
if (!GetMaxSubsampleRegionSize(&max_chunk_size)) {
|
|
|
|
|
LOGW("Unable to get maximum subsample region size. Defaulting to 100 KiB.");
|
|
|
|
|
max_chunk_size = kDefaultMaximumChunkSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
|
|
|
|
|
|
|
|
|
// We can be sure this is only called with one subsample containing one
|
|
|
|
|
// region of data.
|
|
|
|
|
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
|
|
|
|
|
const bool is_encrypted = (subsample.num_bytes_encrypted > 0);
|
|
|
|
|
const bool is_only_subsample =
|
|
|
|
|
subsample.subsample_flags ==
|
|
|
|
|
(OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
|
|
|
|
if (!is_encrypted && is_only_subsample) {
|
|
|
|
|
WithOecSessionLock("LegacyDecrypt() calling CopyBuffer", [&] {
|
|
|
|
|
M_TIME(sts = OEMCrypto_CopyBuffer(
|
|
|
|
|
oec_session_id_, sample.buffers.input_data,
|
|
|
|
|
sample.buffers.input_data_length,
|
|
|
|
|
&sample.buffers.output_descriptor, subsample.subsample_flags),
|
|
|
|
|
metrics_, oemcrypto_copy_buffer_, sts,
|
|
|
|
|
metrics::Pow2Bucket(sample.buffers.input_data_length));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE &&
|
|
|
|
|
sample.buffers.input_data_length > max_chunk_size) {
|
|
|
|
|
// OEMCrypto_CopyBuffer rejected the buffer as too large, so chunk it up
|
|
|
|
|
// into 100 KiB sections.
|
|
|
|
|
sts = LegacyCopyBufferInChunks(sample, max_chunk_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
|
|
|
|
WithOecSessionLock("LegacyDecrypt() calling key_session_->Decrypt()", [&] {
|
|
|
|
|
sts = key_session_->Decrypt(&sample, 1, pattern);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
|
|
|
|
// OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it
|
|
|
|
|
// up into sections no more than 100 KiB. The exact chunk size needs to
|
|
|
|
|
// be an even number of pattern repetitions long or else the pattern
|
|
|
|
|
// will get out of sync.
|
|
|
|
|
const size_t pattern_length =
|
|
|
|
|
(pattern.encrypt + pattern.skip) * kAes128BlockSize;
|
|
|
|
|
const size_t chunk_size =
|
|
|
|
|
pattern_length > 0
|
|
|
|
|
? max_chunk_size - (max_chunk_size % pattern_length)
|
|
|
|
|
: max_chunk_size;
|
|
|
|
|
|
|
|
|
|
if (sample.buffers.input_data_length > chunk_size) {
|
|
|
|
|
sts = LegacyDecryptInChunks(sample, cipher_mode, pattern, chunk_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult CryptoSession::LegacyCopyBufferInChunks(
|
|
|
|
|
const OEMCrypto_SampleDescription& sample, size_t max_chunk_size) {
|
|
|
|
|
const uint8_t* input_data = sample.buffers.input_data;
|
|
|
|
|
size_t remaining_input_data = sample.buffers.input_data_length;
|
|
|
|
|
OEMCrypto_DestBufferDesc output_descriptor = sample.buffers.output_descriptor;
|
|
|
|
|
uint8_t subsample_flags = OEMCrypto_FirstSubsample;
|
|
|
|
|
OEMCryptoResult sts = OEMCrypto_SUCCESS;
|
|
|
|
|
|
|
|
|
|
while (remaining_input_data > 0) {
|
|
|
|
|
// Calculate the size of the next chunk.
|
|
|
|
|
const size_t chunk_size = std::min(remaining_input_data, max_chunk_size);
|
|
|
|
|
|
|
|
|
|
// Re-add "last subsample" flag if this is the last subsample.
|
|
|
|
|
if (remaining_encrypt_length == 0) {
|
|
|
|
|
if (chunk_size == remaining_input_data) {
|
|
|
|
|
subsample_flags |= OEMCrypto_LastSubsample;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult sts;
|
|
|
|
|
WithOecSessionLock("CopyBufferInChunks", [&] {
|
|
|
|
|
M_TIME(sts = OEMCrypto_CopyBuffer(
|
|
|
|
|
oec_session_id_, params.encrypt_buffer + additional_offset,
|
|
|
|
|
chunk_size, &buffer_descriptor, subsample_flags),
|
|
|
|
|
WithOecSessionLock("LegacyCopyBufferInChunks", [&] {
|
|
|
|
|
M_TIME(sts = OEMCrypto_CopyBuffer(oec_session_id_, input_data, chunk_size,
|
|
|
|
|
&output_descriptor, subsample_flags),
|
|
|
|
|
metrics_, oemcrypto_copy_buffer_, sts,
|
|
|
|
|
metrics::Pow2Bucket(chunk_size));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) {
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) break;
|
|
|
|
|
|
|
|
|
|
// Clear any subsample flags before the next loop iteration.
|
|
|
|
|
subsample_flags = 0;
|
|
|
|
|
|
|
|
|
|
// Update the source and destination buffers based on the amount of data
|
|
|
|
|
// copied.
|
|
|
|
|
input_data += chunk_size;
|
|
|
|
|
remaining_input_data -= chunk_size;
|
|
|
|
|
AdvanceDestBuffer(&output_descriptor, chunk_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OEMCrypto_SUCCESS;
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OEMCryptoResult CryptoSession::DecryptInChunks(
|
|
|
|
|
const CdmDecryptionParameters& params,
|
|
|
|
|
const OEMCrypto_DestBufferDesc& full_buffer_descriptor,
|
|
|
|
|
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor,
|
|
|
|
|
size_t max_chunk_size) {
|
|
|
|
|
size_t remaining_encrypt_length = params.encrypt_length;
|
|
|
|
|
uint8_t subsample_flags = (params.subsample_flags & OEMCrypto_FirstSubsample)
|
|
|
|
|
? OEMCrypto_FirstSubsample
|
|
|
|
|
: 0;
|
|
|
|
|
std::vector<uint8_t> iv = *params.iv;
|
|
|
|
|
OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
|
|
|
|
|
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
|
|
|
|
|
const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size) {
|
|
|
|
|
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
|
|
|
|
|
|
|
|
|
|
const size_t pattern_length_in_bytes =
|
|
|
|
|
(pattern_descriptor.encrypt + pattern_descriptor.skip) * kAes128BlockSize;
|
|
|
|
|
const bool is_protected = (subsample.num_bytes_encrypted > 0);
|
|
|
|
|
OEMCrypto_SampleDescription fake_sample = sample;
|
|
|
|
|
OEMCrypto_SubSampleDescription fake_subsample = subsample;
|
|
|
|
|
fake_sample.subsamples = &fake_subsample;
|
|
|
|
|
fake_subsample.subsample_flags =
|
|
|
|
|
subsample.subsample_flags & OEMCrypto_FirstSubsample;
|
|
|
|
|
|
|
|
|
|
while (remaining_encrypt_length > 0) {
|
|
|
|
|
// Calculate the size of the next chunk and its offset into the
|
|
|
|
|
// original buffer.
|
|
|
|
|
const size_t chunk_size =
|
|
|
|
|
std::min(remaining_encrypt_length, max_chunk_size);
|
|
|
|
|
const size_t additional_offset =
|
|
|
|
|
params.encrypt_length - remaining_encrypt_length;
|
|
|
|
|
size_t remaining_input_data = sample.buffers.input_data_length;
|
|
|
|
|
OEMCryptoResult sts = OEMCrypto_SUCCESS;
|
|
|
|
|
|
|
|
|
|
// Update the remaining length of the original buffer only after
|
|
|
|
|
// calculating the new values.
|
|
|
|
|
remaining_encrypt_length -= chunk_size;
|
|
|
|
|
|
|
|
|
|
// Update the destination buffer with the new offset. Because OEMCrypto
|
|
|
|
|
// can modify the OEMCrypto_DestBufferDesc during the call to
|
|
|
|
|
// OEMCrypto_DecryptCENC, (and is known to do so on some platforms) a new
|
|
|
|
|
// OEMCrypto_DestBufferDesc must be allocated for each call.
|
|
|
|
|
OEMCrypto_DestBufferDesc buffer_descriptor = full_buffer_descriptor;
|
|
|
|
|
switch (buffer_descriptor.type) {
|
|
|
|
|
case OEMCrypto_BufferType_Clear:
|
|
|
|
|
buffer_descriptor.buffer.clear.address += additional_offset;
|
|
|
|
|
buffer_descriptor.buffer.clear.address_length -= additional_offset;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Secure:
|
|
|
|
|
buffer_descriptor.buffer.secure.offset += additional_offset;
|
|
|
|
|
break;
|
|
|
|
|
case OEMCrypto_BufferType_Direct:
|
|
|
|
|
// OEMCrypto_BufferType_Direct does not need modification.
|
|
|
|
|
break;
|
|
|
|
|
while (remaining_input_data > 0) {
|
|
|
|
|
// Calculate the size of the next chunk.
|
|
|
|
|
const size_t chunk_size = std::min(remaining_input_data, max_chunk_size);
|
|
|
|
|
fake_sample.buffers.input_data_length = chunk_size;
|
|
|
|
|
if (is_protected) {
|
|
|
|
|
fake_subsample.num_bytes_encrypted = chunk_size;
|
|
|
|
|
} else {
|
|
|
|
|
fake_subsample.num_bytes_clear = chunk_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re-add "last subsample" flag if this is the last subsample.
|
|
|
|
|
if (remaining_encrypt_length == 0 &&
|
|
|
|
|
params.subsample_flags & OEMCrypto_LastSubsample) {
|
|
|
|
|
subsample_flags |= OEMCrypto_LastSubsample;
|
|
|
|
|
if (chunk_size == remaining_input_data) {
|
|
|
|
|
fake_subsample.subsample_flags |=
|
|
|
|
|
subsample.subsample_flags & OEMCrypto_LastSubsample;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// block_offset and pattern_descriptor do not need to change because
|
|
|
|
|
// max_chunk_size is guaranteed to be an even multiple of the
|
|
|
|
|
// |pattern| and |fake_subsample.block_offset| do not need to change because
|
|
|
|
|
// |max_chunk_size| is guaranteed to be an even multiple of the
|
|
|
|
|
// pattern length long, which is also guaranteed to be an exact number
|
|
|
|
|
// of AES blocks long.
|
|
|
|
|
OEMCryptoResult sts;
|
|
|
|
|
sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
|
|
|
|
WithOecSessionLock("DecryptInChunks", [&] {
|
|
|
|
|
M_TIME(sts = key_session_->Decrypt(params, buffer_descriptor,
|
|
|
|
|
additional_offset, pattern_descriptor),
|
|
|
|
|
metrics_, oemcrypto_decrypt_cenc_, sts,
|
|
|
|
|
metrics::Pow2Bucket(chunk_size));
|
|
|
|
|
WithOecSessionLock("LegacyDecryptInChunks", [&] {
|
|
|
|
|
sts = key_session_->Decrypt(&fake_sample, 1, pattern);
|
|
|
|
|
});
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) {
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we are not yet done, update the IV so that it is valid for the next
|
|
|
|
|
// iteration.
|
|
|
|
|
if (remaining_encrypt_length != 0) {
|
|
|
|
|
if (cipher_mode_ == kCipherModeCtr) {
|
|
|
|
|
// For CTR modes, update the IV depending on how many encrypted blocks
|
|
|
|
|
// we passed. Since we calculated the chunk size to be an even number
|
|
|
|
|
// of crypto blocks and pattern repetitions in size, we can do a
|
|
|
|
|
// simplified calculation for this.
|
|
|
|
|
uint64_t encrypted_blocks_passed = 0;
|
|
|
|
|
if (pattern_length_in_bytes == 0) {
|
|
|
|
|
encrypted_blocks_passed = chunk_size / kAes128BlockSize;
|
|
|
|
|
} else {
|
|
|
|
|
const size_t pattern_repetitions_passed =
|
|
|
|
|
chunk_size / pattern_length_in_bytes;
|
|
|
|
|
encrypted_blocks_passed =
|
|
|
|
|
pattern_repetitions_passed * pattern_descriptor.encrypt;
|
|
|
|
|
}
|
|
|
|
|
IncrementIV(encrypted_blocks_passed, &iv);
|
|
|
|
|
} else if (cipher_mode_ == kCipherModeCbc) {
|
|
|
|
|
// For CBC modes, use the previous ciphertext block.
|
|
|
|
|
if (sts != OEMCrypto_SUCCESS) break;
|
|
|
|
|
|
|
|
|
|
// Stash the last crypto block in the IV. We don't have to handle
|
|
|
|
|
// partial crypto blocks here because we know we broke the buffer into
|
|
|
|
|
// chunks along even crypto block boundaries.
|
|
|
|
|
// Clear any subsample flags before the next loop iteration.
|
|
|
|
|
fake_subsample.subsample_flags = 0;
|
|
|
|
|
|
|
|
|
|
// Update the IV so that it is valid for the next iteration. This should not
|
|
|
|
|
// be done on the last iteration both to save time and because the 'cbcs'
|
|
|
|
|
// calculation can underflow if the chunk is less than the max chunk size.
|
|
|
|
|
if (remaining_input_data > chunk_size) {
|
|
|
|
|
if (cipher_mode == kCipherModeCtr) {
|
|
|
|
|
// For 'cenc', update the IV depending on how many encrypted blocks
|
|
|
|
|
// we passed.
|
|
|
|
|
AdvanceIvCtr(&fake_sample.iv, chunk_size + fake_subsample.block_offset);
|
|
|
|
|
} else if (cipher_mode == kCipherModeCbc) {
|
|
|
|
|
// For 'cbcs', use the last ciphertext block as the next IV. The last
|
|
|
|
|
// block that was encrypted is probably not the last block of the
|
|
|
|
|
// subsample. Since the max buffer size is guaranteed to be an even
|
|
|
|
|
// number of pattern repetitions long, we can use the pattern to know
|
|
|
|
|
// how many blocks to look back.
|
|
|
|
|
const uint8_t* const buffer_end =
|
|
|
|
|
params.encrypt_buffer + additional_offset + chunk_size;
|
|
|
|
|
fake_sample.buffers.input_data + chunk_size;
|
|
|
|
|
const uint8_t* const block_end =
|
|
|
|
|
buffer_end - kAes128BlockSize * pattern.skip;
|
|
|
|
|
|
|
|
|
|
const uint8_t* block_end = nullptr;
|
|
|
|
|
if (pattern_length_in_bytes == 0) {
|
|
|
|
|
// For cbc1, the last encrypted block is the last block of the
|
|
|
|
|
// subsample.
|
|
|
|
|
block_end = buffer_end;
|
|
|
|
|
} else {
|
|
|
|
|
// For cbcs, we must look for the last encrypted block, which is
|
|
|
|
|
// probably not the last block of the subsample. Luckily, since the
|
|
|
|
|
// buffer size is guaranteed to be an even number of pattern
|
|
|
|
|
// repetitions long, we can use the pattern to know how many blocks
|
|
|
|
|
// to look back.
|
|
|
|
|
block_end = buffer_end - kAes128BlockSize * pattern_descriptor.skip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iv.assign(block_end - kAes128BlockSize, block_end);
|
|
|
|
|
static_assert(sizeof(fake_sample.iv) == kAes128BlockSize,
|
|
|
|
|
"The size of an AES-128 block and the size of an AES-128 "
|
|
|
|
|
"IV have become misaligned.");
|
|
|
|
|
memcpy(fake_sample.iv, block_end - kAes128BlockSize, kAes128BlockSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear any subsample flags before the next loop iteration.
|
|
|
|
|
subsample_flags = 0;
|
|
|
|
|
// Update the source and destination buffers based on the amount of data
|
|
|
|
|
// copied.
|
|
|
|
|
fake_sample.buffers.input_data += chunk_size;
|
|
|
|
|
remaining_input_data -= chunk_size;
|
|
|
|
|
AdvanceDestBuffer(&fake_sample.buffers.output_descriptor, chunk_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OEMCrypto_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CryptoSession::IncrementIV(uint64_t increase_by,
|
|
|
|
|
std::vector<uint8_t>* iv_out) {
|
|
|
|
|
std::vector<uint8_t>& iv = *iv_out;
|
|
|
|
|
uint64_t* counter_buffer = reinterpret_cast<uint64_t*>(&iv[8]);
|
|
|
|
|
(*counter_buffer) = htonll64(ntohll64(*counter_buffer) + increase_by);
|
|
|
|
|
return sts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Func>
|
|
|
|
|
|