/src/node/src/crypto/crypto_aes.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "crypto/crypto_aes.h" |
2 | | #include "async_wrap-inl.h" |
3 | | #include "base_object-inl.h" |
4 | | #include "crypto/crypto_cipher.h" |
5 | | #include "crypto/crypto_keys.h" |
6 | | #include "crypto/crypto_util.h" |
7 | | #include "env-inl.h" |
8 | | #include "memory_tracker-inl.h" |
9 | | #include "threadpoolwork-inl.h" |
10 | | #include "v8.h" |
11 | | |
12 | | #include <openssl/bn.h> |
13 | | #include <openssl/aes.h> |
14 | | |
15 | | #include <vector> |
16 | | |
17 | | namespace node { |
18 | | |
19 | | using v8::FunctionCallbackInfo; |
20 | | using v8::Just; |
21 | | using v8::Local; |
22 | | using v8::Maybe; |
23 | | using v8::Nothing; |
24 | | using v8::Object; |
25 | | using v8::Uint32; |
26 | | using v8::Value; |
27 | | |
28 | | namespace crypto { |
29 | | namespace { |
30 | | // Implements general AES encryption and decryption for CBC |
31 | | // The key_data must be a secret key. |
32 | | // On success, this function sets out to a new ByteSource |
33 | | // instance containing the results and returns WebCryptoCipherStatus::OK. |
34 | | WebCryptoCipherStatus AES_Cipher( |
35 | | Environment* env, |
36 | | KeyObjectData* key_data, |
37 | | WebCryptoCipherMode cipher_mode, |
38 | | const AESCipherConfig& params, |
39 | | const ByteSource& in, |
40 | 0 | ByteSource* out) { |
41 | 0 | CHECK_NOT_NULL(key_data); |
42 | 0 | CHECK_EQ(key_data->GetKeyType(), kKeyTypeSecret); |
43 | | |
44 | 0 | const int mode = EVP_CIPHER_mode(params.cipher); |
45 | |
|
46 | 0 | CipherCtxPointer ctx(EVP_CIPHER_CTX_new()); |
47 | 0 | EVP_CIPHER_CTX_init(ctx.get()); |
48 | 0 | if (mode == EVP_CIPH_WRAP_MODE) |
49 | 0 | EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); |
50 | |
|
51 | 0 | const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt; |
52 | |
|
53 | 0 | if (!EVP_CipherInit_ex( |
54 | 0 | ctx.get(), |
55 | 0 | params.cipher, |
56 | 0 | nullptr, |
57 | 0 | nullptr, |
58 | 0 | nullptr, |
59 | 0 | encrypt)) { |
60 | | // Cipher init failed |
61 | 0 | return WebCryptoCipherStatus::FAILED; |
62 | 0 | } |
63 | | |
64 | 0 | if (mode == EVP_CIPH_GCM_MODE && !EVP_CIPHER_CTX_ctrl( |
65 | 0 | ctx.get(), |
66 | 0 | EVP_CTRL_AEAD_SET_IVLEN, |
67 | 0 | params.iv.size(), |
68 | 0 | nullptr)) { |
69 | 0 | return WebCryptoCipherStatus::FAILED; |
70 | 0 | } |
71 | | |
72 | 0 | if (!EVP_CIPHER_CTX_set_key_length( |
73 | 0 | ctx.get(), |
74 | 0 | key_data->GetSymmetricKeySize()) || |
75 | 0 | !EVP_CipherInit_ex( |
76 | 0 | ctx.get(), |
77 | 0 | nullptr, |
78 | 0 | nullptr, |
79 | 0 | reinterpret_cast<const unsigned char*>(key_data->GetSymmetricKey()), |
80 | 0 | params.iv.data<unsigned char>(), |
81 | 0 | encrypt)) { |
82 | 0 | return WebCryptoCipherStatus::FAILED; |
83 | 0 | } |
84 | | |
85 | 0 | size_t tag_len = 0; |
86 | |
|
87 | 0 | if (mode == EVP_CIPH_GCM_MODE) { |
88 | 0 | switch (cipher_mode) { |
89 | 0 | case kWebCryptoCipherDecrypt: |
90 | | // If in decrypt mode, the auth tag must be set in the params.tag. |
91 | 0 | CHECK(params.tag); |
92 | 0 | if (!EVP_CIPHER_CTX_ctrl(ctx.get(), |
93 | 0 | EVP_CTRL_AEAD_SET_TAG, |
94 | 0 | params.tag.size(), |
95 | 0 | const_cast<char*>(params.tag.data<char>()))) { |
96 | 0 | return WebCryptoCipherStatus::FAILED; |
97 | 0 | } |
98 | 0 | break; |
99 | 0 | case kWebCryptoCipherEncrypt: |
100 | | // In decrypt mode, we grab the tag length here. We'll use it to |
101 | | // ensure that that allocated buffer has enough room for both the |
102 | | // final block and the auth tag. Unlike our other AES-GCM implementation |
103 | | // in CipherBase, in WebCrypto, the auth tag is concatenated to the end |
104 | | // of the generated ciphertext and returned in the same ArrayBuffer. |
105 | 0 | tag_len = params.length; |
106 | 0 | break; |
107 | 0 | default: |
108 | 0 | UNREACHABLE(); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | 0 | size_t total = 0; |
113 | 0 | int buf_len = in.size() + EVP_CIPHER_CTX_block_size(ctx.get()) + tag_len; |
114 | 0 | int out_len; |
115 | |
|
116 | 0 | if (mode == EVP_CIPH_GCM_MODE && |
117 | 0 | params.additional_data.size() && |
118 | 0 | !EVP_CipherUpdate( |
119 | 0 | ctx.get(), |
120 | 0 | nullptr, |
121 | 0 | &out_len, |
122 | 0 | params.additional_data.data<unsigned char>(), |
123 | 0 | params.additional_data.size())) { |
124 | 0 | return WebCryptoCipherStatus::FAILED; |
125 | 0 | } |
126 | | |
127 | 0 | ByteSource::Builder buf(buf_len); |
128 | | |
129 | | // In some outdated version of OpenSSL (e.g. |
130 | | // ubi81_sharedlibs_openssl111fips_x64) may be used in sharedlib mode, the |
131 | | // logic will be failed when input size is zero. The newly OpenSSL has fixed |
132 | | // it up. But we still have to regard zero as special in Node.js code to |
133 | | // prevent old OpenSSL failure. |
134 | | // |
135 | | // Refs: https://github.com/openssl/openssl/commit/420cb707b880e4fb649094241371701013eeb15f |
136 | | // Refs: https://github.com/nodejs/node/pull/38913#issuecomment-866505244 |
137 | 0 | if (in.size() == 0) { |
138 | 0 | out_len = 0; |
139 | 0 | } else if (!EVP_CipherUpdate(ctx.get(), |
140 | 0 | buf.data<unsigned char>(), |
141 | 0 | &out_len, |
142 | 0 | in.data<unsigned char>(), |
143 | 0 | in.size())) { |
144 | 0 | return WebCryptoCipherStatus::FAILED; |
145 | 0 | } |
146 | | |
147 | 0 | total += out_len; |
148 | 0 | CHECK_LE(out_len, buf_len); |
149 | 0 | out_len = EVP_CIPHER_CTX_block_size(ctx.get()); |
150 | 0 | if (!EVP_CipherFinal_ex( |
151 | 0 | ctx.get(), buf.data<unsigned char>() + total, &out_len)) { |
152 | 0 | return WebCryptoCipherStatus::FAILED; |
153 | 0 | } |
154 | 0 | total += out_len; |
155 | | |
156 | | // If using AES_GCM, grab the generated auth tag and append |
157 | | // it to the end of the ciphertext. |
158 | 0 | if (cipher_mode == kWebCryptoCipherEncrypt && mode == EVP_CIPH_GCM_MODE) { |
159 | 0 | if (!EVP_CIPHER_CTX_ctrl(ctx.get(), |
160 | 0 | EVP_CTRL_AEAD_GET_TAG, |
161 | 0 | tag_len, |
162 | 0 | buf.data<unsigned char>() + total)) |
163 | 0 | return WebCryptoCipherStatus::FAILED; |
164 | 0 | total += tag_len; |
165 | 0 | } |
166 | | |
167 | | // It's possible that we haven't used the full allocated space. Size down. |
168 | 0 | *out = std::move(buf).release(total); |
169 | |
|
170 | 0 | return WebCryptoCipherStatus::OK; |
171 | 0 | } |
172 | | |
173 | | // The AES_CTR implementation here takes it's inspiration from the chromium |
174 | | // implementation here: |
175 | | // https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/aes_ctr.cc |
176 | | |
177 | | template <typename T> |
178 | 0 | T CeilDiv(T a, T b) { |
179 | 0 | return a == 0 ? 0 : 1 + (a - 1) / b; |
180 | 0 | } |
181 | | |
182 | 0 | BignumPointer GetCounter(const AESCipherConfig& params) { |
183 | 0 | unsigned int remainder = (params.length % CHAR_BIT); |
184 | 0 | const unsigned char* data = params.iv.data<unsigned char>(); |
185 | |
|
186 | 0 | if (remainder == 0) { |
187 | 0 | unsigned int byte_length = params.length / CHAR_BIT; |
188 | 0 | return BignumPointer(BN_bin2bn( |
189 | 0 | data + params.iv.size() - byte_length, |
190 | 0 | byte_length, |
191 | 0 | nullptr)); |
192 | 0 | } |
193 | | |
194 | 0 | unsigned int byte_length = |
195 | 0 | CeilDiv(params.length, static_cast<size_t>(CHAR_BIT)); |
196 | |
|
197 | 0 | std::vector<unsigned char> counter( |
198 | 0 | data + params.iv.size() - byte_length, |
199 | 0 | data + params.iv.size()); |
200 | 0 | counter[0] &= ~(0xFF << remainder); |
201 | |
|
202 | 0 | return BignumPointer(BN_bin2bn(counter.data(), counter.size(), nullptr)); |
203 | 0 | } |
204 | | |
205 | | std::vector<unsigned char> BlockWithZeroedCounter( |
206 | 0 | const AESCipherConfig& params) { |
207 | 0 | unsigned int length_bytes = params.length / CHAR_BIT; |
208 | 0 | unsigned int remainder = params.length % CHAR_BIT; |
209 | |
|
210 | 0 | const unsigned char* data = params.iv.data<unsigned char>(); |
211 | |
|
212 | 0 | std::vector<unsigned char> new_counter_block(data, data + params.iv.size()); |
213 | |
|
214 | 0 | size_t index = new_counter_block.size() - length_bytes; |
215 | 0 | memset(&new_counter_block.front() + index, 0, length_bytes); |
216 | |
|
217 | 0 | if (remainder) |
218 | 0 | new_counter_block[index - 1] &= 0xFF << remainder; |
219 | |
|
220 | 0 | return new_counter_block; |
221 | 0 | } |
222 | | |
223 | | WebCryptoCipherStatus AES_CTR_Cipher2( |
224 | | KeyObjectData* key_data, |
225 | | WebCryptoCipherMode cipher_mode, |
226 | | const AESCipherConfig& params, |
227 | | const ByteSource& in, |
228 | | unsigned const char* counter, |
229 | 0 | unsigned char* out) { |
230 | 0 | CipherCtxPointer ctx(EVP_CIPHER_CTX_new()); |
231 | 0 | const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt; |
232 | |
|
233 | 0 | if (!EVP_CipherInit_ex( |
234 | 0 | ctx.get(), |
235 | 0 | params.cipher, |
236 | 0 | nullptr, |
237 | 0 | reinterpret_cast<const unsigned char*>(key_data->GetSymmetricKey()), |
238 | 0 | counter, |
239 | 0 | encrypt)) { |
240 | | // Cipher init failed |
241 | 0 | return WebCryptoCipherStatus::FAILED; |
242 | 0 | } |
243 | | |
244 | 0 | int out_len = 0; |
245 | 0 | int final_len = 0; |
246 | 0 | if (!EVP_CipherUpdate( |
247 | 0 | ctx.get(), |
248 | 0 | out, |
249 | 0 | &out_len, |
250 | 0 | in.data<unsigned char>(), |
251 | 0 | in.size())) { |
252 | 0 | return WebCryptoCipherStatus::FAILED; |
253 | 0 | } |
254 | | |
255 | 0 | if (!EVP_CipherFinal_ex(ctx.get(), out + out_len, &final_len)) |
256 | 0 | return WebCryptoCipherStatus::FAILED; |
257 | | |
258 | 0 | out_len += final_len; |
259 | 0 | if (static_cast<unsigned>(out_len) != in.size()) |
260 | 0 | return WebCryptoCipherStatus::FAILED; |
261 | | |
262 | 0 | return WebCryptoCipherStatus::OK; |
263 | 0 | } |
264 | | |
265 | | WebCryptoCipherStatus AES_CTR_Cipher( |
266 | | Environment* env, |
267 | | KeyObjectData* key_data, |
268 | | WebCryptoCipherMode cipher_mode, |
269 | | const AESCipherConfig& params, |
270 | | const ByteSource& in, |
271 | 0 | ByteSource* out) { |
272 | 0 | BignumPointer num_counters(BN_new()); |
273 | 0 | if (!BN_lshift(num_counters.get(), BN_value_one(), params.length)) |
274 | 0 | return WebCryptoCipherStatus::FAILED; |
275 | | |
276 | 0 | BignumPointer current_counter = GetCounter(params); |
277 | |
|
278 | 0 | BignumPointer num_output(BN_new()); |
279 | |
|
280 | 0 | if (!BN_set_word(num_output.get(), CeilDiv(in.size(), kAesBlockSize))) |
281 | 0 | return WebCryptoCipherStatus::FAILED; |
282 | | |
283 | | // Just like in chromium's implementation, if the counter will |
284 | | // be incremented more than there are counter values, we fail. |
285 | 0 | if (BN_cmp(num_output.get(), num_counters.get()) > 0) |
286 | 0 | return WebCryptoCipherStatus::FAILED; |
287 | | |
288 | 0 | BignumPointer remaining_until_reset(BN_new()); |
289 | 0 | if (!BN_sub(remaining_until_reset.get(), |
290 | 0 | num_counters.get(), |
291 | 0 | current_counter.get())) { |
292 | 0 | return WebCryptoCipherStatus::FAILED; |
293 | 0 | } |
294 | | |
295 | | // Output size is identical to the input size. |
296 | 0 | ByteSource::Builder buf(in.size()); |
297 | | |
298 | | // Also just like in chromium's implementation, if we can process |
299 | | // the input without wrapping the counter, we'll do it as a single |
300 | | // call here. If we can't, we'll fallback to the a two-step approach |
301 | 0 | if (BN_cmp(remaining_until_reset.get(), num_output.get()) >= 0) { |
302 | 0 | auto status = AES_CTR_Cipher2(key_data, |
303 | 0 | cipher_mode, |
304 | 0 | params, |
305 | 0 | in, |
306 | 0 | params.iv.data<unsigned char>(), |
307 | 0 | buf.data<unsigned char>()); |
308 | 0 | if (status == WebCryptoCipherStatus::OK) *out = std::move(buf).release(); |
309 | 0 | return status; |
310 | 0 | } |
311 | | |
312 | 0 | BN_ULONG blocks_part1 = BN_get_word(remaining_until_reset.get()); |
313 | 0 | BN_ULONG input_size_part1 = blocks_part1 * kAesBlockSize; |
314 | | |
315 | | // Encrypt the first part... |
316 | 0 | auto status = |
317 | 0 | AES_CTR_Cipher2(key_data, |
318 | 0 | cipher_mode, |
319 | 0 | params, |
320 | 0 | ByteSource::Foreign(in.data<char>(), input_size_part1), |
321 | 0 | params.iv.data<unsigned char>(), |
322 | 0 | buf.data<unsigned char>()); |
323 | |
|
324 | 0 | if (status != WebCryptoCipherStatus::OK) |
325 | 0 | return status; |
326 | | |
327 | | // Wrap the counter around to zero |
328 | 0 | std::vector<unsigned char> new_counter_block = BlockWithZeroedCounter(params); |
329 | | |
330 | | // Encrypt the second part... |
331 | 0 | status = |
332 | 0 | AES_CTR_Cipher2(key_data, |
333 | 0 | cipher_mode, |
334 | 0 | params, |
335 | 0 | ByteSource::Foreign(in.data<char>() + input_size_part1, |
336 | 0 | in.size() - input_size_part1), |
337 | 0 | new_counter_block.data(), |
338 | 0 | buf.data<unsigned char>() + input_size_part1); |
339 | |
|
340 | 0 | if (status == WebCryptoCipherStatus::OK) *out = std::move(buf).release(); |
341 | |
|
342 | 0 | return status; |
343 | 0 | } |
344 | | |
345 | | bool ValidateIV( |
346 | | Environment* env, |
347 | | CryptoJobMode mode, |
348 | | Local<Value> value, |
349 | 0 | AESCipherConfig* params) { |
350 | 0 | ArrayBufferOrViewContents<char> iv(value); |
351 | 0 | if (UNLIKELY(!iv.CheckSizeInt32())) { |
352 | 0 | THROW_ERR_OUT_OF_RANGE(env, "iv is too big"); |
353 | 0 | return false; |
354 | 0 | } |
355 | 0 | params->iv = (mode == kCryptoJobAsync) |
356 | 0 | ? iv.ToCopy() |
357 | 0 | : iv.ToByteSource(); |
358 | 0 | return true; |
359 | 0 | } |
360 | | |
361 | | bool ValidateCounter( |
362 | | Environment* env, |
363 | | Local<Value> value, |
364 | 0 | AESCipherConfig* params) { |
365 | 0 | CHECK(value->IsUint32()); // Length |
366 | 0 | params->length = value.As<Uint32>()->Value(); |
367 | 0 | if (params->iv.size() != 16 || |
368 | 0 | params->length == 0 || |
369 | 0 | params->length > 128) { |
370 | 0 | THROW_ERR_CRYPTO_INVALID_COUNTER(env); |
371 | 0 | return false; |
372 | 0 | } |
373 | 0 | return true; |
374 | 0 | } |
375 | | |
376 | | bool ValidateAuthTag( |
377 | | Environment* env, |
378 | | CryptoJobMode mode, |
379 | | WebCryptoCipherMode cipher_mode, |
380 | | Local<Value> value, |
381 | 0 | AESCipherConfig* params) { |
382 | 0 | switch (cipher_mode) { |
383 | 0 | case kWebCryptoCipherDecrypt: { |
384 | 0 | if (!IsAnyBufferSource(value)) { |
385 | 0 | THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env); |
386 | 0 | return false; |
387 | 0 | } |
388 | 0 | ArrayBufferOrViewContents<char> tag_contents(value); |
389 | 0 | if (UNLIKELY(!tag_contents.CheckSizeInt32())) { |
390 | 0 | THROW_ERR_OUT_OF_RANGE(env, "tagLength is too big"); |
391 | 0 | return false; |
392 | 0 | } |
393 | 0 | params->tag = mode == kCryptoJobAsync |
394 | 0 | ? tag_contents.ToCopy() |
395 | 0 | : tag_contents.ToByteSource(); |
396 | 0 | break; |
397 | 0 | } |
398 | 0 | case kWebCryptoCipherEncrypt: { |
399 | 0 | if (!value->IsUint32()) { |
400 | 0 | THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env); |
401 | 0 | return false; |
402 | 0 | } |
403 | 0 | params->length = value.As<Uint32>()->Value(); |
404 | 0 | if (params->length > 128) { |
405 | 0 | THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env); |
406 | 0 | return false; |
407 | 0 | } |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | default: |
411 | 0 | UNREACHABLE(); |
412 | 0 | } |
413 | 0 | return true; |
414 | 0 | } |
415 | | |
416 | | bool ValidateAdditionalData( |
417 | | Environment* env, |
418 | | CryptoJobMode mode, |
419 | | Local<Value> value, |
420 | 0 | AESCipherConfig* params) { |
421 | | // Additional Data |
422 | 0 | if (IsAnyBufferSource(value)) { |
423 | 0 | ArrayBufferOrViewContents<char> additional(value); |
424 | 0 | if (UNLIKELY(!additional.CheckSizeInt32())) { |
425 | 0 | THROW_ERR_OUT_OF_RANGE(env, "additionalData is too big"); |
426 | 0 | return false; |
427 | 0 | } |
428 | 0 | params->additional_data = mode == kCryptoJobAsync |
429 | 0 | ? additional.ToCopy() |
430 | 0 | : additional.ToByteSource(); |
431 | 0 | } |
432 | 0 | return true; |
433 | 0 | } |
434 | | |
435 | 0 | void UseDefaultIV(AESCipherConfig* params) { |
436 | 0 | params->iv = ByteSource::Foreign(kDefaultWrapIV, strlen(kDefaultWrapIV)); |
437 | 0 | } |
438 | | } // namespace |
439 | | |
440 | | AESCipherConfig::AESCipherConfig(AESCipherConfig&& other) noexcept |
441 | 0 | : mode(other.mode), |
442 | 0 | variant(other.variant), |
443 | 0 | cipher(other.cipher), |
444 | 0 | length(other.length), |
445 | 0 | iv(std::move(other.iv)), |
446 | 0 | additional_data(std::move(other.additional_data)), |
447 | 0 | tag(std::move(other.tag)) {} |
448 | | |
449 | 0 | AESCipherConfig& AESCipherConfig::operator=(AESCipherConfig&& other) noexcept { |
450 | 0 | if (&other == this) return *this; |
451 | 0 | this->~AESCipherConfig(); |
452 | 0 | return *new (this) AESCipherConfig(std::move(other)); |
453 | 0 | } |
454 | | |
455 | 0 | void AESCipherConfig::MemoryInfo(MemoryTracker* tracker) const { |
456 | | // If mode is sync, then the data in each of these properties |
457 | | // is not owned by the AESCipherConfig, so we ignore it. |
458 | 0 | if (mode == kCryptoJobAsync) { |
459 | 0 | tracker->TrackFieldWithSize("iv", iv.size()); |
460 | 0 | tracker->TrackFieldWithSize("additional_data", additional_data.size()); |
461 | 0 | tracker->TrackFieldWithSize("tag", tag.size()); |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | Maybe<bool> AESCipherTraits::AdditionalConfig( |
466 | | CryptoJobMode mode, |
467 | | const FunctionCallbackInfo<Value>& args, |
468 | | unsigned int offset, |
469 | | WebCryptoCipherMode cipher_mode, |
470 | 0 | AESCipherConfig* params) { |
471 | 0 | Environment* env = Environment::GetCurrent(args); |
472 | |
|
473 | 0 | params->mode = mode; |
474 | |
|
475 | 0 | CHECK(args[offset]->IsUint32()); // Key Variant |
476 | 0 | params->variant = |
477 | 0 | static_cast<AESKeyVariant>(args[offset].As<Uint32>()->Value()); |
478 | |
|
479 | 0 | int cipher_nid; |
480 | |
|
481 | 0 | switch (params->variant) { |
482 | 0 | case kKeyVariantAES_CTR_128: |
483 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params) || |
484 | 0 | !ValidateCounter(env, args[offset + 2], params)) { |
485 | 0 | return Nothing<bool>(); |
486 | 0 | } |
487 | 0 | cipher_nid = NID_aes_128_ctr; |
488 | 0 | break; |
489 | 0 | case kKeyVariantAES_CTR_192: |
490 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params) || |
491 | 0 | !ValidateCounter(env, args[offset + 2], params)) { |
492 | 0 | return Nothing<bool>(); |
493 | 0 | } |
494 | 0 | cipher_nid = NID_aes_192_ctr; |
495 | 0 | break; |
496 | 0 | case kKeyVariantAES_CTR_256: |
497 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params) || |
498 | 0 | !ValidateCounter(env, args[offset + 2], params)) { |
499 | 0 | return Nothing<bool>(); |
500 | 0 | } |
501 | 0 | cipher_nid = NID_aes_256_ctr; |
502 | 0 | break; |
503 | 0 | case kKeyVariantAES_CBC_128: |
504 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params)) |
505 | 0 | return Nothing<bool>(); |
506 | 0 | cipher_nid = NID_aes_128_cbc; |
507 | 0 | break; |
508 | 0 | case kKeyVariantAES_CBC_192: |
509 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params)) |
510 | 0 | return Nothing<bool>(); |
511 | 0 | cipher_nid = NID_aes_192_cbc; |
512 | 0 | break; |
513 | 0 | case kKeyVariantAES_CBC_256: |
514 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params)) |
515 | 0 | return Nothing<bool>(); |
516 | 0 | cipher_nid = NID_aes_256_cbc; |
517 | 0 | break; |
518 | 0 | case kKeyVariantAES_KW_128: |
519 | 0 | UseDefaultIV(params); |
520 | 0 | cipher_nid = NID_id_aes128_wrap; |
521 | 0 | break; |
522 | 0 | case kKeyVariantAES_KW_192: |
523 | 0 | UseDefaultIV(params); |
524 | 0 | cipher_nid = NID_id_aes192_wrap; |
525 | 0 | break; |
526 | 0 | case kKeyVariantAES_KW_256: |
527 | 0 | UseDefaultIV(params); |
528 | 0 | cipher_nid = NID_id_aes256_wrap; |
529 | 0 | break; |
530 | 0 | case kKeyVariantAES_GCM_128: |
531 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params) || |
532 | 0 | !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) || |
533 | 0 | !ValidateAdditionalData(env, mode, args[offset + 3], params)) { |
534 | 0 | return Nothing<bool>(); |
535 | 0 | } |
536 | 0 | cipher_nid = NID_aes_128_gcm; |
537 | 0 | break; |
538 | 0 | case kKeyVariantAES_GCM_192: |
539 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params) || |
540 | 0 | !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) || |
541 | 0 | !ValidateAdditionalData(env, mode, args[offset + 3], params)) { |
542 | 0 | return Nothing<bool>(); |
543 | 0 | } |
544 | 0 | cipher_nid = NID_aes_192_gcm; |
545 | 0 | break; |
546 | 0 | case kKeyVariantAES_GCM_256: |
547 | 0 | if (!ValidateIV(env, mode, args[offset + 1], params) || |
548 | 0 | !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) || |
549 | 0 | !ValidateAdditionalData(env, mode, args[offset + 3], params)) { |
550 | 0 | return Nothing<bool>(); |
551 | 0 | } |
552 | 0 | cipher_nid = NID_aes_256_gcm; |
553 | 0 | break; |
554 | 0 | default: |
555 | 0 | UNREACHABLE(); |
556 | 0 | } |
557 | | |
558 | 0 | params->cipher = EVP_get_cipherbynid(cipher_nid); |
559 | 0 | if (params->cipher == nullptr) { |
560 | 0 | THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); |
561 | 0 | return Nothing<bool>(); |
562 | 0 | } |
563 | | |
564 | 0 | if (params->iv.size() < |
565 | 0 | static_cast<size_t>(EVP_CIPHER_iv_length(params->cipher))) { |
566 | 0 | THROW_ERR_CRYPTO_INVALID_IV(env); |
567 | 0 | return Nothing<bool>(); |
568 | 0 | } |
569 | | |
570 | 0 | return Just(true); |
571 | 0 | } |
572 | | |
573 | | WebCryptoCipherStatus AESCipherTraits::DoCipher( |
574 | | Environment* env, |
575 | | std::shared_ptr<KeyObjectData> key_data, |
576 | | WebCryptoCipherMode cipher_mode, |
577 | | const AESCipherConfig& params, |
578 | | const ByteSource& in, |
579 | 0 | ByteSource* out) { |
580 | 0 | #define V(name, fn) \ |
581 | 0 | case kKeyVariantAES_ ## name: \ |
582 | 0 | return fn(env, key_data.get(), cipher_mode, params, in, out); |
583 | 0 | switch (params.variant) { |
584 | 0 | VARIANTS(V) |
585 | 0 | default: |
586 | 0 | UNREACHABLE(); |
587 | 0 | } |
588 | 0 | #undef V |
589 | 0 | } |
590 | | |
591 | 8.84k | void AES::Initialize(Environment* env, Local<Object> target) { |
592 | 8.84k | AESCryptoJob::Initialize(env, target); |
593 | | |
594 | 106k | #define V(name, _) NODE_DEFINE_CONSTANT(target, kKeyVariantAES_ ## name); |
595 | 106k | VARIANTS(V) |
596 | 8.84k | #undef V |
597 | 8.84k | } |
598 | | |
599 | 0 | void AES::RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
600 | 0 | AESCryptoJob::RegisterExternalReferences(registry); |
601 | 0 | } |
602 | | |
603 | | } // namespace crypto |
604 | | } // namespace node |