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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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