blob: e286a8c1def08f4f0342c14aae44fa1378647c11 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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 "crypto/symmetric_key.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;
// Decrypts |input_data| into |output_data|, using the pattern specified in
// |pattern|. |pattern| only applies to full blocks. Any partial block at
// the end is considered unencrypted. |output_data| must have enough room to
// hold |input_data|.size() bytes.
bool DecryptWithPattern(const crypto::SymmetricKey& key,
base::span<const uint8_t> iv,
const EncryptionPattern& pattern,
base::span<const uint8_t> input_data,
uint8_t* output_data) {
// The AES_CBC decryption is reset for each subsample.
AesCbcCrypto aes_cbc_crypto;
if (!aes_cbc_crypto.Initialize(key, iv))
return false;
// |total_blocks| is the number of blocks in the buffer, ignoring any
// partial block at the end. |remaining_bytes| is the number of bytes
// in the partial block at the end of the buffer, if any.
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());
// |crypt_byte_block| and |skip_byte_block| come from 4 bit values, so fail
// if these are too large.
if (crypt_byte_block >= 16 || skip_byte_block >= 16)
return false;
if (crypt_byte_block == 0 && skip_byte_block == 0) {
// From ISO/IEC 23001-7:2016(E), section 9.6.1:
// "When the fields default_crypt_byte_block and default_skip_byte_block
// in a version 1 Track Encryption Box ('tenc') are non-zero numbers,
// pattern encryption SHALL be applied."
// So for the pattern 0:0, assume that all blocks are encrypted.
crypt_byte_block = total_blocks;
}
// Apply the pattern to |input_data|.
// Example (using Pattern(2,3), Ex is encrypted, Ux unencrypted)
// input_data: |E1|E2|U3|U4|U5|E6|E7|U8|U9|U10|E11|
// We must decrypt 2 blocks, then simply copy the next 3 blocks, and
// repeat until the end. Note that the input does not have to contain
// a full pattern or even |crypt_byte_block| blocks at the end.
size_t blocks_processed = 0;
const uint8_t* src = input_data.data();
uint8_t* dest = output_data;
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;
// From ISO/IEC 23001-7:2016(E), section 10.4.2:
// For a typical pattern length of 10 (e.g. 1:9) "the pattern is repeated
// every 160 bytes of the protected range, until the end of the range. If
// the protected range of the slice body is not a multiple of the pattern
// length (e.g. 160 bytes), then the pattern sequence applies to the
// included whole 16-byte Blocks and a partial 16-byte Block that may
// remain where the pattern is terminated by the byte length of the range
// BytesOfProtectedData, is left unencrypted."
if (is_encrypted_blocks) {
if (!aes_cbc_crypto.Decrypt(base::make_span(src, bytes_to_process),
dest)) {
return false;
}
} else {
memcpy(dest, src, bytes_to_process);
}
blocks_processed += blocks_to_process;
src += bytes_to_process;
dest += bytes_to_process;
}
// Any partial block data remaining in this subsample is considered
// unencrypted so simply copy it into |dest|.
if (remaining_bytes > 0)
memcpy(dest, src, remaining_bytes);
return true;
}
} // namespace
scoped_refptr<DecoderBuffer> DecryptCbcsBuffer(
const DecoderBuffer& input,
const crypto::SymmetricKey& key) {
size_t sample_size = input.data_size();
DCHECK(sample_size) << "No data to decrypt.";
const DecryptConfig* decrypt_config = input.decrypt_config();
DCHECK(decrypt_config) << "No need to call Decrypt() on unencrypted buffer.";
DCHECK_EQ(EncryptionScheme::kCbcs, decrypt_config->encryption_scheme());
DCHECK(decrypt_config->HasPattern());
const EncryptionPattern pattern =
decrypt_config->encryption_pattern().value();
// Decrypted data will be the same size as |input| size.
auto buffer = base::MakeRefCounted<DecoderBuffer>(sample_size);
uint8_t* output_data = buffer->writable_data();
buffer->set_timestamp(input.timestamp());
buffer->set_duration(input.duration());
buffer->set_is_key_frame(input.is_key_frame());
buffer->CopySideDataFrom(input.side_data(), input.side_data_size());
const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
if (subsamples.empty()) {
// Assume the whole buffer is encrypted.
return DecryptWithPattern(
key, base::as_bytes(base::make_span(decrypt_config->iv())),
pattern, base::make_span(input.data(), sample_size), output_data)
? buffer
: nullptr;
}
if (!VerifySubsamplesMatchSize(subsamples, sample_size)) {
DVLOG(1) << "Subsample sizes do not equal input size";
return nullptr;
}
const uint8_t* src = input.data();
uint8_t* dest = output_data;
for (const auto& subsample : subsamples) {
if (subsample.clear_bytes) {
DVLOG(4) << "Copying clear_bytes: " << subsample.clear_bytes;
memcpy(dest, src, subsample.clear_bytes);
src += subsample.clear_bytes;
dest += subsample.clear_bytes;
}
if (subsample.cypher_bytes) {
DVLOG(4) << "Processing cypher_bytes: " << subsample.cypher_bytes
<< ", pattern(" << pattern.crypt_byte_block() << ","
<< pattern.skip_byte_block() << ")";
if (!DecryptWithPattern(
key, base::as_bytes(base::make_span(decrypt_config->iv())),
pattern, base::make_span(src, subsample.cypher_bytes), dest)) {
return nullptr;
}
src += subsample.cypher_bytes;
dest += subsample.cypher_bytes;
}
}
return buffer;
}
} // namespace media