DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
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 #include <openssl/err.h>
11 #include <openssl/rand.h>
12 
13 #include "packager/base/logging.h"
14 
15 namespace {
16 
17 // Increment an 8-byte counter by 1. Return true if overflowed.
18 bool Increment64(uint8_t* counter) {
19  DCHECK(counter);
20  for (int i = 7; i >= 0; --i)
21  if (++counter[i] != 0)
22  return false;
23  return true;
24 }
25 
26 // According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
27 // 64-bit (8-byte) or 128-bit (16-byte).
28 bool IsIvSizeValid(size_t iv_size) { return iv_size == 8 || iv_size == 16; }
29 
30 // AES defines three key sizes: 128, 192 and 256 bits.
31 bool IsKeySizeValidForAes(size_t key_size) {
32  return key_size == 16 || key_size == 24 || key_size == 32;
33 }
34 
35 // CENC protection scheme uses 128-bit keys in counter mode.
36 const uint32_t kCencKeySize = 16;
37 
38 } // namespace
39 
40 namespace edash_packager {
41 namespace media {
42 
43 AesEncryptor::AesEncryptor() {}
44 AesEncryptor::~AesEncryptor() {}
45 
47  const std::vector<uint8_t>& key,
48  uint8_t iv_size) {
49  std::vector<uint8_t> iv(iv_size, 0);
50  if (RAND_bytes(&iv[0], iv_size) != 1) {
51  LOG(ERROR) << "RAND_bytes failed with error: "
52  << ERR_error_string(ERR_get_error(), NULL);
53  return false;
54  }
55  return InitializeWithIv(key, iv);
56 }
57 
58 bool AesEncryptor::Encrypt(const std::vector<uint8_t>& plaintext,
59  std::vector<uint8_t>* ciphertext) {
60  if (plaintext.empty())
61  return true;
62  ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size()));
63  return EncryptData(plaintext.data(), plaintext.size(), ciphertext->data());
64 }
65 
66 bool AesEncryptor::Encrypt(const std::string& plaintext,
67  std::string* ciphertext) {
68  ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size()));
69  return EncryptData(reinterpret_cast<const uint8_t*>(plaintext.data()),
70  plaintext.size(),
71  reinterpret_cast<uint8_t*>(string_as_array(ciphertext)));
72 }
73 
74 AesCtrEncryptor::AesCtrEncryptor()
75  : block_offset_(0),
76  encrypted_counter_(AES_BLOCK_SIZE, 0),
77  counter_overflow_(false) {
78  COMPILE_ASSERT(AES_BLOCK_SIZE == kCencKeySize,
79  cenc_key_size_should_be_the_same_as_aes_block_size);
80 }
81 
82 AesCtrEncryptor::~AesCtrEncryptor() {}
83 
84 bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
85  const std::vector<uint8_t>& iv) {
86  if (key.size() != kCencKeySize) {
87  LOG(ERROR) << "Invalid key size of " << key.size() << " for CENC.";
88  return false;
89  }
90  if (!IsIvSizeValid(iv.size())) {
91  LOG(ERROR) << "Invalid IV size: " << iv.size();
92  return false;
93  }
94 
95  aes_key_.reset(new AES_KEY());
96  CHECK_EQ(AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, aes_key_.get()), 0);
97  return SetIv(iv);
98 }
99 
100 size_t AesCtrEncryptor::NumPaddingBytes(size_t size) {
101  return 0;
102 }
103 
104 bool AesCtrEncryptor::EncryptData(const uint8_t* plaintext,
105  size_t plaintext_size,
106  uint8_t* ciphertext) {
107  DCHECK(plaintext);
108  DCHECK(ciphertext);
109  DCHECK(aes_key_);
110 
111  for (size_t i = 0; i < plaintext_size; ++i) {
112  if (block_offset_ == 0) {
113  AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key_.get());
114  // As mentioned in ISO/IEC FDIS 23001-7: CENC spec, of the 16 byte counter
115  // block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
116  // simple 64 bit unsigned integer that is incremented by one for each
117  // subsequent block of sample data processed and is kept in network byte
118  // order.
119  if (Increment64(&counter_[8]))
120  counter_overflow_ = true;
121  }
122  ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_];
123  block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE;
124  }
125  return true;
126 }
127 
129  block_offset_ = 0;
130 
131  // As recommended in ISO/IEC FDIS 23001-7: CENC spec, for 64-bit (8-byte)
132  // IV_Sizes, initialization vectors for subsequent samples can be created by
133  // incrementing the initialization vector of the previous sample.
134  // For 128-bit (16-byte) IV_Sizes, initialization vectors for subsequent
135  // samples should be created by adding the block count of the previous sample
136  // to the initialization vector of the previous sample.
137  if (iv_.size() == 8) {
138  Increment64(&iv_[0]);
139  counter_ = iv_;
140  counter_.resize(AES_BLOCK_SIZE, 0);
141  } else {
142  DCHECK_EQ(16u, iv_.size());
143  // Even though the block counter portion of the counter (bytes 8 to 15) is
144  // treated as a 64-bit number, it is recommended that the initialization
145  // vector is treated as a 128-bit number when calculating the next
146  // initialization vector from the previous one. The block counter portion
147  // is already incremented by number of blocks, the other 64 bits of the
148  // counter (bytes 0 to 7) is incremented here if the block counter portion
149  // has overflowed.
150  if (counter_overflow_)
151  Increment64(&counter_[0]);
152  iv_ = counter_;
153  }
154  counter_overflow_ = false;
155 }
156 
157 bool AesCtrEncryptor::SetIv(const std::vector<uint8_t>& iv) {
158  if (!IsIvSizeValid(iv.size())) {
159  LOG(ERROR) << "Invalid IV size: " << iv.size();
160  return false;
161  }
162 
163  block_offset_ = 0;
164  counter_ = iv_ = iv;
165  counter_.resize(AES_BLOCK_SIZE, 0);
166  return true;
167 }
168 
169 AesCbcPkcs5Encryptor::AesCbcPkcs5Encryptor() {}
170 AesCbcPkcs5Encryptor::~AesCbcPkcs5Encryptor() {}
171 
172 bool AesCbcPkcs5Encryptor::InitializeWithIv(const std::vector<uint8_t>& key,
173  const std::vector<uint8_t>& iv) {
174  if (!IsKeySizeValidForAes(key.size())) {
175  LOG(ERROR) << "Invalid AES key size: " << key.size();
176  return false;
177  }
178  if (iv.size() != AES_BLOCK_SIZE) {
179  LOG(ERROR) << "Invalid IV size: " << iv.size();
180  return false;
181  }
182 
183  aes_key_.reset(new AES_KEY());
184  CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
185 
186  iv_ = iv;
187  return true;
188 }
189 
190 size_t AesCbcPkcs5Encryptor::NumPaddingBytes(size_t size) {
191  return AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE);
192 }
193 
194 bool AesCbcPkcs5Encryptor::EncryptData(const uint8_t* plaintext,
195  size_t plaintext_size,
196  uint8_t* ciphertext) {
197  DCHECK(ciphertext);
198  DCHECK(aes_key_);
199 
200  // Pad the input with PKCS5 padding.
201  // TODO(kqyang): Consider more efficient implementation.
202  memcpy(ciphertext, plaintext, plaintext_size);
203  for (size_t i = plaintext_size;
204  i < plaintext_size + NumPaddingBytes(plaintext_size); ++i) {
205  ciphertext[i] = NumPaddingBytes(plaintext_size);
206  }
207 
208  std::vector<uint8_t> iv(iv_);
209  AES_cbc_encrypt(ciphertext, ciphertext,
210  plaintext_size + NumPaddingBytes(plaintext_size),
211  aes_key_.get(), &iv[0], AES_ENCRYPT);
212  return true;
213 }
214 
216 
217 bool AesCbcPkcs5Encryptor::SetIv(const std::vector<uint8_t>& iv) {
218  if (iv.size() != AES_BLOCK_SIZE) {
219  LOG(ERROR) << "Invalid IV size: " << iv.size();
220  return false;
221  }
222 
223  iv_ = iv;
224  return true;
225 }
226 
227 AesCbcCtsEncryptor::AesCbcCtsEncryptor() {}
228 AesCbcCtsEncryptor::~AesCbcCtsEncryptor() {}
229 
230 bool AesCbcCtsEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
231  const std::vector<uint8_t>& iv) {
232  if (!IsKeySizeValidForAes(key.size())) {
233  LOG(ERROR) << "Invalid AES key size: " << key.size();
234  return false;
235  }
236  if (iv.size() != AES_BLOCK_SIZE) {
237  LOG(ERROR) << "Invalid IV size: " << iv.size();
238  return false;
239  }
240 
241  aes_key_.reset(new AES_KEY());
242  CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
243 
244  iv_ = iv;
245  return true;
246 }
247 
248 size_t AesCbcCtsEncryptor::NumPaddingBytes(size_t size) {
249  return 0;
250 }
251 
252 bool AesCbcCtsEncryptor::EncryptData(const uint8_t* plaintext,
253  size_t size,
254  uint8_t* ciphertext) {
255  DCHECK(plaintext);
256  DCHECK(ciphertext);
257 
258  if (size < AES_BLOCK_SIZE) {
259  // Don't have a full block, leave unencrypted.
260  memcpy(ciphertext, plaintext, size);
261  return true;
262  }
263 
264  std::vector<uint8_t> iv(iv_);
265  size_t residual_block_size = size % AES_BLOCK_SIZE;
266  size_t cbc_size = size - residual_block_size;
267 
268  // Encrypt everything but the residual block using CBC.
269  AES_cbc_encrypt(plaintext,
270  ciphertext,
271  cbc_size,
272  aes_key_.get(),
273  &iv[0],
274  AES_ENCRYPT);
275  if (residual_block_size == 0) {
276  // No residual block. No need to do ciphertext stealing.
277  return true;
278  }
279 
280  // Zero-pad the residual block and encrypt using CBC.
281  std::vector<uint8_t> residual_block(plaintext + size - residual_block_size,
282  plaintext + size);
283  residual_block.resize(AES_BLOCK_SIZE, 0);
284  AES_cbc_encrypt(&residual_block[0],
285  &residual_block[0],
286  AES_BLOCK_SIZE,
287  aes_key_.get(),
288  &iv[0],
289  AES_ENCRYPT);
290 
291  // Replace the last full block with the zero-padded, encrypted residual block,
292  // and replace the residual block with the equivalent portion of the last full
293  // encrypted block. It may appear that some encrypted bits of the last full
294  // block are lost, but they are not, as they were used as the IV when
295  // encrypting the zero-padded residual block.
296  uint8_t* residual_ciphertext_block = ciphertext + size - residual_block_size;
297  memcpy(residual_ciphertext_block,
298  residual_ciphertext_block - AES_BLOCK_SIZE,
299  residual_block_size);
300  memcpy(residual_ciphertext_block - AES_BLOCK_SIZE,
301  residual_block.data(),
302  AES_BLOCK_SIZE);
303  return true;
304 }
305 
307 
308 bool AesCbcCtsEncryptor::SetIv(const std::vector<uint8_t>& iv) {
309  if (iv.size() != AES_BLOCK_SIZE) {
310  LOG(ERROR) << "Invalid IV size: " << iv.size();
311  return false;
312  }
313 
314  iv_ = iv;
315  return true;
316 }
317 
318 } // namespace media
319 } // namespace edash_packager
virtual bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv)=0
bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv) override
bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv) override
bool SetIv(const std::vector< uint8_t > &iv) override
bool SetIv(const std::vector< uint8_t > &iv) override
bool SetIv(const std::vector< uint8_t > &iv) override
virtual bool InitializeWithRandomIv(const std::vector< uint8_t > &key, uint8_t iv_size)
bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv) override