#include "media/cdm/cbcs_decryptor.h"
#include <stdint.h>
#include <algorithm>
#include <string>
#include <vector>
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/checked_math.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "media/base/encryption_pattern.h"
#include "media/base/subsample_entry.h"
#include "media/cdm/aes_cbc_crypto.h"
namespace media {
namespace {
constexpr size_t kAesBlockSizeInBytes = 16;
bool DecryptWithPattern(base::span<const uint8_t> key,
base::span<const uint8_t> iv,
const EncryptionPattern& pattern,
base::span<const uint8_t> input_data,
base::span<uint8_t> output_data) {
AesCbcCrypto aes_cbc_crypto;
if (!aes_cbc_crypto.Initialize(key, iv)) {
return false;
}
size_t total_blocks = input_data.size_bytes() / kAesBlockSizeInBytes;
size_t remaining_bytes = input_data.size_bytes() % kAesBlockSizeInBytes;
size_t crypt_byte_block =
base::strict_cast<size_t>(pattern.crypt_byte_block());
size_t skip_byte_block = base::strict_cast<size_t>(pattern.skip_byte_block());
if (crypt_byte_block >= 16 || skip_byte_block >= 16)
return false;
if (crypt_byte_block == 0 && skip_byte_block == 0) {
crypt_byte_block = total_blocks;
}
size_t blocks_processed = 0;
size_t offset = 0;
bool is_encrypted_blocks = false;
while (blocks_processed < total_blocks) {
is_encrypted_blocks = !is_encrypted_blocks;
size_t blocks_to_process =
std::min(is_encrypted_blocks ? crypt_byte_block : skip_byte_block,
total_blocks - blocks_processed);
if (blocks_to_process == 0)
continue;
size_t bytes_to_process = blocks_to_process * kAesBlockSizeInBytes;
auto src = input_data.subspan(offset, bytes_to_process);
auto dest = output_data.subspan(offset, bytes_to_process);
if (is_encrypted_blocks) {
if (!aes_cbc_crypto.Decrypt(src, dest)) {
return false;
}
} else {
dest.copy_from(src);
}
blocks_processed += blocks_to_process;
offset += bytes_to_process;
}
if (remaining_bytes > 0) {
auto src = input_data.subspan(offset, remaining_bytes);
auto dest = output_data.subspan(offset, remaining_bytes);
dest.copy_from(src);
}
return true;
}
}
scoped_refptr<DecoderBuffer> DecryptCbcsBuffer(const DecoderBuffer& input,
base::span<const uint8_t> key) {
const size_t sample_size = input.size();
CHECK(sample_size) << "No data to decrypt.";
const DecryptConfig* decrypt_config = input.decrypt_config();
CHECK(decrypt_config) << "No need to call Decrypt() on unencrypted buffer.";
DCHECK_EQ(EncryptionScheme::kCbcs, decrypt_config->encryption_scheme());
CHECK(decrypt_config->HasPattern());
const EncryptionPattern pattern =
decrypt_config->encryption_pattern().value();
auto buffer = base::MakeRefCounted<DecoderBuffer>(sample_size);
base::span<uint8_t> output_data = buffer->writable_span();
buffer->set_timestamp(input.timestamp());
buffer->set_duration(input.duration());
buffer->set_is_key_frame(input.is_key_frame());
if (input.side_data()) {
buffer->set_side_data(input.side_data()->Clone());
}
const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
if (subsamples.empty()) {
return DecryptWithPattern(key, base::as_byte_span(decrypt_config->iv()),
pattern, base::span(input), output_data)
? buffer
: nullptr;
}
if (!VerifySubsamplesMatchSize(subsamples, sample_size)) {
DVLOG(1) << "Subsample sizes do not equal input size";
return nullptr;
}
auto src = base::span(input);
auto dest = output_data;
for (const auto& subsample : subsamples) {
if (subsample.clear_bytes) {
DVLOG(4) << "Copying clear_bytes: " << subsample.clear_bytes;
auto [src_copy, src_rem] = src.split_at(subsample.clear_bytes);
auto [dest_copy, dest_rem] = dest.split_at(subsample.clear_bytes);
src = src_rem;
dest = dest_rem;
dest_copy.copy_from(src_copy);
}
if (subsample.cypher_bytes) {
DVLOG(4) << "Processing cypher_bytes: " << subsample.cypher_bytes
<< ", pattern(" << pattern.crypt_byte_block() << ","
<< pattern.skip_byte_block() << ")";
auto [src_cypher, src_rem] = src.split_at(subsample.cypher_bytes);
auto [dest_cypher, dest_rem] = dest.split_at(subsample.cypher_bytes);
src = src_rem;
dest = dest_rem;
if (!DecryptWithPattern(key, base::as_byte_span(decrypt_config->iv()),
pattern, src_cypher, dest_cypher)) {
return nullptr;
}
}
}
return buffer;
}
}