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 } // namespace
36 
37 namespace edash_packager {
38 namespace media {
39 
40 AesEncryptor::AesEncryptor() {}
41 AesEncryptor::~AesEncryptor() {}
42 
44  const std::vector<uint8_t>& key,
45  uint8_t iv_size) {
46  std::vector<uint8_t> iv(iv_size, 0);
47  if (RAND_bytes(iv.data(), iv_size) != 1) {
48  LOG(ERROR) << "RAND_bytes failed with error: "
49  << ERR_error_string(ERR_get_error(), NULL);
50  return false;
51  }
52  return InitializeWithIv(key, iv);
53 }
54 
55 bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
56  const std::vector<uint8_t>& iv) {
57  if (!IsKeySizeValidForAes(key.size())) {
58  LOG(ERROR) << "Invalid AES key size: " << key.size();
59  return false;
60  }
61 
62  aes_key_.reset(new AES_KEY());
63  CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0);
64  return SetIv(iv);
65 }
66 
67 bool AesEncryptor::Encrypt(const std::vector<uint8_t>& plaintext,
68  std::vector<uint8_t>* ciphertext) {
69  // Save plaintext size to make it work for in-place conversion, since the
70  // next statement will update the plaintext size.
71  const size_t plaintext_size = plaintext.size();
72  ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size()));
73  return EncryptInternal(plaintext.data(), plaintext_size, ciphertext->data());
74 }
75 
76 bool AesEncryptor::Encrypt(const std::string& plaintext,
77  std::string* ciphertext) {
78  // Save plaintext size to make it work for in-place conversion, since the
79  // next statement will update the plaintext size.
80  const size_t plaintext_size = plaintext.size();
81  ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size()));
82  return EncryptInternal(
83  reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext_size,
84  reinterpret_cast<uint8_t*>(string_as_array(ciphertext)));
85 }
86 
87 AesCtrEncryptor::AesCtrEncryptor()
88  : block_offset_(0),
89  encrypted_counter_(AES_BLOCK_SIZE, 0),
90  counter_overflow_(false) {}
91 
92 AesCtrEncryptor::~AesCtrEncryptor() {}
93 
95  block_offset_ = 0;
96 
97  // As recommended in ISO/IEC FDIS 23001-7: CENC spec, for 64-bit (8-byte)
98  // IV_Sizes, initialization vectors for subsequent samples can be created by
99  // incrementing the initialization vector of the previous sample.
100  // For 128-bit (16-byte) IV_Sizes, initialization vectors for subsequent
101  // samples should be created by adding the block count of the previous sample
102  // to the initialization vector of the previous sample.
103  if (iv().size() == 8) {
104  counter_ = iv();
105  Increment64(&counter_[0]);
106  set_iv(counter_);
107  counter_.resize(AES_BLOCK_SIZE, 0);
108  } else {
109  DCHECK_EQ(16u, iv().size());
110  // Even though the block counter portion of the counter (bytes 8 to 15) is
111  // treated as a 64-bit number, it is recommended that the initialization
112  // vector is treated as a 128-bit number when calculating the next
113  // initialization vector from the previous one. The block counter portion
114  // is already incremented by number of blocks, the other 64 bits of the
115  // counter (bytes 0 to 7) is incremented here if the block counter portion
116  // has overflowed.
117  if (counter_overflow_)
118  Increment64(&counter_[0]);
119  set_iv(counter_);
120  }
121  counter_overflow_ = false;
122 }
123 
124 bool AesCtrEncryptor::SetIv(const std::vector<uint8_t>& iv) {
125  if (!IsIvSizeValid(iv.size())) {
126  LOG(ERROR) << "Invalid IV size: " << iv.size();
127  return false;
128  }
129 
130  block_offset_ = 0;
131  set_iv(iv);
132  counter_ = iv;
133  counter_.resize(AES_BLOCK_SIZE, 0);
134  return true;
135 }
136 
137 bool AesCtrEncryptor::EncryptInternal(const uint8_t* plaintext,
138  size_t plaintext_size,
139  uint8_t* ciphertext) {
140  DCHECK(plaintext);
141  DCHECK(ciphertext);
142  DCHECK(aes_key());
143 
144  for (size_t i = 0; i < plaintext_size; ++i) {
145  if (block_offset_ == 0) {
146  AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key());
147  // As mentioned in ISO/IEC FDIS 23001-7: CENC spec, of the 16 byte counter
148  // block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
149  // simple 64 bit unsigned integer that is incremented by one for each
150  // subsequent block of sample data processed and is kept in network byte
151  // order.
152  if (Increment64(&counter_[8]))
153  counter_overflow_ = true;
154  }
155  ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_];
156  block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE;
157  }
158  return true;
159 }
160 
161 size_t AesCtrEncryptor::NumPaddingBytes(size_t size) const {
162  // No padding needed for CTR.
163  return 0;
164 }
165 
166 AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme,
167  bool chain_across_calls)
168  : padding_scheme_(padding_scheme),
169  chain_across_calls_(chain_across_calls) {
170  if (padding_scheme_ != kNoPadding) {
171  CHECK(!chain_across_calls) << "cipher block chain across calls only makes "
172  "sense if the padding_scheme is kNoPadding.";
173  }
174 }
175 AesCbcEncryptor::~AesCbcEncryptor() {}
176 
178  // From CENC spec: CBC mode Initialization Vectors need not be unique per
179  // sample or Subsample and may be generated randomly or sequentially, e.g.
180  // a per sample IV may be (1) equal to the cipher text of the last encrypted
181  // cipher block (a continous cipher block chain across samples), or (2)
182  // generated by incrementing the previuos IV by the number of cipher blocks in the last
183  // sample or (3) by a fixed amount. We use method (1) here. No separate IV
184  // update is needed.
185 }
186 
187 bool AesCbcEncryptor::SetIv(const std::vector<uint8_t>& iv) {
188  if (iv.size() != AES_BLOCK_SIZE) {
189  LOG(ERROR) << "Invalid IV size: " << iv.size();
190  return false;
191  }
192 
193  set_iv(iv);
194  return true;
195 }
196 
197 bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext,
198  size_t plaintext_size,
199  uint8_t* ciphertext) {
200  DCHECK(aes_key());
201 
202  const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE;
203  if (padding_scheme_ == kNoPadding && residual_block_size != 0) {
204  LOG(ERROR) << "Expecting input size to be multiple of " << AES_BLOCK_SIZE
205  << ", got " << plaintext_size;
206  return false;
207  }
208 
209  // Encrypt everything but the residual block using CBC.
210  const size_t cbc_size = plaintext_size - residual_block_size;
211  std::vector<uint8_t> local_iv(iv());
212  if (cbc_size != 0) {
213  AES_cbc_encrypt(plaintext, ciphertext, cbc_size, aes_key(), local_iv.data(),
214  AES_ENCRYPT);
215  } else if (padding_scheme_ == kCtsPadding) {
216  // Don't have a full block, leave unencrypted.
217  memcpy(ciphertext, plaintext, plaintext_size);
218  return true;
219  }
220  if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) {
221  if (chain_across_calls_)
222  set_iv(local_iv);
223  // No residual block. No need to do padding.
224  return true;
225  }
226  DCHECK(!chain_across_calls_);
227 
228  std::vector<uint8_t> residual_block(plaintext + cbc_size,
229  plaintext + plaintext_size);
230  DCHECK_EQ(residual_block.size(), residual_block_size);
231  uint8_t* residual_ciphertext_block = ciphertext + cbc_size;
232 
233  if (padding_scheme_ == kPkcs5Padding) {
234  const size_t num_padding_bytes = AES_BLOCK_SIZE - residual_block_size;
235  DCHECK_EQ(num_padding_bytes, NumPaddingBytes(plaintext_size));
236  // Pad residue block with PKCS5 padding.
237  residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
238  AES_cbc_encrypt(residual_block.data(), residual_ciphertext_block,
239  AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_ENCRYPT);
240  } else {
241  DCHECK_EQ(padding_scheme_, kCtsPadding);
242 
243  // Zero-pad the residual block and encrypt using CBC.
244  residual_block.resize(AES_BLOCK_SIZE, 0);
245  AES_cbc_encrypt(residual_block.data(), residual_block.data(),
246  AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_ENCRYPT);
247 
248  // Replace the last full block with the zero-padded, encrypted residual
249  // block, and replace the residual block with the equivalent portion of the
250  // last full encrypted block. It may appear that some encrypted bits of the
251  // last full block are lost, but they are not, as they were used as the IV
252  // when encrypting the zero-padded residual block.
253  memcpy(residual_ciphertext_block,
254  residual_ciphertext_block - AES_BLOCK_SIZE, residual_block_size);
255  memcpy(residual_ciphertext_block - AES_BLOCK_SIZE, residual_block.data(),
256  AES_BLOCK_SIZE);
257  }
258  return true;
259 }
260 
261 size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const {
262  return (padding_scheme_ == kPkcs5Padding)
263  ? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE))
264  : 0;
265 }
266 
267 } // namespace media
268 } // namespace edash_packager
bool EncryptInternal(const uint8_t *plaintext, size_t plaintext_size, uint8_t *ciphertext) override
size_t NumPaddingBytes(size_t size) const override
bool SetIv(const std::vector< uint8_t > &iv) override
size_t NumPaddingBytes(size_t size) const override
const std::vector< uint8_t > & iv() const
Definition: aes_encryptor.h:62
virtual bool EncryptInternal(const uint8_t *plaintext, size_t plaintext_size, uint8_t *ciphertext)=0
bool EncryptInternal(const uint8_t *plaintext, size_t plaintext_size, uint8_t *ciphertext) override
bool SetIv(const std::vector< uint8_t > &iv) override
AesCbcEncryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls)
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)
virtual size_t NumPaddingBytes(size_t size) const =0
virtual bool SetIv(const std::vector< uint8_t > &iv)=0