2022-08-26 15:44:59 +00:00
|
|
|
// Copyright 2014 Google LLC. All rights reserved.
|
2014-02-14 23:21:05 +00:00
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
2013-11-12 20:34:58 +00:00
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/media/base/aes_encryptor.h"
|
2013-11-12 20:34:58 +00:00
|
|
|
|
2022-11-02 15:34:06 +00:00
|
|
|
#include "glog/logging.h"
|
2013-11-12 20:34:58 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Increment an 8-byte counter by 1. Return true if overflowed.
|
2014-09-30 21:52:21 +00:00
|
|
|
bool Increment64(uint8_t* counter) {
|
2013-11-12 20:34:58 +00:00
|
|
|
DCHECK(counter);
|
2016-04-13 17:52:41 +00:00
|
|
|
for (int i = 7; i >= 0; --i) {
|
2013-11-12 20:34:58 +00:00
|
|
|
if (++counter[i] != 0)
|
|
|
|
return false;
|
2016-04-13 17:52:41 +00:00
|
|
|
}
|
2013-11-12 20:34:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2013-11-12 20:34:58 +00:00
|
|
|
namespace media {
|
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
// We don't support constant iv for counter mode, as we don't have a use case
|
|
|
|
// for that.
|
2016-03-17 17:03:19 +00:00
|
|
|
AesCtrEncryptor::AesCtrEncryptor()
|
2022-11-02 15:34:06 +00:00
|
|
|
: AesCryptor(kDontUseConstantIv),
|
2016-04-13 17:52:41 +00:00
|
|
|
block_offset_(0),
|
2022-11-02 15:34:06 +00:00
|
|
|
// mbedtls requires an extra output block.
|
|
|
|
encrypted_counter_(AES_BLOCK_SIZE * 2, 0) {}
|
2016-03-17 17:03:19 +00:00
|
|
|
|
|
|
|
AesCtrEncryptor::~AesCtrEncryptor() {}
|
|
|
|
|
2022-11-02 15:34:06 +00:00
|
|
|
bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
|
|
|
const std::vector<uint8_t>& iv) {
|
|
|
|
if (!SetupCipher(key.size(), kCtrMode)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mbedtls_cipher_setkey(&cipher_ctx_, key.data(),
|
|
|
|
static_cast<int>(8 * key.size()),
|
|
|
|
MBEDTLS_ENCRYPT) != 0) {
|
|
|
|
LOG(ERROR) << "Failed to set CTR encryption key";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SetIv(iv);
|
|
|
|
}
|
2013-11-12 20:34:58 +00:00
|
|
|
|
2016-04-06 00:19:16 +00:00
|
|
|
bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
|
|
|
|
size_t plaintext_size,
|
|
|
|
uint8_t* ciphertext,
|
|
|
|
size_t* ciphertext_size) {
|
2016-03-25 18:02:43 +00:00
|
|
|
DCHECK(plaintext);
|
|
|
|
DCHECK(ciphertext);
|
2013-12-17 00:49:56 +00:00
|
|
|
|
2016-04-06 00:19:16 +00:00
|
|
|
// |ciphertext_size| is always the same as |plaintext_size| for counter mode.
|
|
|
|
if (*ciphertext_size < plaintext_size) {
|
|
|
|
LOG(ERROR) << "Expecting output size of at least " << plaintext_size
|
|
|
|
<< " bytes.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*ciphertext_size = plaintext_size;
|
|
|
|
|
2016-03-25 18:02:43 +00:00
|
|
|
for (size_t i = 0; i < plaintext_size; ++i) {
|
|
|
|
if (block_offset_ == 0) {
|
2022-11-02 15:34:06 +00:00
|
|
|
size_t ignored_output_size;
|
|
|
|
CHECK_EQ(
|
|
|
|
mbedtls_cipher_crypt(&cipher_ctx_, /* iv= */ NULL, /* iv_len= */ 0,
|
|
|
|
&counter_[0], AES_BLOCK_SIZE,
|
|
|
|
&encrypted_counter_[0], &ignored_output_size),
|
|
|
|
0);
|
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
// As mentioned in ISO/IEC 23001-7:2016 CENC spec, of the 16 byte counter
|
2016-03-25 18:02:43 +00:00
|
|
|
// block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
|
|
|
|
// simple 64 bit unsigned integer that is incremented by one for each
|
|
|
|
// subsequent block of sample data processed and is kept in network byte
|
|
|
|
// order.
|
2016-04-13 17:52:41 +00:00
|
|
|
Increment64(&counter_[8]);
|
2016-03-25 18:02:43 +00:00
|
|
|
}
|
|
|
|
ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_];
|
|
|
|
block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE;
|
2013-12-17 00:49:56 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
void AesCtrEncryptor::SetIvInternal() {
|
|
|
|
block_offset_ = 0;
|
|
|
|
counter_ = iv();
|
|
|
|
counter_.resize(AES_BLOCK_SIZE, 0);
|
2016-03-25 18:02:43 +00:00
|
|
|
}
|
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme)
|
|
|
|
: AesCbcEncryptor(padding_scheme, kDontUseConstantIv) {}
|
2013-12-17 00:49:56 +00:00
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme,
|
|
|
|
ConstantIvFlag constant_iv_flag)
|
2022-11-02 15:34:06 +00:00
|
|
|
: AesCryptor(constant_iv_flag), padding_scheme_(padding_scheme) {
|
2016-04-13 17:52:41 +00:00
|
|
|
if (padding_scheme_ != kNoPadding) {
|
|
|
|
CHECK_EQ(constant_iv_flag, kUseConstantIv)
|
|
|
|
<< "non-constant iv (cipher block chain across calls) only makes sense "
|
|
|
|
"if the padding_scheme is kNoPadding.";
|
2014-09-09 22:56:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
AesCbcEncryptor::~AesCbcEncryptor() {}
|
|
|
|
|
2022-11-02 15:34:06 +00:00
|
|
|
bool AesCbcEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
|
|
|
const std::vector<uint8_t>& iv) {
|
|
|
|
if (!SetupCipher(key.size(), kCbcMode)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mbedtls_cipher_setkey(&cipher_ctx_, key.data(),
|
|
|
|
static_cast<int>(8 * key.size()),
|
|
|
|
MBEDTLS_ENCRYPT) != 0) {
|
|
|
|
LOG(ERROR) << "Failed to set CBC encryption key";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SetIv(iv);
|
|
|
|
}
|
|
|
|
|
2023-08-31 23:59:46 +00:00
|
|
|
size_t AesCbcEncryptor::RequiredOutputSize(size_t plaintext_size) {
|
2023-07-14 15:19:01 +00:00
|
|
|
// mbedtls requires a buffer large enough for one extra block.
|
|
|
|
return plaintext_size + NumPaddingBytes(plaintext_size) + AES_BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
|
2016-04-06 00:19:16 +00:00
|
|
|
bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
|
|
|
|
size_t plaintext_size,
|
|
|
|
uint8_t* ciphertext,
|
|
|
|
size_t* ciphertext_size) {
|
2016-03-25 18:02:43 +00:00
|
|
|
const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE;
|
2016-04-06 00:19:16 +00:00
|
|
|
const size_t num_padding_bytes = NumPaddingBytes(plaintext_size);
|
2023-08-31 23:59:46 +00:00
|
|
|
const size_t required_ciphertext_size = RequiredOutputSize(plaintext_size);
|
2023-07-14 15:19:01 +00:00
|
|
|
|
2016-04-06 00:19:16 +00:00
|
|
|
if (*ciphertext_size < required_ciphertext_size) {
|
|
|
|
LOG(ERROR) << "Expecting output size of at least "
|
|
|
|
<< required_ciphertext_size << " bytes.";
|
|
|
|
return false;
|
|
|
|
}
|
2022-11-02 15:34:06 +00:00
|
|
|
*ciphertext_size = required_ciphertext_size - AES_BLOCK_SIZE;
|
2016-04-06 00:19:16 +00:00
|
|
|
|
2016-03-25 18:02:43 +00:00
|
|
|
// Encrypt everything but the residual block using CBC.
|
|
|
|
const size_t cbc_size = plaintext_size - residual_block_size;
|
2022-11-02 15:34:06 +00:00
|
|
|
|
|
|
|
// Copy the residual block early, since mbedtls may overwrite one extra block
|
|
|
|
// of the output, and input and output may be the same buffer.
|
|
|
|
std::vector<uint8_t> residual_block(plaintext + cbc_size,
|
|
|
|
plaintext + plaintext_size);
|
|
|
|
DCHECK_EQ(residual_block.size(), residual_block_size);
|
|
|
|
|
2016-03-25 18:02:43 +00:00
|
|
|
if (cbc_size != 0) {
|
2022-11-02 15:34:06 +00:00
|
|
|
CbcEncryptBlocks(plaintext, cbc_size, ciphertext);
|
2016-03-25 18:02:43 +00:00
|
|
|
} else if (padding_scheme_ == kCtsPadding) {
|
2014-09-09 22:56:02 +00:00
|
|
|
// Don't have a full block, leave unencrypted.
|
2016-03-25 18:02:43 +00:00
|
|
|
memcpy(ciphertext, plaintext, plaintext_size);
|
2016-03-17 17:03:19 +00:00
|
|
|
return true;
|
2014-09-09 22:56:02 +00:00
|
|
|
}
|
2016-03-25 18:02:43 +00:00
|
|
|
if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) {
|
|
|
|
// No residual block. No need to do padding.
|
2016-03-17 17:03:19 +00:00
|
|
|
return true;
|
2014-09-09 22:56:02 +00:00
|
|
|
}
|
2016-03-25 18:02:43 +00:00
|
|
|
|
2016-04-13 23:43:55 +00:00
|
|
|
if (padding_scheme_ == kNoPadding) {
|
|
|
|
// The residual block is left unencrypted.
|
|
|
|
memcpy(ciphertext + cbc_size, plaintext + cbc_size, residual_block_size);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-25 18:02:43 +00:00
|
|
|
uint8_t* residual_ciphertext_block = ciphertext + cbc_size;
|
|
|
|
if (padding_scheme_ == kPkcs5Padding) {
|
2016-04-06 00:19:16 +00:00
|
|
|
DCHECK_EQ(num_padding_bytes, AES_BLOCK_SIZE - residual_block_size);
|
|
|
|
|
2016-03-25 18:02:43 +00:00
|
|
|
// Pad residue block with PKCS5 padding.
|
|
|
|
residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
|
2022-11-02 15:34:06 +00:00
|
|
|
|
|
|
|
CbcEncryptBlocks(residual_block.data(), AES_BLOCK_SIZE,
|
|
|
|
residual_ciphertext_block);
|
2016-03-25 18:02:43 +00:00
|
|
|
} else {
|
2016-04-06 00:19:16 +00:00
|
|
|
DCHECK_EQ(num_padding_bytes, 0u);
|
2016-03-25 18:02:43 +00:00
|
|
|
DCHECK_EQ(padding_scheme_, kCtsPadding);
|
|
|
|
|
|
|
|
// Zero-pad the residual block and encrypt using CBC.
|
|
|
|
residual_block.resize(AES_BLOCK_SIZE, 0);
|
2022-11-02 15:34:06 +00:00
|
|
|
// mbedtls requires an extra block in the output buffer, and it cannot be
|
|
|
|
// the same as the input buffer.
|
|
|
|
std::vector<uint8_t> encrypted_residual_block(AES_BLOCK_SIZE * 2);
|
|
|
|
|
|
|
|
CbcEncryptBlocks(residual_block.data(), AES_BLOCK_SIZE,
|
|
|
|
encrypted_residual_block.data());
|
2016-03-25 18:02:43 +00:00
|
|
|
|
|
|
|
// Replace the last full block with the zero-padded, encrypted residual
|
|
|
|
// block, and replace the residual block with the equivalent portion of the
|
|
|
|
// last full encrypted block. It may appear that some encrypted bits of the
|
|
|
|
// last full block are lost, but they are not, as they were used as the IV
|
|
|
|
// when encrypting the zero-padded residual block.
|
2022-11-02 15:34:06 +00:00
|
|
|
// This ordering of the output is described as "CS2" in literature.
|
|
|
|
// https://en.wikipedia.org/wiki/Ciphertext_stealing#CS2
|
2016-03-25 18:02:43 +00:00
|
|
|
memcpy(residual_ciphertext_block,
|
|
|
|
residual_ciphertext_block - AES_BLOCK_SIZE, residual_block_size);
|
2022-11-02 15:34:06 +00:00
|
|
|
memcpy(residual_ciphertext_block - AES_BLOCK_SIZE,
|
|
|
|
encrypted_residual_block.data(), AES_BLOCK_SIZE);
|
2016-03-25 18:02:43 +00:00
|
|
|
}
|
2014-09-09 22:56:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-13 17:52:41 +00:00
|
|
|
void AesCbcEncryptor::SetIvInternal() {
|
|
|
|
internal_iv_ = iv();
|
|
|
|
internal_iv_.resize(AES_BLOCK_SIZE, 0);
|
|
|
|
}
|
|
|
|
|
2016-03-25 18:02:43 +00:00
|
|
|
size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const {
|
|
|
|
return (padding_scheme_ == kPkcs5Padding)
|
|
|
|
? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE))
|
|
|
|
: 0;
|
2013-12-17 00:49:56 +00:00
|
|
|
}
|
|
|
|
|
2022-11-02 15:34:06 +00:00
|
|
|
void AesCbcEncryptor::CbcEncryptBlocks(const uint8_t* plaintext,
|
|
|
|
size_t plaintext_size,
|
|
|
|
uint8_t* ciphertext) {
|
|
|
|
CHECK_EQ(plaintext_size % AES_BLOCK_SIZE, 0u);
|
|
|
|
|
|
|
|
size_t output_size = 0;
|
|
|
|
CHECK_EQ(
|
|
|
|
mbedtls_cipher_crypt(&cipher_ctx_, internal_iv_.data(), AES_BLOCK_SIZE,
|
|
|
|
plaintext, plaintext_size, ciphertext, &output_size),
|
|
|
|
0);
|
|
|
|
|
|
|
|
CHECK_EQ(output_size % AES_BLOCK_SIZE, 0u);
|
|
|
|
CHECK_GT(output_size, 0u);
|
|
|
|
|
|
|
|
uint8_t* last_block = ciphertext + output_size - AES_BLOCK_SIZE;
|
|
|
|
internal_iv_.assign(last_block, last_block + AES_BLOCK_SIZE);
|
|
|
|
}
|
|
|
|
|
2013-12-17 00:49:56 +00:00
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|