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