GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_dh.cc Lines: 312 390 80.0 %
Date: 2022-07-29 04:16:17 Branches: 132 257 51.4 %

Line Branch Exec Source
1
#include "crypto/crypto_dh.h"
2
#include "async_wrap-inl.h"
3
#include "base_object-inl.h"
4
#include "crypto/crypto_keys.h"
5
#include "env-inl.h"
6
#include "memory_tracker-inl.h"
7
#include "threadpoolwork-inl.h"
8
#include "v8.h"
9
10
#include <variant>
11
12
namespace node {
13
14
using v8::ArrayBuffer;
15
using v8::BackingStore;
16
using v8::ConstructorBehavior;
17
using v8::DontDelete;
18
using v8::FunctionCallback;
19
using v8::FunctionCallbackInfo;
20
using v8::FunctionTemplate;
21
using v8::HandleScope;
22
using v8::Int32;
23
using v8::Just;
24
using v8::Local;
25
using v8::Maybe;
26
using v8::Nothing;
27
using v8::Object;
28
using v8::PropertyAttribute;
29
using v8::ReadOnly;
30
using v8::SideEffectType;
31
using v8::Signature;
32
using v8::String;
33
using v8::Value;
34
35
namespace crypto {
36
namespace {
37
58
void ZeroPadDiffieHellmanSecret(size_t remainder_size,
38
                                char* data,
39
                                size_t length) {
40
  // DH_size returns number of bytes in a prime number.
41
  // DH_compute_key returns number of bytes in a remainder of exponent, which
42
  // may have less bytes than a prime number. Therefore add 0-padding to the
43
  // allocated buffer.
44
58
  const size_t prime_size = length;
45
58
  if (remainder_size != prime_size) {
46
2
    CHECK_LT(remainder_size, prime_size);
47
2
    const size_t padding = prime_size - remainder_size;
48
2
    memmove(data + padding, data, remainder_size);
49
2
    memset(data, 0, padding);
50
  }
51
58
}
52
}  // namespace
53
54
62
DiffieHellman::DiffieHellman(Environment* env, Local<Object> wrap)
55
62
    : BaseObject(env, wrap), verifyError_(0) {
56
62
  MakeWeak();
57
62
}
58
59
1305
void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
60
2610
  auto make = [&] (Local<String> name, FunctionCallback callback) {
61
5220
    Local<FunctionTemplate> t = env->NewFunctionTemplate(callback);
62
63
2610
    const PropertyAttribute attributes =
64
        static_cast<PropertyAttribute>(ReadOnly | DontDelete);
65
66
5220
    t->InstanceTemplate()->SetInternalFieldCount(
67
        DiffieHellman::kInternalFieldCount);
68
2610
    t->Inherit(BaseObject::GetConstructorTemplate(env));
69
70
2610
    env->SetProtoMethod(t, "generateKeys", GenerateKeys);
71
2610
    env->SetProtoMethod(t, "computeSecret", ComputeSecret);
72
2610
    env->SetProtoMethodNoSideEffect(t, "getPrime", GetPrime);
73
2610
    env->SetProtoMethodNoSideEffect(t, "getGenerator", GetGenerator);
74
2610
    env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey);
75
2610
    env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey);
76
2610
    env->SetProtoMethod(t, "setPublicKey", SetPublicKey);
77
2610
    env->SetProtoMethod(t, "setPrivateKey", SetPrivateKey);
78
79
    Local<FunctionTemplate> verify_error_getter_templ =
80
        FunctionTemplate::New(env->isolate(),
81
                              DiffieHellman::VerifyErrorGetter,
82
                              Local<Value>(),
83
                              Signature::New(env->isolate(), t),
84
                              /* length */ 0,
85
                              ConstructorBehavior::kThrow,
86
2610
                              SideEffectType::kHasNoSideEffect);
87
88
10440
    t->InstanceTemplate()->SetAccessorProperty(
89
        env->verify_error_string(),
90
        verify_error_getter_templ,
91
        Local<FunctionTemplate>(),
92
        attributes);
93
94
2610
    env->SetConstructorFunction(target, name, t);
95
3915
  };
96
97
1305
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellman"), New);
98
1305
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
99
       DiffieHellmanGroup);
100
101
1305
  env->SetMethodNoSideEffect(target, "statelessDH", DiffieHellman::Stateless);
102
1305
  DHKeyPairGenJob::Initialize(env, target);
103
1305
  DHKeyExportJob::Initialize(env, target);
104
1305
  DHBitsJob::Initialize(env, target);
105
1305
}
106
107
5287
void DiffieHellman::RegisterExternalReferences(
108
    ExternalReferenceRegistry* registry) {
109
5287
  registry->Register(New);
110
5287
  registry->Register(DiffieHellmanGroup);
111
112
5287
  registry->Register(GenerateKeys);
113
5287
  registry->Register(ComputeSecret);
114
5287
  registry->Register(GetPrime);
115
5287
  registry->Register(GetGenerator);
116
5287
  registry->Register(GetPublicKey);
117
5287
  registry->Register(GetPrivateKey);
118
5287
  registry->Register(SetPublicKey);
119
5287
  registry->Register(SetPrivateKey);
120
121
5287
  registry->Register(DiffieHellman::VerifyErrorGetter);
122
5287
  registry->Register(DiffieHellman::Stateless);
123
124
5287
  DHKeyPairGenJob::RegisterExternalReferences(registry);
125
5287
  DHKeyExportJob::RegisterExternalReferences(registry);
126
5287
  DHBitsJob::RegisterExternalReferences(registry);
127
5287
}
128
129
10
bool DiffieHellman::Init(int primeLength, int g) {
130
10
  dh_.reset(DH_new());
131
10
  if (!DH_generate_parameters_ex(dh_.get(), primeLength, g, nullptr))
132
3
    return false;
133
7
  return VerifyContext();
134
}
135
136
void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
137
  tracker->TrackFieldWithSize("dh", dh_ ? kSizeOf_DH : 0);
138
}
139
140
10
bool DiffieHellman::Init(BignumPointer&& bn_p, int g) {
141
10
  dh_.reset(DH_new());
142
10
  CHECK_GE(g, 2);
143
10
  BignumPointer bn_g(BN_new());
144

30
  return bn_g && BN_set_word(bn_g.get(), g) &&
145

30
         DH_set0_pqg(dh_.get(), bn_p.release(), nullptr, bn_g.release()) &&
146
20
         VerifyContext();
147
}
148
149
18
bool DiffieHellman::Init(const char* p, int p_len, int g) {
150
18
  dh_.reset(DH_new());
151
18
  if (p_len <= 0) {
152
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
153
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
154
    return false;
155
  }
156
18
  if (g <= 1) {
157
4
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
158
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
159
4
    return false;
160
  }
161
  BIGNUM* bn_p =
162
14
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
163
14
  BIGNUM* bn_g = BN_new();
164

28
  if (!BN_set_word(bn_g, g) ||
165
14
      !DH_set0_pqg(dh_.get(), bn_p, nullptr, bn_g)) {
166
    BN_free(bn_p);
167
    BN_free(bn_g);
168
    return false;
169
  }
170
14
  return VerifyContext();
171
}
172
173
23
bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
174
23
  dh_.reset(DH_new());
175
23
  if (p_len <= 0) {
176
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
177
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
178
    return false;
179
  }
180
23
  if (g_len <= 0) {
181
2
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
182
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
183
2
    return false;
184
  }
185
  BIGNUM* bn_g =
186
21
      BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr);
187

21
  if (BN_is_zero(bn_g) || BN_is_one(bn_g)) {
188
4
    BN_free(bn_g);
189
4
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
190
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
191
4
    return false;
192
  }
193
  BIGNUM* bn_p =
194
17
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
195
17
  if (!DH_set0_pqg(dh_.get(), bn_p, nullptr, bn_g)) {
196
    BN_free(bn_p);
197
    BN_free(bn_g);
198
    return false;
199
  }
200
17
  return VerifyContext();
201
}
202
203
constexpr int kStandardizedGenerator = 2;
204
205
template <BIGNUM* (*p)(BIGNUM*)>
206
30
BignumPointer InstantiateStandardizedGroup() {
207
30
  return BignumPointer(p(nullptr));
208
}
209
210
typedef BignumPointer (*StandardizedGroupInstantiator)();
211
212
// Returns a function that can be used to create an instance of a standardized
213
// Diffie-Hellman group. The generator is always kStandardizedGenerator.
214
17
inline StandardizedGroupInstantiator FindDiffieHellmanGroup(const char* name) {
215
#define V(n, p)                                                                \
216
  if (StringEqualNoCase(name, n)) return InstantiateStandardizedGroup<p>
217
17
  V("modp1", BN_get_rfc2409_prime_768);
218
15
  V("modp2", BN_get_rfc2409_prime_1024);
219
13
  V("modp5", BN_get_rfc3526_prime_1536);
220
4
  V("modp14", BN_get_rfc3526_prime_2048);
221
3
  V("modp15", BN_get_rfc3526_prime_3072);
222
3
  V("modp16", BN_get_rfc3526_prime_4096);
223
3
  V("modp17", BN_get_rfc3526_prime_6144);
224
3
  V("modp18", BN_get_rfc3526_prime_8192);
225
#undef V
226
2
  return nullptr;
227
}
228
229
11
void DiffieHellman::DiffieHellmanGroup(
230
    const FunctionCallbackInfo<Value>& args) {
231
11
  Environment* env = Environment::GetCurrent(args);
232
11
  DiffieHellman* diffieHellman = new DiffieHellman(env, args.This());
233
234
11
  CHECK_EQ(args.Length(), 1);
235
23
  THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Group name");
236
237
11
  bool initialized = false;
238
239
11
  const node::Utf8Value group_name(env->isolate(), args[0]);
240
11
  auto group = FindDiffieHellmanGroup(*group_name);
241
11
  if (group == nullptr)
242
1
    return THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
243
244
10
  initialized = diffieHellman->Init(group(), kStandardizedGenerator);
245
10
  if (!initialized)
246
    THROW_ERR_CRYPTO_INITIALIZATION_FAILED(env);
247
}
248
249
250
51
void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
251
51
  Environment* env = Environment::GetCurrent(args);
252
  DiffieHellman* diffieHellman =
253
51
      new DiffieHellman(env, args.This());
254
51
  bool initialized = false;
255
256
51
  if (args.Length() == 2) {
257
51
    if (args[0]->IsInt32()) {
258
10
      if (args[1]->IsInt32()) {
259
30
        initialized = diffieHellman->Init(args[0].As<Int32>()->Value(),
260
30
                                          args[1].As<Int32>()->Value());
261
      }
262
    } else {
263
41
      ArrayBufferOrViewContents<char> arg0(args[0]);
264
41
      if (UNLIKELY(!arg0.CheckSizeInt32()))
265
        return THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
266
41
      if (args[1]->IsInt32()) {
267
36
        initialized = diffieHellman->Init(arg0.data(),
268
18
                                          arg0.size(),
269
54
                                          args[1].As<Int32>()->Value());
270
      } else {
271
23
        ArrayBufferOrViewContents<char> arg1(args[1]);
272
23
        if (UNLIKELY(!arg1.CheckSizeInt32()))
273
          return THROW_ERR_OUT_OF_RANGE(env, "generator is too big");
274
23
        initialized = diffieHellman->Init(arg0.data(), arg0.size(),
275
23
                                          arg1.data(), arg1.size());
276
      }
277
    }
278
  }
279
280
51
  if (!initialized) {
281
13
    return ThrowCryptoError(env, ERR_get_error(), "Initialization failed");
282
  }
283
}
284
285
286
40
void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
287
40
  Environment* env = Environment::GetCurrent(args);
288
289
  DiffieHellman* diffieHellman;
290
40
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
291
292
40
  if (!DH_generate_key(diffieHellman->dh_.get())) {
293
    return ThrowCryptoError(env, ERR_get_error(), "Key generation failed");
294
  }
295
296
  const BIGNUM* pub_key;
297
40
  DH_get0_key(diffieHellman->dh_.get(), &pub_key, nullptr);
298
299
40
  std::unique_ptr<BackingStore> bs;
300
  {
301
40
    const int size = BN_num_bytes(pub_key);
302
40
    CHECK_GE(size, 0);
303
40
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
304
40
    bs = ArrayBuffer::NewBackingStore(env->isolate(), size);
305
  }
306
307
40
  CHECK_EQ(static_cast<int>(bs->ByteLength()),
308
           BN_bn2binpad(pub_key,
309
                        static_cast<unsigned char*>(bs->Data()),
310
                        bs->ByteLength()));
311
312
40
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
313
  Local<Value> buffer;
314
80
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
315
80
  args.GetReturnValue().Set(buffer);
316
}
317
318
319
67
void DiffieHellman::GetField(const FunctionCallbackInfo<Value>& args,
320
                             const BIGNUM* (*get_field)(const DH*),
321
                             const char* err_if_null) {
322
67
  Environment* env = Environment::GetCurrent(args);
323
324
  DiffieHellman* dh;
325
67
  ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder());
326
327
67
  const BIGNUM* num = get_field(dh->dh_.get());
328
67
  if (num == nullptr)
329
    return THROW_ERR_CRYPTO_INVALID_STATE(env, err_if_null);
330
331
67
  std::unique_ptr<BackingStore> bs;
332
  {
333
67
    const int size = BN_num_bytes(num);
334
67
    CHECK_GE(size, 0);
335
67
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
336
67
    bs = ArrayBuffer::NewBackingStore(env->isolate(), size);
337
  }
338
339
67
  CHECK_EQ(static_cast<int>(bs->ByteLength()),
340
           BN_bn2binpad(num,
341
                        static_cast<unsigned char*>(bs->Data()),
342
                        bs->ByteLength()));
343
344
67
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
345
  Local<Value> buffer;
346
134
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
347
134
  args.GetReturnValue().Set(buffer);
348
}
349
350
13
void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) {
351
13
  GetField(args, [](const DH* dh) -> const BIGNUM* {
352
    const BIGNUM* p;
353
13
    DH_get0_pqg(dh, &p, nullptr, nullptr);
354
13
    return p;
355
  }, "p is null");
356
13
}
357
358
7
void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) {
359
7
  GetField(args, [](const DH* dh) -> const BIGNUM* {
360
    const BIGNUM* g;
361
7
    DH_get0_pqg(dh, nullptr, nullptr, &g);
362
7
    return g;
363
  }, "g is null");
364
7
}
365
366
38
void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
367
38
  GetField(args, [](const DH* dh) -> const BIGNUM* {
368
    const BIGNUM* pub_key;
369
38
    DH_get0_key(dh, &pub_key, nullptr);
370
38
    return pub_key;
371
  }, "No public key - did you forget to generate one?");
372
38
}
373
374
9
void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
375
9
  GetField(args, [](const DH* dh) -> const BIGNUM* {
376
    const BIGNUM* priv_key;
377
9
    DH_get0_key(dh, nullptr, &priv_key);
378
9
    return priv_key;
379
  }, "No private key - did you forget to generate one?");
380
9
}
381
382
46
void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
383
46
  Environment* env = Environment::GetCurrent(args);
384
385
  DiffieHellman* diffieHellman;
386
47
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
387
388
  ClearErrorOnReturn clear_error_on_return;
389
390
46
  CHECK_EQ(args.Length(), 1);
391
46
  ArrayBufferOrViewContents<unsigned char> key_buf(args[0]);
392
46
  if (UNLIKELY(!key_buf.CheckSizeInt32()))
393
    return THROW_ERR_OUT_OF_RANGE(env, "secret is too big");
394
46
  BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.size(), nullptr));
395
396
46
  std::unique_ptr<BackingStore> bs;
397
  {
398
46
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
399
92
    bs = ArrayBuffer::NewBackingStore(env->isolate(),
400
92
                                      DH_size(diffieHellman->dh_.get()));
401
  }
402
403
46
  int size = DH_compute_key(static_cast<unsigned char*>(bs->Data()),
404
46
                            key.get(),
405
46
                            diffieHellman->dh_.get());
406
407
46
  if (size == -1) {
408
    int checkResult;
409
    int checked;
410
411
1
    checked = DH_check_pub_key(diffieHellman->dh_.get(),
412
1
                               key.get(),
413
                               &checkResult);
414
415
1
    if (!checked) {
416
1
      return ThrowCryptoError(env, ERR_get_error(), "Invalid Key");
417
    } else if (checkResult) {
418
      if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
419
        return THROW_ERR_CRYPTO_INVALID_KEYLEN(env,
420
            "Supplied key is too small");
421
      } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
422
        return THROW_ERR_CRYPTO_INVALID_KEYLEN(env,
423
            "Supplied key is too large");
424
      }
425
    }
426
427
    return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
428
  }
429
430
45
  CHECK_GE(size, 0);
431
90
  ZeroPadDiffieHellmanSecret(size,
432
45
                             static_cast<char*>(bs->Data()),
433
                             bs->ByteLength());
434
435
45
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
436
  Local<Value> buffer;
437
90
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
438
90
  args.GetReturnValue().Set(buffer);
439
}
440
441
100009
void DiffieHellman::SetKey(const FunctionCallbackInfo<Value>& args,
442
                           int (*set_field)(DH*, BIGNUM*), const char* what) {
443
100009
  Environment* env = Environment::GetCurrent(args);
444
  DiffieHellman* dh;
445
100009
  ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder());
446
100009
  CHECK_EQ(args.Length(), 1);
447
100009
  ArrayBufferOrViewContents<unsigned char> buf(args[0]);
448
100009
  if (UNLIKELY(!buf.CheckSizeInt32()))
449
    return THROW_ERR_OUT_OF_RANGE(env, "buf is too big");
450
100009
  BIGNUM* num = BN_bin2bn(buf.data(), buf.size(), nullptr);
451
100009
  CHECK_NOT_NULL(num);
452
100009
  CHECK_EQ(1, set_field(dh->dh_.get(), num));
453
}
454
455
50003
void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
456
50003
  SetKey(args,
457
50003
         [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, num, nullptr); },
458
         "Public key");
459
50003
}
460
461
50006
void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
462
50006
  SetKey(args,
463
50006
         [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, nullptr, num); },
464
         "Private key");
465
50006
}
466
467
48
void DiffieHellman::VerifyErrorGetter(const FunctionCallbackInfo<Value>& args) {
468
48
  HandleScope scope(args.GetIsolate());
469
470
  DiffieHellman* diffieHellman;
471
48
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
472
473
96
  args.GetReturnValue().Set(diffieHellman->verifyError_);
474
}
475
476
48
bool DiffieHellman::VerifyContext() {
477
  int codes;
478
48
  if (!DH_check(dh_.get(), &codes))
479
    return false;
480
48
  verifyError_ = codes;
481
48
  return true;
482
}
483
484
// The input arguments to DhKeyPairGenJob can vary
485
//   1. CryptoJobMode
486
// and either
487
//   2. Group name (as a string)
488
// or
489
//   2. Prime or Prime Length
490
//   3. Generator
491
// Followed by the public and private key encoding parameters:
492
//   * Public format
493
//   * Public type
494
//   * Private format
495
//   * Private type
496
//   * Cipher
497
//   * Passphrase
498
8
Maybe<bool> DhKeyGenTraits::AdditionalConfig(
499
    CryptoJobMode mode,
500
    const FunctionCallbackInfo<Value>& args,
501
    unsigned int* offset,
502
    DhKeyPairGenConfig* params) {
503
8
  Environment* env = Environment::GetCurrent(args);
504
505

24
  if (args[*offset]->IsString()) {
506
12
    Utf8Value group_name(env->isolate(), args[*offset]);
507
6
    auto group = FindDiffieHellmanGroup(*group_name);
508
6
    if (group == nullptr) {
509
1
      THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
510
1
      return Nothing<bool>();
511
    }
512
513
5
    params->params.prime = group();
514
5
    params->params.generator = kStandardizedGenerator;
515
5
    *offset += 1;
516
  } else {
517

4
    if (args[*offset]->IsInt32()) {
518
3
      int size = args[*offset].As<Int32>()->Value();
519
1
      if (size < 0) {
520
        THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size");
521
        return Nothing<bool>();
522
      }
523
1
      params->params.prime = size;
524
    } else {
525
2
      ArrayBufferOrViewContents<unsigned char> input(args[*offset]);
526
1
      if (UNLIKELY(!input.CheckSizeInt32())) {
527
        THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
528
        return Nothing<bool>();
529
      }
530
2
      params->params.prime = BignumPointer(
531
2
          BN_bin2bn(input.data(), input.size(), nullptr));
532
    }
533
534

4
    CHECK(args[*offset + 1]->IsInt32());
535
6
    params->params.generator = args[*offset + 1].As<Int32>()->Value();
536
2
    *offset += 2;
537
  }
538
539
7
  return Just(true);
540
}
541
542
7
EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) {
543
7
  EVPKeyPointer key_params;
544
7
  if (BignumPointer* prime_fixed_value =
545
7
          std::get_if<BignumPointer>(&params->params.prime)) {
546
6
    DHPointer dh(DH_new());
547
6
    if (!dh)
548
      return EVPKeyCtxPointer();
549
550
6
    BIGNUM* prime = prime_fixed_value->get();
551
6
    BignumPointer bn_g(BN_new());
552

12
    if (!BN_set_word(bn_g.get(), params->params.generator) ||
553
6
        !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get())) {
554
      return EVPKeyCtxPointer();
555
    }
556
557
6
    prime_fixed_value->release();
558
6
    bn_g.release();
559
560
6
    key_params = EVPKeyPointer(EVP_PKEY_new());
561
6
    CHECK(key_params);
562
6
    CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1);
563
1
  } else if (int* prime_size = std::get_if<int>(&params->params.prime)) {
564
1
    EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr));
565
1
    EVP_PKEY* raw_params = nullptr;
566
2
    if (!param_ctx ||
567
2
        EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
568
1
        EVP_PKEY_CTX_set_dh_paramgen_prime_len(
569
            param_ctx.get(),
570
1
            *prime_size) <= 0 ||
571
1
        EVP_PKEY_CTX_set_dh_paramgen_generator(
572
            param_ctx.get(),
573

3
            params->params.generator) <= 0 ||
574
1
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
575
      return EVPKeyCtxPointer();
576
    }
577
578
1
    key_params = EVPKeyPointer(raw_params);
579
  } else {
580
    UNREACHABLE();
581
  }
582
583
14
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
584

7
  if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
585
    return EVPKeyCtxPointer();
586
587
7
  return ctx;
588
}
589
590
Maybe<bool> DHKeyExportTraits::AdditionalConfig(
591
    const FunctionCallbackInfo<Value>& args,
592
    unsigned int offset,
593
    DHKeyExportConfig* params) {
594
  return Just(true);
595
}
596
597
WebCryptoKeyExportStatus DHKeyExportTraits::DoExport(
598
    std::shared_ptr<KeyObjectData> key_data,
599
    WebCryptoKeyFormat format,
600
    const DHKeyExportConfig& params,
601
    ByteSource* out) {
602
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
603
604
  switch (format) {
605
    case kWebCryptoKeyFormatPKCS8:
606
      if (key_data->GetKeyType() != kKeyTypePrivate)
607
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
608
      return PKEY_PKCS8_Export(key_data.get(), out);
609
    case kWebCryptoKeyFormatSPKI:
610
      if (key_data->GetKeyType() != kKeyTypePublic)
611
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
612
      return PKEY_SPKI_Export(key_data.get(), out);
613
    default:
614
      UNREACHABLE();
615
  }
616
}
617
618
namespace {
619
15
ByteSource StatelessDiffieHellmanThreadsafe(
620
    const ManagedEVPPKey& our_key,
621
    const ManagedEVPPKey& their_key) {
622
  size_t out_size;
623
624
30
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr));
625
30
  if (!ctx ||
626
30
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
627

45
      EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 ||
628
13
      EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0)
629
2
    return ByteSource();
630
631
26
  ByteSource::Builder out(out_size);
632
13
  if (EVP_PKEY_derive(ctx.get(), out.data<unsigned char>(), &out_size) <= 0) {
633
    return ByteSource();
634
  }
635
636
13
  ZeroPadDiffieHellmanSecret(out_size, out.data<char>(), out.size());
637
13
  return std::move(out).release();
638
}
639
}  // namespace
640
641
15
void DiffieHellman::Stateless(const FunctionCallbackInfo<Value>& args) {
642
15
  Environment* env = Environment::GetCurrent(args);
643
644

30
  CHECK(args[0]->IsObject() && args[1]->IsObject());
645
  KeyObjectHandle* our_key_object;
646
32
  ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
647
15
  CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate);
648
  KeyObjectHandle* their_key_object;
649
30
  ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
650
15
  CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);
651
652
15
  ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
653
15
  ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();
654
655
  Local<Value> out;
656
30
  if (!StatelessDiffieHellmanThreadsafe(our_key, their_key)
657
15
          .ToBuffer(env)
658
15
              .ToLocal(&out)) return;
659
660
15
  if (Buffer::Length(out) == 0)
661
2
    return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
662
663
26
  args.GetReturnValue().Set(out);
664
}
665
666
Maybe<bool> DHBitsTraits::AdditionalConfig(
667
    CryptoJobMode mode,
668
    const FunctionCallbackInfo<Value>& args,
669
    unsigned int offset,
670
    DHBitsConfig* params) {
671
  Environment* env = Environment::GetCurrent(args);
672
673
  CHECK(args[offset]->IsObject());  // public key
674
  CHECK(args[offset + 1]->IsObject());  // private key
675
676
  KeyObjectHandle* private_key;
677
  KeyObjectHandle* public_key;
678
679
  ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<bool>());
680
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<bool>());
681
682
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
683
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
684
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
685
    return Nothing<bool>();
686
  }
687
688
  params->public_key = public_key->Data();
689
  params->private_key = private_key->Data();
690
691
  return Just(true);
692
}
693
694
Maybe<bool> DHBitsTraits::EncodeOutput(
695
    Environment* env,
696
    const DHBitsConfig& params,
697
    ByteSource* out,
698
    v8::Local<v8::Value>* result) {
699
  *result = out->ToArrayBuffer(env);
700
  return Just(!result->IsEmpty());
701
}
702
703
bool DHBitsTraits::DeriveBits(
704
    Environment* env,
705
    const DHBitsConfig& params,
706
    ByteSource* out) {
707
  *out = StatelessDiffieHellmanThreadsafe(
708
      params.private_key->GetAsymmetricKey(),
709
      params.public_key->GetAsymmetricKey());
710
  return true;
711
}
712
713
Maybe<bool> GetDhKeyDetail(
714
    Environment* env,
715
    std::shared_ptr<KeyObjectData> key,
716
    Local<Object> target) {
717
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
718
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_DH);
719
  return Just(true);
720
}
721
722
}  // namespace crypto
723
}  // namespace node