Shaka Packager SDK
aes_encryptor.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/base/aes_encryptor.h"
8 
9 #include <openssl/aes.h>
10 
11 #include "packager/base/logging.h"
12 
13 namespace {
14 
15 // Increment an 8-byte counter by 1. Return true if overflowed.
16 bool Increment64(uint8_t* counter) {
17  DCHECK(counter);
18  for (int i = 7; i >= 0; --i) {
19  if (++counter[i] != 0)
20  return false;
21  }
22  return true;
23 }
24 
25 // AES defines three key sizes: 128, 192 and 256 bits.
26 bool IsKeySizeValidForAes(size_t key_size) {
27  return key_size == 16 || key_size == 24 || key_size == 32;
28 }
29 
30 } // namespace
31 
32 namespace shaka {
33 namespace media {
34 
35 AesEncryptor::AesEncryptor(ConstantIvFlag constant_iv_flag)
36  : AesCryptor(constant_iv_flag) {}
37 AesEncryptor::~AesEncryptor() {}
38 
39 bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
40  const std::vector<uint8_t>& iv) {
41  if (!IsKeySizeValidForAes(key.size())) {
42  LOG(ERROR) << "Invalid AES key size: " << key.size();
43  return false;
44  }
45 
46  CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, mutable_aes_key()),
47  0);
48  return SetIv(iv);
49 }
50 
51 // We don't support constant iv for counter mode, as we don't have a use case
52 // for that.
53 AesCtrEncryptor::AesCtrEncryptor()
54  : AesEncryptor(kDontUseConstantIv),
55  block_offset_(0),
56  encrypted_counter_(AES_BLOCK_SIZE, 0) {}
57 
58 AesCtrEncryptor::~AesCtrEncryptor() {}
59 
60 
61 bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
62  size_t plaintext_size,
63  uint8_t* ciphertext,
64  size_t* ciphertext_size) {
65  DCHECK(plaintext);
66  DCHECK(ciphertext);
67  DCHECK(aes_key());
68 
69  // |ciphertext_size| is always the same as |plaintext_size| for counter mode.
70  if (*ciphertext_size < plaintext_size) {
71  LOG(ERROR) << "Expecting output size of at least " << plaintext_size
72  << " bytes.";
73  return false;
74  }
75  *ciphertext_size = plaintext_size;
76 
77  for (size_t i = 0; i < plaintext_size; ++i) {
78  if (block_offset_ == 0) {
79  AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key());
80  // As mentioned in ISO/IEC 23001-7:2016 CENC spec, of the 16 byte counter
81  // block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
82  // simple 64 bit unsigned integer that is incremented by one for each
83  // subsequent block of sample data processed and is kept in network byte
84  // order.
85  Increment64(&counter_[8]);
86  }
87  ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_];
88  block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE;
89  }
90  return true;
91 }
92 
93 void AesCtrEncryptor::SetIvInternal() {
94  block_offset_ = 0;
95  counter_ = iv();
96  counter_.resize(AES_BLOCK_SIZE, 0);
97 }
98 
99 AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme)
100  : AesCbcEncryptor(padding_scheme, kDontUseConstantIv) {}
101 
102 AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme,
103  ConstantIvFlag constant_iv_flag)
104  : AesEncryptor(constant_iv_flag), padding_scheme_(padding_scheme) {
105  if (padding_scheme_ != kNoPadding) {
106  CHECK_EQ(constant_iv_flag, kUseConstantIv)
107  << "non-constant iv (cipher block chain across calls) only makes sense "
108  "if the padding_scheme is kNoPadding.";
109  }
110 }
111 
112 AesCbcEncryptor::~AesCbcEncryptor() {}
113 
114 bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
115  size_t plaintext_size,
116  uint8_t* ciphertext,
117  size_t* ciphertext_size) {
118  DCHECK(aes_key());
119 
120  const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE;
121  const size_t num_padding_bytes = NumPaddingBytes(plaintext_size);
122  const size_t required_ciphertext_size = plaintext_size + num_padding_bytes;
123  if (*ciphertext_size < required_ciphertext_size) {
124  LOG(ERROR) << "Expecting output size of at least "
125  << required_ciphertext_size << " bytes.";
126  return false;
127  }
128  *ciphertext_size = required_ciphertext_size;
129 
130  // Encrypt everything but the residual block using CBC.
131  const size_t cbc_size = plaintext_size - residual_block_size;
132  if (cbc_size != 0) {
133  AES_cbc_encrypt(plaintext, ciphertext, cbc_size, aes_key(),
134  internal_iv_.data(), AES_ENCRYPT);
135  } else if (padding_scheme_ == kCtsPadding) {
136  // Don't have a full block, leave unencrypted.
137  memcpy(ciphertext, plaintext, plaintext_size);
138  return true;
139  }
140  if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) {
141  // No residual block. No need to do padding.
142  return true;
143  }
144 
145  if (padding_scheme_ == kNoPadding) {
146  // The residual block is left unencrypted.
147  memcpy(ciphertext + cbc_size, plaintext + cbc_size, residual_block_size);
148  return true;
149  }
150 
151  std::vector<uint8_t> residual_block(plaintext + cbc_size,
152  plaintext + plaintext_size);
153  DCHECK_EQ(residual_block.size(), residual_block_size);
154  uint8_t* residual_ciphertext_block = ciphertext + cbc_size;
155 
156  if (padding_scheme_ == kPkcs5Padding) {
157  DCHECK_EQ(num_padding_bytes, AES_BLOCK_SIZE - residual_block_size);
158 
159  // Pad residue block with PKCS5 padding.
160  residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
161  AES_cbc_encrypt(residual_block.data(), residual_ciphertext_block,
162  AES_BLOCK_SIZE, aes_key(), internal_iv_.data(),
163  AES_ENCRYPT);
164  } else {
165  DCHECK_EQ(num_padding_bytes, 0u);
166  DCHECK_EQ(padding_scheme_, kCtsPadding);
167 
168  // Zero-pad the residual block and encrypt using CBC.
169  residual_block.resize(AES_BLOCK_SIZE, 0);
170  AES_cbc_encrypt(residual_block.data(), residual_block.data(),
171  AES_BLOCK_SIZE, aes_key(), internal_iv_.data(),
172  AES_ENCRYPT);
173 
174  // Replace the last full block with the zero-padded, encrypted residual
175  // block, and replace the residual block with the equivalent portion of the
176  // last full encrypted block. It may appear that some encrypted bits of the
177  // last full block are lost, but they are not, as they were used as the IV
178  // when encrypting the zero-padded residual block.
179  memcpy(residual_ciphertext_block,
180  residual_ciphertext_block - AES_BLOCK_SIZE, residual_block_size);
181  memcpy(residual_ciphertext_block - AES_BLOCK_SIZE, residual_block.data(),
182  AES_BLOCK_SIZE);
183  }
184  return true;
185 }
186 
187 void AesCbcEncryptor::SetIvInternal() {
188  internal_iv_ = iv();
189  internal_iv_.resize(AES_BLOCK_SIZE, 0);
190 }
191 
192 size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const {
193  return (padding_scheme_ == kPkcs5Padding)
194  ? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE))
195  : 0;
196 }
197 
198 } // namespace media
199 } // namespace shaka
AesCbcEncryptor(CbcPaddingScheme padding_scheme)
bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv) override
All the methods that are virtual are virtual for mocking.
AesEncryptor(ConstantIvFlag constant_iv_flag)
const std::vector< uint8_t > & iv() const
Definition: aes_cryptor.h:82
bool SetIv(const std::vector< uint8_t > &iv)
Definition: aes_cryptor.cc:67