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