GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_aes.cc Lines: 215 310 69.4 %
Date: 2022-06-06 04:15:48 Branches: 132 238 55.5 %

Line Branch Exec Source
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
801
WebCryptoCipherStatus AES_Cipher(
35
    Environment* env,
36
    KeyObjectData* key_data,
37
    WebCryptoCipherMode cipher_mode,
38
    const AESCipherConfig& params,
39
    const ByteSource& in,
40
    ByteSource* out) {
41
801
  CHECK_NOT_NULL(key_data);
42
801
  CHECK_EQ(key_data->GetKeyType(), kKeyTypeSecret);
43
44
801
  const int mode = EVP_CIPHER_mode(params.cipher);
45
46
1602
  CipherCtxPointer ctx(EVP_CIPHER_CTX_new());
47
801
  EVP_CIPHER_CTX_init(ctx.get());
48
801
  if (mode == EVP_CIPH_WRAP_MODE)
49
76
    EVP_CIPHER_CTX_set_flags(ctx.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
50
51
801
  const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt;
52
53
801
  if (!EVP_CipherInit_ex(
54
          ctx.get(),
55
801
          params.cipher,
56
          nullptr,
57
          nullptr,
58
          nullptr,
59
          encrypt)) {
60
    // Cipher init failed
61
    return WebCryptoCipherStatus::FAILED;
62
  }
63
64

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

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

1275
      params.additional_data.size() &&
119
318
      !EVP_CipherUpdate(
120
            ctx.get(),
121
            nullptr,
122
            &out_len,
123
            params.additional_data.data<unsigned char>(),
124
318
            params.additional_data.size())) {
125
    return WebCryptoCipherStatus::FAILED;
126
  }
127
128
801
  char* data = MallocOpenSSL<char>(buf_len);
129
1602
  ByteSource buf = ByteSource::Allocated(data, buf_len);
130
801
  unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
131
132
  // In some outdated version of OpenSSL (e.g.
133
  // ubi81_sharedlibs_openssl111fips_x64) may be used in sharedlib mode, the
134
  // logic will be failed when input size is zero. The newly OpenSSL has fixed
135
  // it up. But we still have to regard zero as special in Node.js code to
136
  // prevent old OpenSSL failure.
137
  //
138
  // Refs: https://github.com/openssl/openssl/commit/420cb707b880e4fb649094241371701013eeb15f
139
  // Refs: https://github.com/nodejs/node/pull/38913#issuecomment-866505244
140
801
  if (in.size() == 0) {
141
2
    out_len = 0;
142
799
  } else if (!EVP_CipherUpdate(
143
          ctx.get(),
144
          ptr,
145
          &out_len,
146
          in.data<unsigned char>(),
147
799
          in.size())) {
148
    return WebCryptoCipherStatus::FAILED;
149
  }
150
151
801
  total += out_len;
152
801
  CHECK_LE(out_len, buf_len);
153
801
  ptr += out_len;
154
801
  out_len = EVP_CIPHER_CTX_block_size(ctx.get());
155
801
  if (!EVP_CipherFinal_ex(ctx.get(), ptr, &out_len)) {
156
15
    return WebCryptoCipherStatus::FAILED;
157
  }
158
786
  total += out_len;
159
160
  // If using AES_GCM, grab the generated auth tag and append
161
  // it to the end of the ciphertext.
162

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

472
      params->length == 0 ||
378
236
      params->length > 128) {
379
    THROW_ERR_CRYPTO_INVALID_COUNTER(env);
380
    return false;
381
  }
382
236
  return true;
383
}
384
385
474
bool ValidateAuthTag(
386
    Environment* env,
387
    CryptoJobMode mode,
388
    WebCryptoCipherMode cipher_mode,
389
    Local<Value> value,
390
    AESCipherConfig* params) {
391
474
  switch (cipher_mode) {
392
228
    case kWebCryptoCipherDecrypt: {
393
228
      if (!IsAnyByteSource(value)) {
394
        THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
395
        return false;
396
      }
397
228
      ArrayBufferOrViewContents<char> tag_contents(value);
398
228
      if (UNLIKELY(!tag_contents.CheckSizeInt32())) {
399
        THROW_ERR_OUT_OF_RANGE(env, "tagLength is too big");
400
        return false;
401
      }
402
      params->tag = mode == kCryptoJobAsync
403
456
          ? tag_contents.ToCopy()
404
228
          : tag_contents.ToByteSource();
405
228
      break;
406
    }
407
246
    case kWebCryptoCipherEncrypt: {
408
246
      if (!value->IsUint32()) {
409
        THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
410
        return false;
411
      }
412
246
      params->length = value.As<Uint32>()->Value();
413
246
      if (params->length > 128) {
414
        THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
415
        return false;
416
      }
417
246
      break;
418
    }
419
    default:
420
      UNREACHABLE();
421
  }
422
474
  return true;
423
}
424
425
474
bool ValidateAdditionalData(
426
    Environment* env,
427
    CryptoJobMode mode,
428
    Local<Value> value,
429
    AESCipherConfig* params) {
430
  // Additional Data
431
474
  if (IsAnyByteSource(value)) {
432
318
    ArrayBufferOrViewContents<char> additional(value);
433
318
    if (UNLIKELY(!additional.CheckSizeInt32())) {
434
      THROW_ERR_OUT_OF_RANGE(env, "additionalData is too big");
435
      return false;
436
    }
437
    params->additional_data = mode == kCryptoJobAsync
438
636
        ? additional.ToCopy()
439
318
        : additional.ToByteSource();
440
  }
441
474
  return true;
442
}
443
444
76
void UseDefaultIV(AESCipherConfig* params) {
445
76
  params->iv = ByteSource::Foreign(kDefaultWrapIV, strlen(kDefaultWrapIV));
446
76
}
447
}  // namespace
448
449
1037
AESCipherConfig::AESCipherConfig(AESCipherConfig&& other) noexcept
450
1037
    : mode(other.mode),
451
1037
      variant(other.variant),
452
1037
      cipher(other.cipher),
453
1037
      length(other.length),
454
1037
      iv(std::move(other.iv)),
455
1037
      additional_data(std::move(other.additional_data)),
456
1037
      tag(std::move(other.tag)) {}
457
458
AESCipherConfig& AESCipherConfig::operator=(AESCipherConfig&& other) noexcept {
459
  if (&other == this) return *this;
460
  this->~AESCipherConfig();
461
  return *new (this) AESCipherConfig(std::move(other));
462
}
463
464
void AESCipherConfig::MemoryInfo(MemoryTracker* tracker) const {
465
  // If mode is sync, then the data in each of these properties
466
  // is not owned by the AESCipherConfig, so we ignore it.
467
  if (mode == kCryptoJobAsync) {
468
    tracker->TrackFieldWithSize("iv", iv.size());
469
    tracker->TrackFieldWithSize("additional_data", additional_data.size());
470
    tracker->TrackFieldWithSize("tag", tag.size());
471
  }
472
}
473
474
1037
Maybe<bool> AESCipherTraits::AdditionalConfig(
475
    CryptoJobMode mode,
476
    const FunctionCallbackInfo<Value>& args,
477
    unsigned int offset,
478
    WebCryptoCipherMode cipher_mode,
479
    AESCipherConfig* params) {
480
1037
  Environment* env = Environment::GetCurrent(args);
481
482
1037
  params->mode = mode;
483
484

2074
  CHECK(args[offset]->IsUint32());  // Key Variant
485
1037
  params->variant =
486
3111
      static_cast<AESKeyVariant>(args[offset].As<Uint32>()->Value());
487
488
  int cipher_nid;
489
490



1037
  switch (params->variant) {
491
223
    case kKeyVariantAES_CTR_128:
492

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

446
          !ValidateCounter(env, args[offset + 2], params)) {
494
        return Nothing<bool>();
495
      }
496
223
      cipher_nid = NID_aes_128_ctr;
497
223
      break;
498
4
    case kKeyVariantAES_CTR_192:
499

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

8
          !ValidateCounter(env, args[offset + 2], params)) {
501
        return Nothing<bool>();
502
      }
503
4
      cipher_nid = NID_aes_192_ctr;
504
4
      break;
505
9
    case kKeyVariantAES_CTR_256:
506

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

18
          !ValidateCounter(env, args[offset + 2], params)) {
508
        return Nothing<bool>();
509
      }
510
9
      cipher_nid = NID_aes_256_ctr;
511
9
      break;
512
229
    case kKeyVariantAES_CBC_128:
513

458
      if (!ValidateIV(env, mode, args[offset + 1], params))
514
        return Nothing<bool>();
515
229
      cipher_nid = NID_aes_128_cbc;
516
229
      break;
517
7
    case kKeyVariantAES_CBC_192:
518

14
      if (!ValidateIV(env, mode, args[offset + 1], params))
519
        return Nothing<bool>();
520
7
      cipher_nid = NID_aes_192_cbc;
521
7
      break;
522
15
    case kKeyVariantAES_CBC_256:
523

30
      if (!ValidateIV(env, mode, args[offset + 1], params))
524
        return Nothing<bool>();
525
15
      cipher_nid = NID_aes_256_cbc;
526
15
      break;
527
76
    case kKeyVariantAES_KW_128:
528
76
      UseDefaultIV(params);
529
76
      cipher_nid = NID_id_aes128_wrap;
530
76
      break;
531
    case kKeyVariantAES_KW_192:
532
      UseDefaultIV(params);
533
      cipher_nid = NID_id_aes192_wrap;
534
      break;
535
    case kKeyVariantAES_KW_256:
536
      UseDefaultIV(params);
537
      cipher_nid = NID_id_aes256_wrap;
538
      break;
539
314
    case kKeyVariantAES_GCM_128:
540
628
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
541


942
          !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
542

628
          !ValidateAdditionalData(env, mode, args[offset + 3], params)) {
543
        return Nothing<bool>();
544
      }
545
314
      cipher_nid = NID_aes_128_gcm;
546
314
      break;
547
56
    case kKeyVariantAES_GCM_192:
548
112
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
549


168
          !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
550

112
          !ValidateAdditionalData(env, mode, args[offset + 3], params)) {
551
        return Nothing<bool>();
552
      }
553
56
      cipher_nid = NID_aes_192_gcm;
554
56
      break;
555
104
    case kKeyVariantAES_GCM_256:
556
208
      if (!ValidateIV(env, mode, args[offset + 1], params) ||
557


312
          !ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
558

208
          !ValidateAdditionalData(env, mode, args[offset + 3], params)) {
559
        return Nothing<bool>();
560
      }
561
104
      cipher_nid = NID_aes_256_gcm;
562
104
      break;
563
    default:
564
      UNREACHABLE();
565
  }
566
567
1037
  params->cipher = EVP_get_cipherbynid(cipher_nid);
568
1037
  CHECK_NOT_NULL(params->cipher);
569
570
1037
  if (params->iv.size() <
571
1037
      static_cast<size_t>(EVP_CIPHER_iv_length(params->cipher))) {
572
    THROW_ERR_CRYPTO_INVALID_IV(env);
573
    return Nothing<bool>();
574
  }
575
576
1037
  return Just(true);
577
}
578
579
1037
WebCryptoCipherStatus AESCipherTraits::DoCipher(
580
    Environment* env,
581
    std::shared_ptr<KeyObjectData> key_data,
582
    WebCryptoCipherMode cipher_mode,
583
    const AESCipherConfig& params,
584
    const ByteSource& in,
585
    ByteSource* out) {
586
#define V(name, fn)                                                           \
587
  case kKeyVariantAES_ ## name:                                               \
588
    return fn(env, key_data.get(), cipher_mode, params, in, out);
589



1037
  switch (params.variant) {
590
1037
    VARIANTS(V)
591
    default:
592
      UNREACHABLE();
593
  }
594
#undef V
595
}
596
597
854
void AES::Initialize(Environment* env, Local<Object> target) {
598
854
  AESCryptoJob::Initialize(env, target);
599
600
#define V(name, _) NODE_DEFINE_CONSTANT(target, kKeyVariantAES_ ## name);
601
20496
  VARIANTS(V)
602
#undef V
603
854
}
604
605
5206
void AES::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
606
5206
  AESCryptoJob::RegisterExternalReferences(registry);
607
5206
}
608
609
}  // namespace crypto
610
}  // namespace node