GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_dh.cc Lines: 314 368 85.3 %
Date: 2021-10-21 04:13:57 Branches: 124 223 55.6 %

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

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

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

21
  if (args[*offset]->IsString()) {
455
10
    Utf8Value group_name(env->isolate(), args[*offset]);
456
5
    const modp_group* group = FindDiffieHellmanGroup(*group_name);
457
5
    if (group == nullptr) {
458
1
      THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
459
1
      return Nothing<bool>();
460
    }
461
462
8
    params->params.prime_fixed_value = BignumPointer(
463
4
        BN_bin2bn(reinterpret_cast<const unsigned char*>(group->prime),
464
8
                  group->prime_size, nullptr));
465
4
    params->params.generator = group->gen;
466
4
    *offset += 1;
467
  } else {
468

4
    if (args[*offset]->IsInt32()) {
469
3
      int size = args[*offset].As<Int32>()->Value();
470
1
      if (size < 0) {
471
        THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size");
472
        return Nothing<bool>();
473
      }
474
1
      params->params.prime_size = size;
475
    } else {
476
2
      ArrayBufferOrViewContents<unsigned char> input(args[*offset]);
477
1
      if (UNLIKELY(!input.CheckSizeInt32())) {
478
        THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
479
        return Nothing<bool>();
480
      }
481
2
      params->params.prime_fixed_value = BignumPointer(
482
2
          BN_bin2bn(input.data(), input.size(), nullptr));
483
    }
484
485

4
    CHECK(args[*offset + 1]->IsInt32());
486
6
    params->params.generator = args[*offset + 1].As<Int32>()->Value();
487
2
    *offset += 2;
488
  }
489
490
6
  return Just(true);
491
}
492
493
6
EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) {
494
6
  EVPKeyPointer key_params;
495
6
  if (params->params.prime_fixed_value) {
496
5
    DHPointer dh(DH_new());
497
5
    if (!dh)
498
      return EVPKeyCtxPointer();
499
500
5
    BIGNUM* prime = params->params.prime_fixed_value.get();
501
5
    BignumPointer bn_g(BN_new());
502

10
    if (!BN_set_word(bn_g.get(), params->params.generator) ||
503
5
        !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get()))
504
      return EVPKeyCtxPointer();
505
506
5
    params->params.prime_fixed_value.release();
507
5
    bn_g.release();
508
509
5
    key_params = EVPKeyPointer(EVP_PKEY_new());
510
5
    CHECK(key_params);
511
5
    EVP_PKEY_assign_DH(key_params.get(), dh.release());
512
  } else {
513
1
    EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr));
514
1
    EVP_PKEY* raw_params = nullptr;
515
2
    if (!param_ctx ||
516
2
        EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
517
1
        EVP_PKEY_CTX_set_dh_paramgen_prime_len(
518
            param_ctx.get(),
519
2
            params->params.prime_size) <= 0 ||
520
1
        EVP_PKEY_CTX_set_dh_paramgen_generator(
521
            param_ctx.get(),
522

3
            params->params.generator) <= 0 ||
523
1
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
524
      return EVPKeyCtxPointer();
525
    }
526
527
1
    key_params = EVPKeyPointer(raw_params);
528
  }
529
530
12
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
531

6
  if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
532
    return EVPKeyCtxPointer();
533
534
6
  return ctx;
535
}
536
537
Maybe<bool> DHKeyExportTraits::AdditionalConfig(
538
    const FunctionCallbackInfo<Value>& args,
539
    unsigned int offset,
540
    DHKeyExportConfig* params) {
541
  return Just(true);
542
}
543
544
WebCryptoKeyExportStatus DHKeyExportTraits::DoExport(
545
    std::shared_ptr<KeyObjectData> key_data,
546
    WebCryptoKeyFormat format,
547
    const DHKeyExportConfig& params,
548
    ByteSource* out) {
549
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
550
551
  switch (format) {
552
    case kWebCryptoKeyFormatPKCS8:
553
      if (key_data->GetKeyType() != kKeyTypePrivate)
554
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
555
      return PKEY_PKCS8_Export(key_data.get(), out);
556
    case kWebCryptoKeyFormatSPKI:
557
      if (key_data->GetKeyType() != kKeyTypePublic)
558
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
559
      return PKEY_SPKI_Export(key_data.get(), out);
560
    default:
561
      UNREACHABLE();
562
  }
563
}
564
565
namespace {
566
19
ByteSource StatelessDiffieHellmanThreadsafe(
567
    const ManagedEVPPKey& our_key,
568
    const ManagedEVPPKey& their_key) {
569
  size_t out_size;
570
571
38
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr));
572
38
  if (!ctx ||
573
38
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
574

57
      EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 ||
575
18
      EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0)
576
1
    return ByteSource();
577
578
18
  char* buf = MallocOpenSSL<char>(out_size);
579
36
  ByteSource out = ByteSource::Allocated(buf, out_size);
580
581
18
  if (EVP_PKEY_derive(
582
          ctx.get(),
583
          reinterpret_cast<unsigned char*>(buf),
584
18
          &out_size) <= 0) {
585
    return ByteSource();
586
  }
587
588
18
  ZeroPadDiffieHellmanSecret(out_size, buf, out.size());
589
18
  return out;
590
}
591
}  // namespace
592
593
14
void DiffieHellman::Stateless(const FunctionCallbackInfo<Value>& args) {
594
14
  Environment* env = Environment::GetCurrent(args);
595
596

28
  CHECK(args[0]->IsObject() && args[1]->IsObject());
597
  KeyObjectHandle* our_key_object;
598
29
  ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
599
14
  CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate);
600
  KeyObjectHandle* their_key_object;
601
28
  ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
602
14
  CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);
603
604
14
  ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
605
14
  ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();
606
607
  Local<Value> out;
608
  {
609
28
    Local<ArrayBuffer> ab = StatelessDiffieHellmanThreadsafe(our_key, their_key)
610
14
        .ToArrayBuffer(env);
611
28
    out = Buffer::New(env, ab, 0, ab->ByteLength())
612
        .FromMaybe(Local<Uint8Array>());
613
  }
614
615
14
  if (Buffer::Length(out) == 0)
616
1
    return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
617
618
26
  args.GetReturnValue().Set(out);
619
}
620
621
5
Maybe<bool> DHBitsTraits::AdditionalConfig(
622
    CryptoJobMode mode,
623
    const FunctionCallbackInfo<Value>& args,
624
    unsigned int offset,
625
    DHBitsConfig* params) {
626
5
  Environment* env = Environment::GetCurrent(args);
627
628

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

10
  CHECK(args[offset + 1]->IsObject());  // private key
630
631
  KeyObjectHandle* private_key;
632
  KeyObjectHandle* public_key;
633
634

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

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

10
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
638
5
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
639
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
640
    return Nothing<bool>();
641
  }
642
643
5
  params->public_key = public_key->Data();
644
5
  params->private_key = private_key->Data();
645
646
5
  return Just(true);
647
}
648
649
5
Maybe<bool> DHBitsTraits::EncodeOutput(
650
    Environment* env,
651
    const DHBitsConfig& params,
652
    ByteSource* out,
653
    v8::Local<v8::Value>* result) {
654
10
  *result = out->ToArrayBuffer(env);
655
5
  return Just(!result->IsEmpty());
656
}
657
658
5
bool DHBitsTraits::DeriveBits(
659
    Environment* env,
660
    const DHBitsConfig& params,
661
    ByteSource* out) {
662
10
  *out = StatelessDiffieHellmanThreadsafe(
663
10
      params.private_key->GetAsymmetricKey(),
664
15
      params.public_key->GetAsymmetricKey());
665
5
  return true;
666
}
667
668
3
Maybe<bool> GetDhKeyDetail(
669
    Environment* env,
670
    std::shared_ptr<KeyObjectData> key,
671
    Local<Object> target) {
672
3
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
673
3
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_DH);
674
3
  return Just(true);
675
}
676
677
}  // namespace crypto
678
}  // namespace node