GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/crypto/crypto_aes.cc Lines: 216 319 67.7 %
Date: 2021-06-01 04:11:54 Branches: 100 206 48.5 %

Line Branch Exec Source
1
#include "crypto/crypto_aes.h"
2
#include "crypto/crypto_cipher.h"
3
#include "crypto/crypto_keys.h"
4
#include "crypto/crypto_util.h"
5
#include "allocated_buffer-inl.h"
6
#include "async_wrap-inl.h"
7
#include "base_object-inl.h"
8
#include "env-inl.h"
9
#include "memory_tracker-inl.h"
10
#include "threadpoolwork-inl.h"
11
#include "v8.h"
12
13
#include <openssl/bn.h>
14
#include <openssl/aes.h>
15
16
#include <vector>
17
18
namespace node {
19
20
using v8::FunctionCallbackInfo;
21
using v8::Just;
22
using v8::Local;
23
using v8::Maybe;
24
using v8::Nothing;
25
using v8::Object;
26
using v8::Uint32;
27
using v8::Value;
28
29
namespace crypto {
30
namespace {
31
// Implements general AES encryption and decryption for CBC
32
// The key_data must be a secret key.
33
// On success, this function sets out to a new AllocatedBuffer
34
// instance containing the results and returns WebCryptoCipherStatus::OK.
35
246
WebCryptoCipherStatus AES_Cipher(
36
    Environment* env,
37
    KeyObjectData* key_data,
38
    WebCryptoCipherMode cipher_mode,
39
    const AESCipherConfig& params,
40
    const ByteSource& in,
41
    ByteSource* out) {
42
246
  CHECK_NOT_NULL(key_data);
43
246
  CHECK_EQ(key_data->GetKeyType(), kKeyTypeSecret);
44
45
246
  const int mode = EVP_CIPHER_mode(params.cipher);
46
47
492
  CipherCtxPointer ctx(EVP_CIPHER_CTX_new());
48
246
  EVP_CIPHER_CTX_init(ctx.get());
49
246
  if (mode == EVP_CIPH_WRAP_MODE)
50
24
    EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
51
52
246
  const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt;
53
54
492
  if (!EVP_CipherInit_ex(
55
          ctx.get(),
56
246
          params.cipher,
57
          nullptr,
58
          nullptr,
59
          nullptr,
60
          encrypt)) {
61
    // Cipher init failed
62
    return WebCryptoCipherStatus::FAILED;
63
  }
64
65

394
  if (mode == EVP_CIPH_GCM_MODE && !EVP_CIPHER_CTX_ctrl(
66
        ctx.get(),
67
        EVP_CTRL_AEAD_SET_IVLEN,
68
148
        params.iv.size(),
69
        nullptr)) {
70
    return WebCryptoCipherStatus::FAILED;
71
  }
72
73
492
  if (!EVP_CIPHER_CTX_set_key_length(
74
          ctx.get(),
75

738
          key_data->GetSymmetricKeySize()) ||
76
492
      !EVP_CipherInit_ex(
77
          ctx.get(),
78
          nullptr,
79
          nullptr,
80
246
          reinterpret_cast<const unsigned char*>(key_data->GetSymmetricKey()),
81
          params.iv.data<unsigned char>(),
82
          encrypt)) {
83
    return WebCryptoCipherStatus::FAILED;
84
  }
85
86
246
  size_t tag_len = 0;
87
88
246
  if (mode == EVP_CIPH_GCM_MODE) {
89
148
    switch (cipher_mode) {
90
      case kWebCryptoCipherDecrypt:
91
        // If in decrypt mode, the auth tag must be set in the params.tag.
92
60
        CHECK(params.tag);
93
60
        if (!EVP_CIPHER_CTX_ctrl(
94
                ctx.get(),
95
                EVP_CTRL_AEAD_SET_TAG,
96
60
                params.tag.size(),
97
60
                const_cast<char*>(params.tag.get()))) {
98
          return WebCryptoCipherStatus::FAILED;
99
        }
100
60
        break;
101
      case kWebCryptoCipherEncrypt:
102
        // In decrypt mode, we grab the tag length here. We'll use it to
103
        // ensure that that allocated buffer has enough room for both the
104
        // final block and the auth tag. Unlike our other AES-GCM implementation
105
        // in CipherBase, in WebCrypto, the auth tag is concatenated to the end
106
        // of the generated ciphertext and returned in the same ArrayBuffer.
107
88
        tag_len = params.length;
108
88
        break;
109
      default:
110
        UNREACHABLE();
111
    }
112
  }
113
114
246
  size_t total = 0;
115
246
  int buf_len = in.size() + EVP_CIPHER_CTX_block_size(ctx.get()) + tag_len;
116
  int out_len;
117
118

394
  if (mode == EVP_CIPH_GCM_MODE &&
119

350
      params.additional_data.size() &&
120
104
      !EVP_CipherUpdate(
121
            ctx.get(),
122
            nullptr,
123
            &out_len,
124
            params.additional_data.data<unsigned char>(),
125
104
            params.additional_data.size())) {
126
    return WebCryptoCipherStatus::FAILED;
127
  }
128
129
246
  char* data = MallocOpenSSL<char>(buf_len);
130
492
  ByteSource buf = ByteSource::Allocated(data, buf_len);
131
246
  unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
132
133
246
  if (!EVP_CipherUpdate(
134
          ctx.get(),
135
          ptr,
136
          &out_len,
137
          in.data<unsigned char>(),
138
246
          in.size())) {
139
    return WebCryptoCipherStatus::FAILED;
140
  }
141
142
246
  total += out_len;
143
246
  CHECK_LE(out_len, buf_len);
144
246
  ptr += out_len;
145
246
  out_len = EVP_CIPHER_CTX_block_size(ctx.get());
146
246
  if (!EVP_CipherFinal_ex(ctx.get(), ptr, &out_len)) {
147
6
    return WebCryptoCipherStatus::FAILED;
148
  }
149
240
  total += out_len;
150
151
  // If using AES_GCM, grab the generated auth tag and append
152
  // it to the end of the ciphertext.
153

240
  if (cipher_mode == kWebCryptoCipherEncrypt && mode == EVP_CIPH_GCM_MODE) {
154
88
    data += out_len;
155
88
    if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, tag_len, ptr))
156
      return WebCryptoCipherStatus::FAILED;
157
88
    total += tag_len;
158
  }
159
160
  // It's possible that we haven't used the full allocated space. Size down.
161
240
  buf.Resize(total);
162
240
  *out = std::move(buf);
163
164
240
  return WebCryptoCipherStatus::OK;
165
}
166
167
// The AES_CTR implementation here takes it's inspiration from the chromium
168
// implementation here:
169
// https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/aes_ctr.cc
170
171
template <typename T>
172
68
T CeilDiv(T a, T b) {
173
68
  return a == 0 ? 0 : 1 + (a - 1) / b;
174
}
175
176
68
BignumPointer GetCounter(const AESCipherConfig& params) {
177
68
  unsigned int remainder = (params.length % CHAR_BIT);
178
68
  const unsigned char* data = params.iv.data<unsigned char>();
179
180
68
  if (remainder == 0) {
181
68
    unsigned int byte_length = params.length / CHAR_BIT;
182
    return BignumPointer(BN_bin2bn(
183
68
        data + params.iv.size() - byte_length,
184
        byte_length,
185
136
        nullptr));
186
  }
187
188
  unsigned int byte_length =
189
      CeilDiv(params.length, static_cast<size_t>(CHAR_BIT));
190
191
  std::vector<unsigned char> counter(
192
      data + params.iv.size() - byte_length,
193
      data + params.iv.size());
194
  counter[0] &= ~(0xFF << remainder);
195
196
  return BignumPointer(BN_bin2bn(counter.data(), counter.size(), nullptr));
197
}
198
199
std::vector<unsigned char> BlockWithZeroedCounter(
200
    const AESCipherConfig& params) {
201
  unsigned int length_bytes = params.length / CHAR_BIT;
202
  unsigned int remainder = params.length % CHAR_BIT;
203
204
  const unsigned char* data = params.iv.data<unsigned char>();
205
206
  std::vector<unsigned char> new_counter_block(data, data + params.iv.size());
207
208
  size_t index = new_counter_block.size() - length_bytes;
209
  memset(&new_counter_block.front() + index, 0, length_bytes);
210
211
  if (remainder)
212
    new_counter_block[index - 1] &= 0xFF << remainder;
213
214
  return new_counter_block;
215
}
216
217
68
WebCryptoCipherStatus AES_CTR_Cipher2(
218
    KeyObjectData* key_data,
219
    WebCryptoCipherMode cipher_mode,
220
    const AESCipherConfig& params,
221
    const ByteSource& in,
222
    unsigned const char* counter,
223
    unsigned char* out) {
224
136
  CipherCtxPointer ctx(EVP_CIPHER_CTX_new());
225
68
  const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt;
226
227
136
  if (!EVP_CipherInit_ex(
228
          ctx.get(),
229
68
          params.cipher,
230
          nullptr,
231
68
          reinterpret_cast<const unsigned char*>(key_data->GetSymmetricKey()),
232
          counter,
233
          encrypt)) {
234
    // Cipher init failed
235
    return WebCryptoCipherStatus::FAILED;
236
  }
237
238
68
  int out_len = 0;
239
68
  int final_len = 0;
240
68
  if (!EVP_CipherUpdate(
241
          ctx.get(),
242
          out,
243
          &out_len,
244
          in.data<unsigned char>(),
245
68
          in.size())) {
246
    return WebCryptoCipherStatus::FAILED;
247
  }
248
249
68
  if (!EVP_CipherFinal_ex(ctx.get(), out + out_len, &final_len))
250
    return WebCryptoCipherStatus::FAILED;
251
252
68
  out_len += final_len;
253
68
  if (static_cast<unsigned>(out_len) != in.size())
254
    return WebCryptoCipherStatus::FAILED;
255
256
68
  return WebCryptoCipherStatus::OK;
257
}
258
259
68
WebCryptoCipherStatus AES_CTR_Cipher(
260
    Environment* env,
261
    KeyObjectData* key_data,
262
    WebCryptoCipherMode cipher_mode,
263
    const AESCipherConfig& params,
264
    const ByteSource& in,
265
    ByteSource* out) {
266
136
  BignumPointer num_counters(BN_new());
267
68
  if (!BN_lshift(num_counters.get(), BN_value_one(), params.length))
268
    return WebCryptoCipherStatus::FAILED;
269
270
136
  BignumPointer current_counter = GetCounter(params);
271
272
136
  BignumPointer num_output(BN_new());
273
274
68
  if (!BN_set_word(num_output.get(), CeilDiv(in.size(), kAesBlockSize)))
275
    return WebCryptoCipherStatus::FAILED;
276
277
  // Just like in chromium's implementation, if the counter will
278
  // be incremented more than there are counter values, we fail.
279
68
  if (BN_cmp(num_output.get(), num_counters.get()) > 0)
280
    return WebCryptoCipherStatus::FAILED;
281
282
136
  BignumPointer remaining_until_reset(BN_new());
283
68
  if (!BN_sub(remaining_until_reset.get(),
284
68
              num_counters.get(),
285
68
              current_counter.get())) {
286
    return WebCryptoCipherStatus::FAILED;
287
  }
288
289
  // Output size is identical to the input size
290
68
  char* data = MallocOpenSSL<char>(in.size());
291
136
  ByteSource buf = ByteSource::Allocated(data, in.size());
292
68
  unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
293
294
  // Also just like in chromium's implementation, if we can process
295
  // the input without wrapping the counter, we'll do it as a single
296
  // call here. If we can't, we'll fallback to the a two-step approach
297
68
  if (BN_cmp(remaining_until_reset.get(), num_output.get()) >= 0) {
298
68
    auto status = AES_CTR_Cipher2(
299
        key_data,
300
        cipher_mode,
301
        params,
302
        in,
303
        params.iv.data<unsigned char>(),
304
68
        ptr);
305
68
    if (status == WebCryptoCipherStatus::OK)
306
68
      *out = std::move(buf);
307
68
    return status;
308
  }
309
310
  BN_ULONG blocks_part1 = BN_get_word(remaining_until_reset.get());
311
  BN_ULONG input_size_part1 = blocks_part1 * kAesBlockSize;
312
313
  // Encrypt the first part...
314
  auto status = AES_CTR_Cipher2(
315
      key_data,
316
      cipher_mode,
317
      params,
318
      ByteSource::Foreign(in.get(), input_size_part1),
319
      params.iv.data<unsigned char>(),
320
      ptr);
321
322
  if (status != WebCryptoCipherStatus::OK)
323
    return status;
324
325
  // Wrap the counter around to zero
326
  std::vector<unsigned char> new_counter_block = BlockWithZeroedCounter(params);
327
328
  // Encrypt the second part...
329
  status = AES_CTR_Cipher2(
330
      key_data,
331
      cipher_mode,
332
      params,
333
      ByteSource::Foreign(
334
          in.get() + input_size_part1,
335
          in.size() - input_size_part1),
336
      new_counter_block.data(),
337
      ptr + input_size_part1);
338
339
  if (status == WebCryptoCipherStatus::OK)
340
    *out = std::move(buf);
341
342
  return status;
343
}
344
345
290
bool ValidateIV(
346
    Environment* env,
347
    CryptoJobMode mode,
348
    Local<Value> value,
349
    AESCipherConfig* params) {
350
580
  ArrayBufferOrViewContents<char> iv(value);
351
290
  if (UNLIKELY(!iv.CheckSizeInt32())) {
352
    THROW_ERR_OUT_OF_RANGE(env, "iv is too big");
353
    return false;
354
  }
355
  params->iv = (mode == kCryptoJobAsync)
356
580
      ? iv.ToCopy()
357
290
      : iv.ToByteSource();
358
290
  return true;
359
}
360
361
68
bool ValidateCounter(
362
  Environment* env,
363
  Local<Value> value,
364
  AESCipherConfig* params) {
365
68
  CHECK(value->IsUint32());  // Length
366
136
  params->length = value.As<Uint32>()->Value();
367

204
  if (params->iv.size() != 16 ||
368

136
      params->length == 0 ||
369
68
      params->length > 128) {
370
    THROW_ERR_CRYPTO_INVALID_COUNTER(env);
371
    return false;
372
  }
373
68
  return true;
374
}
375
376
148
bool ValidateAuthTag(
377
    Environment* env,
378
    CryptoJobMode mode,
379
    WebCryptoCipherMode cipher_mode,
380
    Local<Value> value,
381
    AESCipherConfig* params) {
382
148
  switch (cipher_mode) {
383
    case kWebCryptoCipherDecrypt: {
384
60
      if (!IsAnyByteSource(value)) {
385
        THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
386
        return false;
387
      }
388
60
      ArrayBufferOrViewContents<char> tag_contents(value);
389
60
      if (UNLIKELY(!tag_contents.CheckSizeInt32())) {
390
        THROW_ERR_OUT_OF_RANGE(env, "tagLength is too big");
391
        return false;
392
      }
393
      params->tag = mode == kCryptoJobAsync
394
120
          ? tag_contents.ToCopy()
395
60
          : tag_contents.ToByteSource();
396
60
      break;
397
    }
398
    case kWebCryptoCipherEncrypt: {
399
88
      if (!value->IsUint32()) {
400
        THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
401
        return false;
402
      }
403
176
      params->length = value.As<Uint32>()->Value();
404
88
      if (params->length > 128) {
405
        THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
406
        return false;
407
      }
408
88
      break;
409
    }
410
    default:
411
      UNREACHABLE();
412
  }
413
148
  return true;
414
}
415
416
148
bool ValidateAdditionalData(
417
    Environment* env,
418
    CryptoJobMode mode,
419
    Local<Value> value,
420
    AESCipherConfig* params) {
421
  // Additional Data
422
148
  if (IsAnyByteSource(value)) {
423
208
    ArrayBufferOrViewContents<char> additional(value);
424
104
    if (UNLIKELY(!additional.CheckSizeInt32())) {
425
      THROW_ERR_OUT_OF_RANGE(env, "additionalData is too big");
426
      return false;
427
    }
428
    params->additional_data = mode == kCryptoJobAsync
429

208
        ? additional.ToCopy()
430
104
        : additional.ToByteSource();
431
  }
432
148
  return true;
433
}
434
435
24
void UseDefaultIV(AESCipherConfig* params) {
436
24
  params->iv = ByteSource::Foreign(kDefaultWrapIV, strlen(kDefaultWrapIV));
437
24
}
438
}  // namespace
439
440
314
AESCipherConfig::AESCipherConfig(AESCipherConfig&& other) noexcept
441
314
    : mode(other.mode),
442
314
      variant(other.variant),
443
314
      cipher(other.cipher),
444
314
      length(other.length),
445
314
      iv(std::move(other.iv)),
446
314
      additional_data(std::move(other.additional_data)),
447
2198
      tag(std::move(other.tag)) {}
448
449
AESCipherConfig& AESCipherConfig::operator=(AESCipherConfig&& other) noexcept {
450
  if (&other == this) return *this;
451
  this->~AESCipherConfig();
452
  return *new (this) AESCipherConfig(std::move(other));
453
}
454
455
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
  if (mode == kCryptoJobAsync) {
459
    tracker->TrackFieldWithSize("iv", iv.size());
460
    tracker->TrackFieldWithSize("additional_data", additional_data.size());
461
    tracker->TrackFieldWithSize("tag", tag.size());
462
  }
463
}
464
465
314
Maybe<bool> AESCipherTraits::AdditionalConfig(
466
    CryptoJobMode mode,
467
    const FunctionCallbackInfo<Value>& args,
468
    unsigned int offset,
469
    WebCryptoCipherMode cipher_mode,
470
    AESCipherConfig* params) {
471
314
  Environment* env = Environment::GetCurrent(args);
472
473
314
  params->mode = mode;
474
475
942
  CHECK(args[offset]->IsUint32());  // Key Variant
476
314
  params->variant =
477
1570
      static_cast<AESKeyVariant>(args[offset].As<Uint32>()->Value());
478
479
  int cipher_nid;
480
481



314
  switch (params->variant) {
482
    case kKeyVariantAES_CTR_128:
483

189
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
484
126
          !ValidateCounter(env, args[offset + 2], params)) {
485
        return Nothing<bool>();
486
      }
487
63
      cipher_nid = NID_aes_128_ctr;
488
63
      break;
489
    case kKeyVariantAES_CTR_192:
490
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
491
          !ValidateCounter(env, args[offset + 2], params)) {
492
        return Nothing<bool>();
493
      }
494
      cipher_nid = NID_aes_192_ctr;
495
      break;
496
    case kKeyVariantAES_CTR_256:
497

15
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
498
10
          !ValidateCounter(env, args[offset + 2], params)) {
499
        return Nothing<bool>();
500
      }
501
5
      cipher_nid = NID_aes_256_ctr;
502
5
      break;
503
    case kKeyVariantAES_CBC_128:
504
132
      if (!ValidateIV(env, mode, args[offset + 1], params))
505
        return Nothing<bool>();
506
66
      cipher_nid = NID_aes_128_cbc;
507
66
      break;
508
    case kKeyVariantAES_CBC_192:
509
      if (!ValidateIV(env, mode, args[offset + 1], params))
510
        return Nothing<bool>();
511
      cipher_nid = NID_aes_192_cbc;
512
      break;
513
    case kKeyVariantAES_CBC_256:
514
16
      if (!ValidateIV(env, mode, args[offset + 1], params))
515
        return Nothing<bool>();
516
8
      cipher_nid = NID_aes_256_cbc;
517
8
      break;
518
    case kKeyVariantAES_KW_128:
519
24
      UseDefaultIV(params);
520
24
      cipher_nid = NID_id_aes128_wrap;
521
24
      break;
522
    case kKeyVariantAES_KW_192:
523
      UseDefaultIV(params);
524
      cipher_nid = NID_id_aes192_wrap;
525
      break;
526
    case kKeyVariantAES_KW_256:
527
      UseDefaultIV(params);
528
      cipher_nid = NID_id_aes256_wrap;
529
      break;
530
    case kKeyVariantAES_GCM_128:
531

408
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
532

306
          !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
533
204
          !ValidateAdditionalData(env, mode, args[offset + 3], params)) {
534
        return Nothing<bool>();
535
      }
536
102
      cipher_nid = NID_aes_128_gcm;
537
102
      break;
538
    case kKeyVariantAES_GCM_192:
539
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
540
          !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
541
          !ValidateAdditionalData(env, mode, args[offset + 3], params)) {
542
        return Nothing<bool>();
543
      }
544
      cipher_nid = NID_aes_192_gcm;
545
      break;
546
    case kKeyVariantAES_GCM_256:
547

184
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
548

138
          !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
549
92
          !ValidateAdditionalData(env, mode, args[offset + 3], params)) {
550
        return Nothing<bool>();
551
      }
552
46
      cipher_nid = NID_aes_256_gcm;
553
46
      break;
554
    default:
555
      UNREACHABLE();
556
  }
557
558
314
  params->cipher = EVP_get_cipherbynid(cipher_nid);
559
314
  CHECK_NOT_NULL(params->cipher);
560
561
628
  if (params->iv.size() <
562
314
      static_cast<size_t>(EVP_CIPHER_iv_length(params->cipher))) {
563
    THROW_ERR_CRYPTO_INVALID_IV(env);
564
    return Nothing<bool>();
565
  }
566
567
314
  return Just(true);
568
}
569
570
314
WebCryptoCipherStatus AESCipherTraits::DoCipher(
571
    Environment* env,
572
    std::shared_ptr<KeyObjectData> key_data,
573
    WebCryptoCipherMode cipher_mode,
574
    const AESCipherConfig& params,
575
    const ByteSource& in,
576
    ByteSource* out) {
577
#define V(name, fn)                                                           \
578
  case kKeyVariantAES_ ## name:                                               \
579
    return fn(env, key_data.get(), cipher_mode, params, in, out);
580



314
  switch (params.variant) {
581
63
    VARIANTS(V)
582
    default:
583
      UNREACHABLE();
584
  }
585
8
#undef V
586
102
}
587
588
4211
void AES::Initialize(Environment* env, Local<Object> target) {
589
4165
  AESCryptoJob::Initialize(env, target);
590
591
#define V(name, _) NODE_DEFINE_CONSTANT(target, kKeyVariantAES_ ## name);
592
99960
  VARIANTS(V)
593
#undef V
594
12495
}
595
4165
596
8330
}  // namespace crypto
597

18685
}  // namespace node