Shaka Packager SDK
aes_encryptor.cc
1 // Copyright 2014 Google LLC. 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 <absl/log/check.h>
10 #include <absl/log/log.h>
11 
12 #include <packager/macros/crypto.h>
13 
14 namespace {
15 
16 // Increment an 8-byte counter by 1. Return true if overflowed.
17 bool Increment64(uint8_t* counter) {
18  DCHECK(counter);
19  for (int i = 7; i >= 0; --i) {
20  if (++counter[i] != 0)
21  return false;
22  }
23  return true;
24 }
25 
26 } // namespace
27 
28 namespace shaka {
29 namespace media {
30 
31 // We don't support constant iv for counter mode, as we don't have a use case
32 // for that.
33 AesCtrEncryptor::AesCtrEncryptor()
34  : AesCryptor(kDontUseConstantIv),
35  block_offset_(0),
36  encrypted_counter_(AES_BLOCK_SIZE, 0) {}
37 
38 AesCtrEncryptor::~AesCtrEncryptor() {}
39 
40 bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
41  const std::vector<uint8_t>& iv) {
42  if (!SetupCipher(key.size(), kCtrMode)) {
43  return false;
44  }
45 
46  if (mbedtls_cipher_setkey(&cipher_ctx_, key.data(),
47  static_cast<int>(8 * key.size()),
48  MBEDTLS_ENCRYPT) != 0) {
49  LOG(ERROR) << "Failed to set CTR encryption key";
50  return false;
51  }
52 
53  return SetIv(iv);
54 }
55 
56 bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
57  size_t plaintext_size,
58  uint8_t* ciphertext,
59  size_t* ciphertext_size) {
60  DCHECK(plaintext);
61  DCHECK(ciphertext);
62 
63  // |ciphertext_size| is always the same as |plaintext_size| for counter mode.
64  if (*ciphertext_size < plaintext_size) {
65  LOG(ERROR) << "Expecting output size of at least " << plaintext_size
66  << " bytes.";
67  return false;
68  }
69  *ciphertext_size = plaintext_size;
70 
71  for (size_t i = 0; i < plaintext_size; ++i) {
72  if (block_offset_ == 0) {
73  size_t ignored_output_size;
74  CHECK_EQ(
75  mbedtls_cipher_crypt(&cipher_ctx_, /* iv= */ NULL, /* iv_len= */ 0,
76  &counter_[0], AES_BLOCK_SIZE,
77  &encrypted_counter_[0], &ignored_output_size),
78  0);
79 
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  : AesCryptor(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::InitializeWithIv(const std::vector<uint8_t>& key,
115  const std::vector<uint8_t>& iv) {
116  if (!SetupCipher(key.size(), kCbcMode)) {
117  return false;
118  }
119 
120  if (mbedtls_cipher_setkey(&cipher_ctx_, key.data(),
121  static_cast<int>(8 * key.size()),
122  MBEDTLS_ENCRYPT) != 0) {
123  LOG(ERROR) << "Failed to set CBC encryption key";
124  return false;
125  }
126 
127  return SetIv(iv);
128 }
129 
130 size_t AesCbcEncryptor::RequiredOutputSize(size_t plaintext_size) {
131  return plaintext_size + NumPaddingBytes(plaintext_size);
132 }
133 
134 bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
135  size_t plaintext_size,
136  uint8_t* ciphertext,
137  size_t* ciphertext_size) {
138  const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE;
139  const size_t num_padding_bytes = NumPaddingBytes(plaintext_size);
140  const size_t required_ciphertext_size = RequiredOutputSize(plaintext_size);
141 
142  if (*ciphertext_size < required_ciphertext_size) {
143  LOG(ERROR) << "Expecting output size of at least "
144  << required_ciphertext_size << " bytes.";
145  return false;
146  }
147  *ciphertext_size = required_ciphertext_size;
148 
149  // Encrypt everything but the residual block using CBC.
150  const size_t cbc_size = plaintext_size - residual_block_size;
151  if (cbc_size != 0) {
152  CbcEncryptBlocks(plaintext, cbc_size, ciphertext, internal_iv_.data());
153  } else if (padding_scheme_ == kCtsPadding) {
154  // Don't have a full block, leave unencrypted.
155  memcpy(ciphertext, plaintext, plaintext_size);
156  return true;
157  }
158  if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) {
159  // No residual block. No need to do padding.
160  return true;
161  }
162 
163  if (padding_scheme_ == kNoPadding) {
164  // The residual block is left unencrypted.
165  memcpy(ciphertext + cbc_size, plaintext + cbc_size, residual_block_size);
166  return true;
167  }
168 
169  std::vector<uint8_t> residual_block(plaintext + cbc_size,
170  plaintext + plaintext_size);
171  DCHECK_EQ(residual_block.size(), residual_block_size);
172  uint8_t* residual_ciphertext_block = ciphertext + cbc_size;
173 
174  if (padding_scheme_ == kPkcs5Padding) {
175  DCHECK_EQ(num_padding_bytes, AES_BLOCK_SIZE - residual_block_size);
176 
177  // Pad residue block with PKCS5 padding.
178  residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
179  CbcEncryptBlocks(residual_block.data(), AES_BLOCK_SIZE,
180  residual_ciphertext_block, internal_iv_.data());
181  } else {
182  DCHECK_EQ(num_padding_bytes, 0u);
183  DCHECK_EQ(padding_scheme_, kCtsPadding);
184 
185  // Zero-pad the residual block and encrypt using CBC.
186  residual_block.resize(AES_BLOCK_SIZE, 0);
187  CbcEncryptBlocks(residual_block.data(), AES_BLOCK_SIZE,
188  residual_block.data(), internal_iv_.data());
189 
190  // Replace the last full block with the zero-padded, encrypted residual
191  // block, and replace the residual block with the equivalent portion of the
192  // last full encrypted block. It may appear that some encrypted bits of the
193  // last full block are lost, but they are not, as they were used as the IV
194  // when encrypting the zero-padded residual block.
195  // This ordering of the output is described as "CS2" in literature.
196  // https://en.wikipedia.org/wiki/Ciphertext_stealing#CS2
197  memcpy(residual_ciphertext_block,
198  residual_ciphertext_block - AES_BLOCK_SIZE, residual_block_size);
199  memcpy(residual_ciphertext_block - AES_BLOCK_SIZE, residual_block.data(),
200  AES_BLOCK_SIZE);
201  }
202  return true;
203 }
204 
205 void AesCbcEncryptor::SetIvInternal() {
206  internal_iv_ = iv();
207  internal_iv_.resize(AES_BLOCK_SIZE, 0);
208 }
209 
210 size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const {
211  return (padding_scheme_ == kPkcs5Padding)
212  ? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE))
213  : 0;
214 }
215 
216 void AesCbcEncryptor::CbcEncryptBlocks(const uint8_t* plaintext,
217  size_t plaintext_size,
218  uint8_t* ciphertext,
219  uint8_t* iv) {
220  CHECK_EQ(plaintext_size % AES_BLOCK_SIZE, 0u);
221 
222  size_t output_size = 0;
223  CHECK_EQ(mbedtls_cipher_crypt(&cipher_ctx_, iv, AES_BLOCK_SIZE, plaintext,
224  plaintext_size, ciphertext, &output_size),
225  0);
226 
227  CHECK_EQ(output_size % AES_BLOCK_SIZE, 0u);
228  CHECK_GT(output_size, 0u);
229 
230  uint8_t* last_block = ciphertext + output_size - AES_BLOCK_SIZE;
231  memcpy(iv, last_block, AES_BLOCK_SIZE);
232 }
233 
234 } // namespace media
235 } // namespace shaka
bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv) override
AesCbcEncryptor(CbcPaddingScheme padding_scheme)
const std::vector< uint8_t > & iv() const
Definition: aes_cryptor.h:85
bool SetIv(const std::vector< uint8_t > &iv)
Definition: aes_cryptor.cc:70
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66