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