GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_dh.cc Lines: 327 382 85.6 %
Date: 2022-05-21 04:15:56 Branches: 130 235 55.3 %

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

48
  if (!BN_set_word(bn_g, g) ||
157
24
      !DH_set0_pqg(dh_.get(), bn_p, nullptr, bn_g)) {
158
    BN_free(bn_p);
159
    BN_free(bn_g);
160
    return false;
161
  }
162
24
  return VerifyContext();
163
}
164
165
21
bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
166
21
  dh_.reset(DH_new());
167
21
  if (p_len <= 0) {
168
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
169
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
170
    return false;
171
  }
172
21
  if (g_len <= 0) {
173
2
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
174
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
175
2
    return false;
176
  }
177
  BIGNUM* bn_g =
178
19
      BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr);
179

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

27
  if (args[*offset]->IsString()) {
482
14
    Utf8Value group_name(env->isolate(), args[*offset]);
483
7
    const modp_group* group = FindDiffieHellmanGroup(*group_name);
484
7
    if (group == nullptr) {
485
1
      THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
486
1
      return Nothing<bool>();
487
    }
488
489
12
    params->params.prime = BignumPointer(
490
6
        BN_bin2bn(reinterpret_cast<const unsigned char*>(group->prime),
491
12
                  group->prime_size, nullptr));
492
6
    params->params.generator = group->gen;
493
6
    *offset += 1;
494
  } else {
495

4
    if (args[*offset]->IsInt32()) {
496
3
      int size = args[*offset].As<Int32>()->Value();
497
1
      if (size < 0) {
498
        THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size");
499
        return Nothing<bool>();
500
      }
501
1
      params->params.prime = size;
502
    } else {
503
2
      ArrayBufferOrViewContents<unsigned char> input(args[*offset]);
504
1
      if (UNLIKELY(!input.CheckSizeInt32())) {
505
        THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
506
        return Nothing<bool>();
507
      }
508
2
      params->params.prime = BignumPointer(
509
2
          BN_bin2bn(input.data(), input.size(), nullptr));
510
    }
511
512

4
    CHECK(args[*offset + 1]->IsInt32());
513
6
    params->params.generator = args[*offset + 1].As<Int32>()->Value();
514
2
    *offset += 2;
515
  }
516
517
8
  return Just(true);
518
}
519
520
8
EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) {
521
8
  EVPKeyPointer key_params;
522
8
  if (BignumPointer* prime_fixed_value =
523
8
          std::get_if<BignumPointer>(&params->params.prime)) {
524
7
    DHPointer dh(DH_new());
525
7
    if (!dh)
526
      return EVPKeyCtxPointer();
527
528
7
    BIGNUM* prime = prime_fixed_value->get();
529
7
    BignumPointer bn_g(BN_new());
530

14
    if (!BN_set_word(bn_g.get(), params->params.generator) ||
531
7
        !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get())) {
532
      return EVPKeyCtxPointer();
533
    }
534
535
7
    prime_fixed_value->release();
536
7
    bn_g.release();
537
538
7
    key_params = EVPKeyPointer(EVP_PKEY_new());
539
7
    CHECK(key_params);
540
7
    CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1);
541
1
  } else if (int* prime_size = std::get_if<int>(&params->params.prime)) {
542
1
    EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr));
543
1
    EVP_PKEY* raw_params = nullptr;
544
2
    if (!param_ctx ||
545
2
        EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
546
1
        EVP_PKEY_CTX_set_dh_paramgen_prime_len(
547
            param_ctx.get(),
548
1
            *prime_size) <= 0 ||
549
1
        EVP_PKEY_CTX_set_dh_paramgen_generator(
550
            param_ctx.get(),
551

3
            params->params.generator) <= 0 ||
552
1
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
553
      return EVPKeyCtxPointer();
554
    }
555
556
1
    key_params = EVPKeyPointer(raw_params);
557
  } else {
558
    UNREACHABLE();
559
  }
560
561
16
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
562

8
  if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
563
    return EVPKeyCtxPointer();
564
565
8
  return ctx;
566
}
567
568
Maybe<bool> DHKeyExportTraits::AdditionalConfig(
569
    const FunctionCallbackInfo<Value>& args,
570
    unsigned int offset,
571
    DHKeyExportConfig* params) {
572
  return Just(true);
573
}
574
575
WebCryptoKeyExportStatus DHKeyExportTraits::DoExport(
576
    std::shared_ptr<KeyObjectData> key_data,
577
    WebCryptoKeyFormat format,
578
    const DHKeyExportConfig& params,
579
    ByteSource* out) {
580
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
581
582
  switch (format) {
583
    case kWebCryptoKeyFormatPKCS8:
584
      if (key_data->GetKeyType() != kKeyTypePrivate)
585
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
586
      return PKEY_PKCS8_Export(key_data.get(), out);
587
    case kWebCryptoKeyFormatSPKI:
588
      if (key_data->GetKeyType() != kKeyTypePublic)
589
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
590
      return PKEY_SPKI_Export(key_data.get(), out);
591
    default:
592
      UNREACHABLE();
593
  }
594
}
595
596
namespace {
597
20
ByteSource StatelessDiffieHellmanThreadsafe(
598
    const ManagedEVPPKey& our_key,
599
    const ManagedEVPPKey& their_key) {
600
  size_t out_size;
601
602
40
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr));
603
40
  if (!ctx ||
604
40
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
605

60
      EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 ||
606
18
      EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0)
607
2
    return ByteSource();
608
609
18
  char* buf = MallocOpenSSL<char>(out_size);
610
36
  ByteSource out = ByteSource::Allocated(buf, out_size);
611
612
18
  if (EVP_PKEY_derive(
613
          ctx.get(),
614
          reinterpret_cast<unsigned char*>(buf),
615
18
          &out_size) <= 0) {
616
    return ByteSource();
617
  }
618
619
18
  ZeroPadDiffieHellmanSecret(out_size, buf, out.size());
620
18
  return out;
621
}
622
}  // namespace
623
624
15
void DiffieHellman::Stateless(const FunctionCallbackInfo<Value>& args) {
625
15
  Environment* env = Environment::GetCurrent(args);
626
627

30
  CHECK(args[0]->IsObject() && args[1]->IsObject());
628
  KeyObjectHandle* our_key_object;
629
32
  ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
630
15
  CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate);
631
  KeyObjectHandle* their_key_object;
632
30
  ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
633
15
  CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);
634
635
15
  ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
636
15
  ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();
637
638
  Local<Value> out;
639
30
  if (!StatelessDiffieHellmanThreadsafe(our_key, their_key)
640
15
          .ToBuffer(env)
641
15
              .ToLocal(&out)) return;
642
643
15
  if (Buffer::Length(out) == 0)
644
2
    return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
645
646
26
  args.GetReturnValue().Set(out);
647
}
648
649
5
Maybe<bool> DHBitsTraits::AdditionalConfig(
650
    CryptoJobMode mode,
651
    const FunctionCallbackInfo<Value>& args,
652
    unsigned int offset,
653
    DHBitsConfig* params) {
654
5
  Environment* env = Environment::GetCurrent(args);
655
656

10
  CHECK(args[offset]->IsObject());  // public key
657

10
  CHECK(args[offset + 1]->IsObject());  // private key
658
659
  KeyObjectHandle* private_key;
660
  KeyObjectHandle* public_key;
661
662

10
  ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<bool>());
663

10
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<bool>());
664
665

10
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
666
5
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
667
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
668
    return Nothing<bool>();
669
  }
670
671
5
  params->public_key = public_key->Data();
672
5
  params->private_key = private_key->Data();
673
674
5
  return Just(true);
675
}
676
677
5
Maybe<bool> DHBitsTraits::EncodeOutput(
678
    Environment* env,
679
    const DHBitsConfig& params,
680
    ByteSource* out,
681
    v8::Local<v8::Value>* result) {
682
10
  *result = out->ToArrayBuffer(env);
683
5
  return Just(!result->IsEmpty());
684
}
685
686
5
bool DHBitsTraits::DeriveBits(
687
    Environment* env,
688
    const DHBitsConfig& params,
689
    ByteSource* out) {
690
10
  *out = StatelessDiffieHellmanThreadsafe(
691
10
      params.private_key->GetAsymmetricKey(),
692
15
      params.public_key->GetAsymmetricKey());
693
5
  return true;
694
}
695
696
3
Maybe<bool> GetDhKeyDetail(
697
    Environment* env,
698
    std::shared_ptr<KeyObjectData> key,
699
    Local<Object> target) {
700
3
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
701
3
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_DH);
702
3
  return Just(true);
703
}
704
705
}  // namespace crypto
706
}  // namespace node