GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_dh.cc Lines: 316 371 85.2 %
Date: 2021-10-23 04:13:56 Branches: 126 227 55.5 %

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

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

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

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

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

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

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

3
            params->params.generator) <= 0 ||
527
1
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
528
      return EVPKeyCtxPointer();
529
    }
530
531
1
    key_params = EVPKeyPointer(raw_params);
532
  } else {
533
    UNREACHABLE();
534
  }
535
536
12
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
537

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

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

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

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

10
  CHECK(args[offset + 1]->IsObject());  // private key
636
637
  KeyObjectHandle* private_key;
638
  KeyObjectHandle* public_key;
639
640

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

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

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