Shaka Packager SDK
Loading...
Searching...
No Matches
aes_decryptor.cc
1// Copyright 2016 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_decryptor.h>
8
9#include <algorithm>
10#include <cstddef>
11#include <cstdint>
12#include <cstring>
13#include <vector>
14
15#include <absl/log/check.h>
16#include <absl/log/log.h>
17#include <mbedtls/cipher.h>
18
19#include <packager/macros/crypto.h>
20#include <packager/media/base/aes_cryptor.h>
21#include <packager/media/base/aes_encryptor.h>
22
23namespace shaka {
24namespace media {
25
26AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme)
27 : AesCbcDecryptor(padding_scheme, kDontUseConstantIv) {}
28
29AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme,
30 ConstantIvFlag constant_iv_flag)
31 : AesCryptor(constant_iv_flag), padding_scheme_(padding_scheme) {
32 if (padding_scheme_ != kNoPadding) {
33 CHECK_EQ(constant_iv_flag, kUseConstantIv)
34 << "non-constant iv (cipher block chain across calls) only makes sense "
35 "if the padding_scheme is kNoPadding.";
36 }
37}
38
39AesCbcDecryptor::~AesCbcDecryptor() {}
40
41bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
42 const std::vector<uint8_t>& iv) {
43 if (!SetupCipher(key.size(), kCbcMode)) {
44 return false;
45 }
46
47 if (mbedtls_cipher_setkey(&cipher_ctx_, key.data(),
48 static_cast<int>(8 * key.size()),
49 MBEDTLS_DECRYPT) != 0) {
50 LOG(ERROR) << "Failed to set CBC decryption key";
51 return false;
52 }
53
54 return SetIv(iv);
55}
56
57size_t AesCbcDecryptor::RequiredOutputSize(size_t plaintext_size) {
58 return plaintext_size;
59}
60
61bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
62 size_t ciphertext_size,
63 uint8_t* plaintext,
64 size_t* plaintext_size) {
65 DCHECK(plaintext_size);
66 // Plaintext size is the same as ciphertext size except for pkcs5 padding.
67 // Will update later if using pkcs5 padding. For pkcs5 padding, we still
68 // need at least |ciphertext_size| bytes for intermediate operation.
69 if (*plaintext_size < ciphertext_size) {
70 LOG(ERROR) << "Expecting output size of at least " << ciphertext_size
71 << " bytes.";
72 return false;
73 }
74 *plaintext_size = ciphertext_size;
75
76 // If the ciphertext size is 0, this can be a no-op decrypt, so long as the
77 // padding mode isn't PKCS5.
78 if (ciphertext_size == 0) {
79 if (padding_scheme_ == kPkcs5Padding) {
80 LOG(ERROR) << "Expected ciphertext to be at least " << AES_BLOCK_SIZE
81 << " bytes with Pkcs5 padding.";
82 return false;
83 }
84 return true;
85 }
86 DCHECK(plaintext);
87
88 const size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE;
89 const size_t cbc_size = ciphertext_size - residual_block_size;
90 if (residual_block_size == 0) {
91 CbcDecryptBlocks(ciphertext, ciphertext_size, plaintext,
92 internal_iv_.data());
93 if (padding_scheme_ != kPkcs5Padding)
94 return true;
95
96 // Strip off PKCS5 padding bytes.
97 const uint8_t num_padding_bytes = plaintext[ciphertext_size - 1];
98 if (num_padding_bytes > AES_BLOCK_SIZE) {
99 LOG(ERROR) << "Padding length is too large : "
100 << static_cast<int>(num_padding_bytes);
101 return false;
102 }
103 *plaintext_size -= num_padding_bytes;
104 return true;
105 } else if (padding_scheme_ == kNoPadding) {
106 if (cbc_size > 0) {
107 CbcDecryptBlocks(ciphertext, cbc_size, plaintext, internal_iv_.data());
108 }
109 // The residual block is not encrypted.
110 memcpy(plaintext + cbc_size, ciphertext + cbc_size, residual_block_size);
111 return true;
112 } else if (padding_scheme_ != kCtsPadding) {
113 LOG(ERROR) << "Expecting cipher text size to be multiple of "
114 << AES_BLOCK_SIZE << ", got " << ciphertext_size;
115 return false;
116 }
117
118 DCHECK_EQ(padding_scheme_, kCtsPadding);
119 if (ciphertext_size < AES_BLOCK_SIZE) {
120 // Don't have a full block, leave unencrypted.
121 memcpy(plaintext, ciphertext, ciphertext_size);
122 return true;
123 }
124
125 // AES-CBC decrypt everything up to the next-to-last full block.
126 if (cbc_size > AES_BLOCK_SIZE) {
127 CbcDecryptBlocks(ciphertext, cbc_size - AES_BLOCK_SIZE, plaintext,
128 internal_iv_.data());
129 }
130
131 const uint8_t* next_to_last_ciphertext_block =
132 ciphertext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE;
133 uint8_t* next_to_last_plaintext_block =
134 plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE;
135
136 // Determine what the last IV should be so that we can "skip ahead" in the
137 // CBC decryption.
138 std::vector<uint8_t> last_iv(
139 ciphertext + ciphertext_size - residual_block_size,
140 ciphertext + ciphertext_size);
141 last_iv.resize(AES_BLOCK_SIZE, 0);
142
143 // Decrypt the next-to-last block using the IV determined above. This decrypts
144 // the residual block bits.
145 CbcDecryptBlocks(next_to_last_ciphertext_block, AES_BLOCK_SIZE,
146 next_to_last_plaintext_block, last_iv.data());
147
148 // Swap back the residual block bits and the next-to-last block.
149 if (plaintext == ciphertext) {
150 std::swap_ranges(next_to_last_plaintext_block,
151 next_to_last_plaintext_block + residual_block_size,
152 next_to_last_plaintext_block + AES_BLOCK_SIZE);
153 } else {
154 memcpy(next_to_last_plaintext_block + AES_BLOCK_SIZE,
155 next_to_last_plaintext_block, residual_block_size);
156 memcpy(next_to_last_plaintext_block,
157 next_to_last_ciphertext_block + AES_BLOCK_SIZE, residual_block_size);
158 }
159
160 // Decrypt the next-to-last full block.
161 CbcDecryptBlocks(next_to_last_plaintext_block, AES_BLOCK_SIZE,
162 next_to_last_plaintext_block, internal_iv_.data());
163 return true;
164}
165
166void AesCbcDecryptor::SetIvInternal() {
167 internal_iv_ = iv();
168 internal_iv_.resize(AES_BLOCK_SIZE, 0);
169}
170
171void AesCbcDecryptor::CbcDecryptBlocks(const uint8_t* ciphertext,
172 size_t ciphertext_size,
173 uint8_t* plaintext,
174 uint8_t* iv) {
175 CHECK_EQ(ciphertext_size % AES_BLOCK_SIZE, 0u);
176 CHECK_GT(ciphertext_size, 0u);
177
178 // Copy the final block of ciphertext before decryption, since we could be
179 // decrypting in-place.
180 const uint8_t* last_block = ciphertext + ciphertext_size - AES_BLOCK_SIZE;
181 std::vector<uint8_t> next_iv(last_block, last_block + AES_BLOCK_SIZE);
182
183 size_t output_size = 0;
184 CHECK_EQ(mbedtls_cipher_crypt(&cipher_ctx_, iv, AES_BLOCK_SIZE, ciphertext,
185 ciphertext_size, plaintext, &output_size),
186 0);
187 DCHECK_EQ(output_size % AES_BLOCK_SIZE, 0u);
188
189 memcpy(iv, next_iv.data(), next_iv.size());
190}
191
192} // namespace media
193} // namespace shaka
Class which implements AES-CBC (Cipher block chaining) decryption.
bool InitializeWithIv(const std::vector< uint8_t > &key, const std::vector< uint8_t > &iv) override
AesCbcDecryptor(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.