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