#include "media/cdm/cenc_decryptor.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/not_fatal_until.h"
#include "crypto/aes_ctr.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "media/base/subsample_entry.h"
namespace media {
namespace {
constexpr size_t kRequiredKeyBytes = 16;
enum ClearBytesBufferSel { kSrcContainsClearBytes, kDstContainsClearBytes };
void CopySubsamples(const std::vector<SubsampleEntry>& subsamples,
const ClearBytesBufferSel sel,
base::span<const uint8_t> src,
base::span<uint8_t> dst) {
size_t src_i = 0;
size_t dst_i = 0;
for (const auto& subsample : subsamples) {
if (sel == kSrcContainsClearBytes) {
src_i += subsample.clear_bytes;
} else {
dst_i += subsample.clear_bytes;
}
auto src_view = src.subspan(src_i, subsample.cypher_bytes);
auto dst_view = dst.subspan(dst_i, subsample.cypher_bytes);
dst_view.copy_from(src_view);
src_i += subsample.cypher_bytes;
dst_i += subsample.cypher_bytes;
}
}
void CopyExtraSettings(const DecoderBuffer& input, DecoderBuffer* output) {
output->set_timestamp(input.timestamp());
output->set_duration(input.duration());
output->set_is_key_frame(input.is_key_frame());
if (input.side_data()) {
output->set_side_data(input.side_data()->Clone());
}
}
}
scoped_refptr<DecoderBuffer> DecryptCencBuffer(const DecoderBuffer& input,
base::span<const uint8_t> key) {
base::span<const uint8_t> sample = input;
CHECK(!sample.empty()) << "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::kCenc, decrypt_config->encryption_scheme());
if (key.size() != kRequiredKeyBytes) {
DVLOG(1) << "Supplied key is the wrong size for CENC";
return nullptr;
}
auto iv = base::as_byte_span(decrypt_config->iv())
.to_fixed_extent<crypto::aes_ctr::kCounterSize>();
if (!iv) {
DVLOG(1) << "Supplied IV is the wrong size for CENC";
return nullptr;
}
const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
if (subsamples.empty()) {
auto decrypted = base::HeapArray<uint8_t>::Uninit(sample.size());
crypto::aes_ctr::Decrypt(key, *iv, sample, decrypted);
auto output = DecoderBuffer::FromArray(std::move(decrypted));
CopyExtraSettings(input, output.get());
return output;
}
if (!VerifySubsamplesMatchSize(subsamples, sample.size())) {
DVLOG(1) << "Subsample sizes do not equal input size";
return nullptr;
}
size_t total_encrypted_size = 0;
for (const auto& subsample : subsamples)
total_encrypted_size += subsample.cypher_bytes;
if (total_encrypted_size == 0) {
auto output = DecoderBuffer::CopyFrom(sample);
CopyExtraSettings(input, output.get());
return output;
}
auto encrypted = base::HeapArray<uint8_t>::Uninit(total_encrypted_size);
auto decrypted = base::HeapArray<uint8_t>::Uninit(total_encrypted_size);
CopySubsamples(subsamples, kSrcContainsClearBytes, sample, encrypted);
crypto::aes_ctr::Decrypt(key, *iv, encrypted, decrypted);
scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom(sample);
CopySubsamples(subsamples, kDstContainsClearBytes, decrypted,
output->writable_span());
CopyExtraSettings(input, output.get());
return output;
}
}