GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/crypto/crypto_dh.cc Lines: 320 369 86.7 %
Date: 2021-02-11 04:11:15 Branches: 149 251 59.4 %

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::ConstructorBehavior;
15
using v8::DontDelete;
16
using v8::FunctionCallback;
17
using v8::FunctionCallbackInfo;
18
using v8::FunctionTemplate;
19
using v8::HandleScope;
20
using v8::Int32;
21
using v8::Just;
22
using v8::Local;
23
using v8::Maybe;
24
using v8::Nothing;
25
using v8::Object;
26
using v8::PropertyAttribute;
27
using v8::ReadOnly;
28
using v8::SideEffectType;
29
using v8::Signature;
30
using v8::String;
31
using v8::Value;
32
33
namespace crypto {
34
namespace {
35
59
static void ZeroPadDiffieHellmanSecret(size_t remainder_size,
36
                                       char* data,
37
                                       size_t length) {
38
  // DH_size returns number of bytes in a prime number.
39
  // DH_compute_key returns number of bytes in a remainder of exponent, which
40
  // may have less bytes than a prime number. Therefore add 0-padding to the
41
  // allocated buffer.
42
59
  const size_t prime_size = length;
43
59
  if (remainder_size != prime_size) {
44
2
    CHECK_LT(remainder_size, prime_size);
45
2
    const size_t padding = prime_size - remainder_size;
46
2
    memmove(data + padding, data, remainder_size);
47
2
    memset(data, 0, padding);
48
  }
49
59
}
50
54
static void ZeroPadDiffieHellmanSecret(size_t remainder_size,
51
                                       AllocatedBuffer* ret) {
52
54
  ZeroPadDiffieHellmanSecret(remainder_size, ret->data(), ret->size());
53
54
}
54
}  // namespace
55
56
58
DiffieHellman::DiffieHellman(Environment* env, Local<Object> wrap)
57
58
    : BaseObject(env, wrap), verifyError_(0) {
58
58
  MakeWeak();
59
58
}
60
61
667
void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
62
1334
  auto make = [&] (Local<String> name, FunctionCallback callback) {
63
18676
    Local<FunctionTemplate> t = env->NewFunctionTemplate(callback);
64
65
    const PropertyAttribute attributes =
66
1334
        static_cast<PropertyAttribute>(ReadOnly | DontDelete);
67
68
4002
    t->InstanceTemplate()->SetInternalFieldCount(
69
1334
        DiffieHellman::kInternalFieldCount);
70
2668
    t->Inherit(BaseObject::GetConstructorTemplate(env));
71
72
1334
    env->SetProtoMethod(t, "generateKeys", GenerateKeys);
73
1334
    env->SetProtoMethod(t, "computeSecret", ComputeSecret);
74
1334
    env->SetProtoMethodNoSideEffect(t, "getPrime", GetPrime);
75
1334
    env->SetProtoMethodNoSideEffect(t, "getGenerator", GetGenerator);
76
1334
    env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey);
77
1334
    env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey);
78
1334
    env->SetProtoMethod(t, "setPublicKey", SetPublicKey);
79
1334
    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
2668
                              SideEffectType::kHasNoSideEffect);
89
90
5336
    t->InstanceTemplate()->SetAccessorProperty(
91
        env->verify_error_string(),
92
        verify_error_getter_templ,
93
        Local<FunctionTemplate>(),
94
1334
        attributes);
95
96
2668
    env->SetConstructorFunction(target, name, t);
97
2001
  };
98
99
667
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellman"), New);
100
667
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
101
667
       DiffieHellmanGroup);
102
103
667
  env->SetMethodNoSideEffect(target, "statelessDH", DiffieHellman::Stateless);
104
667
  DHKeyPairGenJob::Initialize(env, target);
105
667
  DHKeyExportJob::Initialize(env, target);
106
667
  DHBitsJob::Initialize(env, target);
107
667
}
108
109
9
bool DiffieHellman::Init(int primeLength, int g) {
110
9
  dh_.reset(DH_new());
111
9
  if (!DH_generate_parameters_ex(dh_.get(), primeLength, g, nullptr))
112
3
    return false;
113
6
  return VerifyContext();
114
}
115
116
void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
117
  tracker->TrackFieldWithSize("dh", dh_ ? kSizeOf_DH : 0);
118
}
119
120
27
bool DiffieHellman::Init(const char* p, int p_len, int g) {
121
27
  dh_.reset(DH_new());
122
27
  if (p_len <= 0) {
123
    BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
124
    return false;
125
  }
126
27
  if (g <= 1) {
127
4
    DHerr(DH_F_DH_BUILTIN_GENPARAMS, DH_R_BAD_GENERATOR);
128
4
    return false;
129
  }
130
  BIGNUM* bn_p =
131
23
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
132
23
  BIGNUM* bn_g = BN_new();
133

46
  if (!BN_set_word(bn_g, g) ||
134
23
      !DH_set0_pqg(dh_.get(), bn_p, nullptr, bn_g)) {
135
    BN_free(bn_p);
136
    BN_free(bn_g);
137
    return false;
138
  }
139
23
  return VerifyContext();
140
}
141
142
21
bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
143
21
  dh_.reset(DH_new());
144
21
  if (p_len <= 0) {
145
    BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
146
    return false;
147
  }
148
21
  if (g_len <= 0) {
149
2
    DHerr(DH_F_DH_BUILTIN_GENPARAMS, DH_R_BAD_GENERATOR);
150
2
    return false;
151
  }
152
  BIGNUM* bn_g =
153
19
      BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr);
154

19
  if (BN_is_zero(bn_g) || BN_is_one(bn_g)) {
155
4
    BN_free(bn_g);
156
4
    DHerr(DH_F_DH_BUILTIN_GENPARAMS, DH_R_BAD_GENERATOR);
157
4
    return false;
158
  }
159
  BIGNUM* bn_p =
160
15
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
161
15
  if (!DH_set0_pqg(dh_.get(), bn_p, nullptr, bn_g)) {
162
    BN_free(bn_p);
163
    BN_free(bn_g);
164
    return false;
165
  }
166
15
  return VerifyContext();
167
}
168
169
18
inline const modp_group* FindDiffieHellmanGroup(const char* name) {
170
69
  for (const modp_group& group : modp_groups) {
171
67
    if (StringEqualNoCase(name, group.name))
172
16
      return &group;
173
  }
174
2
  return nullptr;
175
}
176
177
10
void DiffieHellman::DiffieHellmanGroup(
178
    const FunctionCallbackInfo<Value>& args) {
179
10
  Environment* env = Environment::GetCurrent(args);
180
10
  DiffieHellman* diffieHellman = new DiffieHellman(env, args.This());
181
182
10
  CHECK_EQ(args.Length(), 1);
183
31
  THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Group name");
184
185
10
  bool initialized = false;
186
187
19
  const node::Utf8Value group_name(env->isolate(), args[0]);
188
10
  const modp_group* group = FindDiffieHellmanGroup(*group_name);
189
10
  if (group == nullptr)
190
1
    return THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
191
192
18
  initialized = diffieHellman->Init(group->prime,
193
9
                                    group->prime_size,
194
18
                                    group->gen);
195
9
  if (!initialized)
196
    THROW_ERR_CRYPTO_INITIALIZATION_FAILED(env);
197
}
198
199
200
48
void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
201
48
  Environment* env = Environment::GetCurrent(args);
202
  DiffieHellman* diffieHellman =
203
48
      new DiffieHellman(env, args.This());
204
48
  bool initialized = false;
205
206
48
  if (args.Length() == 2) {
207
96
    if (args[0]->IsInt32()) {
208
18
      if (args[1]->IsInt32()) {
209
45
        initialized = diffieHellman->Init(args[0].As<Int32>()->Value(),
210
36
                                          args[1].As<Int32>()->Value());
211
      }
212
    } else {
213
78
      ArrayBufferOrViewContents<char> arg0(args[0]);
214
39
      if (UNLIKELY(!arg0.CheckSizeInt32()))
215
        return THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
216
78
      if (args[1]->IsInt32()) {
217
36
        initialized = diffieHellman->Init(arg0.data(),
218
18
                                          arg0.size(),
219
54
                                          args[1].As<Int32>()->Value());
220
      } else {
221
42
        ArrayBufferOrViewContents<char> arg1(args[1]);
222
21
        if (UNLIKELY(!arg1.CheckSizeInt32()))
223
          return THROW_ERR_OUT_OF_RANGE(env, "generator is too big");
224
42
        initialized = diffieHellman->Init(arg0.data(), arg0.size(),
225
63
                                          arg1.data(), arg1.size());
226
      }
227
    }
228
  }
229
230
48
  if (!initialized) {
231
13
    return ThrowCryptoError(env, ERR_get_error(), "Initialization failed");
232
  }
233
}
234
235
236
25
void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
237
25
  Environment* env = Environment::GetCurrent(args);
238
239
  DiffieHellman* diffieHellman;
240
25
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
241
242
25
  if (!DH_generate_key(diffieHellman->dh_.get())) {
243
    return ThrowCryptoError(env, ERR_get_error(), "Key generation failed");
244
  }
245
246
  const BIGNUM* pub_key;
247
25
  DH_get0_key(diffieHellman->dh_.get(), &pub_key, nullptr);
248
25
  const int size = BN_num_bytes(pub_key);
249
25
  CHECK_GE(size, 0);
250
50
  AllocatedBuffer data = AllocatedBuffer::AllocateManaged(env, size);
251
25
  CHECK_EQ(size,
252
           BN_bn2binpad(
253
               pub_key, reinterpret_cast<unsigned char*>(data.data()), size));
254
75
  args.GetReturnValue().Set(data.ToBuffer().FromMaybe(Local<Value>()));
255
}
256
257
258
63
void DiffieHellman::GetField(const FunctionCallbackInfo<Value>& args,
259
                             const BIGNUM* (*get_field)(const DH*),
260
                             const char* err_if_null) {
261
63
  Environment* env = Environment::GetCurrent(args);
262
263
  DiffieHellman* dh;
264
63
  ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder());
265
266
63
  const BIGNUM* num = get_field(dh->dh_.get());
267
63
  if (num == nullptr)
268
    return THROW_ERR_CRYPTO_INVALID_STATE(env, err_if_null);
269
270
63
  const int size = BN_num_bytes(num);
271
63
  CHECK_GE(size, 0);
272
126
  AllocatedBuffer data = AllocatedBuffer::AllocateManaged(env, size);
273
63
  CHECK_EQ(
274
      size,
275
      BN_bn2binpad(num, reinterpret_cast<unsigned char*>(data.data()), size));
276
189
  args.GetReturnValue().Set(data.ToBuffer().FromMaybe(Local<Value>()));
277
}
278
279
13
void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) {
280
65
  GetField(args, [](const DH* dh) -> const BIGNUM* {
281
    const BIGNUM* p;
282
13
    DH_get0_pqg(dh, &p, nullptr, nullptr);
283
13
    return p;
284
39
  }, "p is null");
285
13
}
286
287
7
void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) {
288
35
  GetField(args, [](const DH* dh) -> const BIGNUM* {
289
    const BIGNUM* g;
290
7
    DH_get0_pqg(dh, nullptr, nullptr, &g);
291
7
    return g;
292
21
  }, "g is null");
293
7
}
294
295
34
void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
296
170
  GetField(args, [](const DH* dh) -> const BIGNUM* {
297
    const BIGNUM* pub_key;
298
34
    DH_get0_key(dh, &pub_key, nullptr);
299
34
    return pub_key;
300
102
  }, "No public key - did you forget to generate one?");
301
34
}
302
303
9
void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
304
45
  GetField(args, [](const DH* dh) -> const BIGNUM* {
305
    const BIGNUM* priv_key;
306
9
    DH_get0_key(dh, nullptr, &priv_key);
307
9
    return priv_key;
308
27
  }, "No private key - did you forget to generate one?");
309
9
}
310
311
42
void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
312
42
  Environment* env = Environment::GetCurrent(args);
313
314
  DiffieHellman* diffieHellman;
315
43
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
316
317
41
  ClearErrorOnReturn clear_error_on_return;
318
319
42
  CHECK_EQ(args.Length(), 1);
320
83
  ArrayBufferOrViewContents<unsigned char> key_buf(args[0]);
321
42
  if (UNLIKELY(!key_buf.CheckSizeInt32()))
322
    return THROW_ERR_OUT_OF_RANGE(env, "secret is too big");
323
83
  BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.size(), nullptr));
324
325
  AllocatedBuffer ret =
326
83
      AllocatedBuffer::AllocateManaged(env, DH_size(diffieHellman->dh_.get()));
327
328
84
  int size = DH_compute_key(reinterpret_cast<unsigned char*>(ret.data()),
329
42
                            key.get(),
330
84
                            diffieHellman->dh_.get());
331
332
42
  if (size == -1) {
333
    int checkResult;
334
    int checked;
335
336
1
    checked = DH_check_pub_key(diffieHellman->dh_.get(),
337
1
                               key.get(),
338
1
                               &checkResult);
339
340
1
    if (!checked) {
341
      return ThrowCryptoError(env, ERR_get_error(), "Invalid Key");
342
1
    } else if (checkResult) {
343
1
      if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
344
        return THROW_ERR_CRYPTO_INVALID_KEYLEN(env,
345
1
            "Supplied key is too small");
346
      } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
347
        return THROW_ERR_CRYPTO_INVALID_KEYLEN(env,
348
            "Supplied key is too large");
349
      }
350
    }
351
352
    return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
353
  }
354
355
41
  CHECK_GE(size, 0);
356
41
  ZeroPadDiffieHellmanSecret(static_cast<size_t>(size), &ret);
357
358
123
  args.GetReturnValue().Set(ret.ToBuffer().FromMaybe(Local<Value>()));
359
}
360
361
100009
void DiffieHellman::SetKey(const FunctionCallbackInfo<Value>& args,
362
                           int (*set_field)(DH*, BIGNUM*), const char* what) {
363
100009
  Environment* env = Environment::GetCurrent(args);
364
  DiffieHellman* dh;
365
100009
  ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder());
366
100009
  CHECK_EQ(args.Length(), 1);
367
200018
  ArrayBufferOrViewContents<unsigned char> buf(args[0]);
368
100009
  if (UNLIKELY(!buf.CheckSizeInt32()))
369
    return THROW_ERR_OUT_OF_RANGE(env, "buf is too big");
370
100009
  BIGNUM* num = BN_bin2bn(buf.data(), buf.size(), nullptr);
371
100009
  CHECK_NOT_NULL(num);
372

100009
  CHECK_EQ(1, set_field(dh->dh_.get(), num));
373
}
374
375
50003
void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
376
100006
  SetKey(args,
377
150009
         [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, num, nullptr); },
378
50003
         "Public key");
379
50003
}
380
381
50006
void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
382
100012
  SetKey(args,
383
150018
         [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, nullptr, num); },
384
50006
         "Private key");
385
50006
}
386
387
44
void DiffieHellman::VerifyErrorGetter(const FunctionCallbackInfo<Value>& args) {
388
88
  HandleScope scope(args.GetIsolate());
389
390
  DiffieHellman* diffieHellman;
391
44
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
392
393
132
  args.GetReturnValue().Set(diffieHellman->verifyError_);
394
}
395
396
44
bool DiffieHellman::VerifyContext() {
397
  int codes;
398
44
  if (!DH_check(dh_.get(), &codes))
399
    return false;
400
44
  verifyError_ = codes;
401
44
  return true;
402
}
403
404
// The input arguments to DhKeyPairGenJob can vary
405
//   1. CryptoJobMode
406
// and either
407
//   2. Group name (as a string)
408
// or
409
//   2. Prime or Prime Length
410
//   3. Generator
411
// Followed by the public and private key encoding parameters:
412
//   * Public format
413
//   * Public type
414
//   * Private format
415
//   * Private type
416
//   * Cipher
417
//   * Passphrase
418
13
Maybe<bool> DhKeyGenTraits::AdditionalConfig(
419
    CryptoJobMode mode,
420
    const FunctionCallbackInfo<Value>& args,
421
    unsigned int* offset,
422
    DhKeyPairGenConfig* params) {
423
13
  Environment* env = Environment::GetCurrent(args);
424
425
52
  if (args[*offset]->IsString()) {
426
23
    Utf8Value group_name(env->isolate(), args[*offset]);
427
8
    const modp_group* group = FindDiffieHellmanGroup(*group_name);
428
8
    if (group == nullptr) {
429
1
      THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
430
1
      return Nothing<bool>();
431
    }
432
433
21
    params->params.prime_fixed_value = BignumPointer(
434
7
        BN_bin2bn(reinterpret_cast<const unsigned char*>(group->prime),
435
14
                  group->prime_size, nullptr));
436
7
    params->params.generator = group->gen;
437
7
    *offset += 1;
438
  } else {
439
15
    if (args[*offset]->IsInt32()) {
440
12
      int size = args[*offset].As<Int32>()->Value();
441
3
      if (size < 0) {
442
        THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size");
443
        return Nothing<bool>();
444
      }
445
3
      params->params.prime_size = size;
446
    } else {
447
6
      ArrayBufferOrViewContents<unsigned char> input(args[*offset]);
448
2
      if (UNLIKELY(!input.CheckSizeInt32())) {
449
        THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
450
        return Nothing<bool>();
451
      }
452
6
      params->params.prime_fixed_value = BignumPointer(
453
6
          BN_bin2bn(input.data(), input.size(), nullptr));
454
    }
455
456
15
    CHECK(args[*offset + 1]->IsInt32());
457
20
    params->params.generator = args[*offset + 1].As<Int32>()->Value();
458
5
    *offset += 2;
459
  }
460
461
12
  return Just(true);
462
}
463
464
12
EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) {
465
24
  EVPKeyPointer key_params;
466
12
  if (params->params.prime_fixed_value) {
467
18
    DHPointer dh(DH_new());
468
9
    if (!dh)
469
      return EVPKeyCtxPointer();
470
471
9
    BIGNUM* prime = params->params.prime_fixed_value.get();
472
18
    BignumPointer bn_g(BN_new());
473

18
    if (!BN_set_word(bn_g.get(), params->params.generator) ||
474
9
        !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get()))
475
      return EVPKeyCtxPointer();
476
477
9
    params->params.prime_fixed_value.release();
478
9
    bn_g.release();
479
480
9
    key_params = EVPKeyPointer(EVP_PKEY_new());
481
9
    CHECK(key_params);
482
9
    EVP_PKEY_assign_DH(key_params.get(), dh.release());
483
  } else {
484
6
    EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr));
485
3
    EVP_PKEY* raw_params = nullptr;
486

9
    if (!param_ctx ||
487
6
        EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
488
3
        EVP_PKEY_CTX_set_dh_paramgen_prime_len(
489
            param_ctx.get(),
490
3
            params->params.prime_size) <= 0 ||
491
3
        EVP_PKEY_CTX_set_dh_paramgen_generator(
492
            param_ctx.get(),
493

6
            params->params.generator) <= 0 ||
494
3
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
495
      return EVPKeyCtxPointer();
496
    }
497
498
3
    key_params = EVPKeyPointer(raw_params);
499
  }
500
501
24
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
502

12
  if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
503
    return EVPKeyCtxPointer();
504
505
12
  return ctx;
506
}
507
508
Maybe<bool> DHKeyExportTraits::AdditionalConfig(
509
    const FunctionCallbackInfo<Value>& args,
510
    unsigned int offset,
511
    DHKeyExportConfig* params) {
512
  return Just(true);
513
}
514
515
WebCryptoKeyExportStatus DHKeyExportTraits::DoExport(
516
    std::shared_ptr<KeyObjectData> key_data,
517
    WebCryptoKeyFormat format,
518
    const DHKeyExportConfig& params,
519
    ByteSource* out) {
520
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
521
522
  switch (format) {
523
    case kWebCryptoKeyFormatPKCS8:
524
      if (key_data->GetKeyType() != kKeyTypePrivate)
525
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
526
      return PKEY_PKCS8_Export(key_data.get(), out);
527
    case kWebCryptoKeyFormatSPKI:
528
      if (key_data->GetKeyType() != kKeyTypePublic)
529
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
530
      return PKEY_SPKI_Export(key_data.get(), out);
531
    default:
532
      UNREACHABLE();
533
  }
534
}
535
536
namespace {
537
17
AllocatedBuffer StatelessDiffieHellman(
538
    Environment* env,
539
    ManagedEVPPKey our_key,
540
    ManagedEVPPKey their_key) {
541
  size_t out_size;
542
543
34
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr));
544

51
  if (!ctx ||
545
34
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
546

47
      EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 ||
547
13
      EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0)
548
4
    return AllocatedBuffer();
549
550
26
  AllocatedBuffer result = AllocatedBuffer::AllocateManaged(env, out_size);
551
13
  CHECK_NOT_NULL(result.data());
552
553
13
  unsigned char* data = reinterpret_cast<unsigned char*>(result.data());
554
13
  if (EVP_PKEY_derive(ctx.get(), data, &out_size) <= 0)
555
    return AllocatedBuffer();
556
557
13
  ZeroPadDiffieHellmanSecret(out_size, &result);
558
13
  return result;
559
}
560
561
// The version of StatelessDiffieHellman that returns an AllocatedBuffer
562
// is not threadsafe because of the AllocatedBuffer allocation of a
563
// v8::BackingStore (it'll cause much crashing if we call it from a
564
// libuv worker thread). This version allocates a ByteSource instead,
565
// which we can convert into a v8::BackingStore later.
566
// TODO(@jasnell): Eliminate the code duplication between these two
567
// versions of the function.
568
5
ByteSource StatelessDiffieHellmanThreadsafe(
569
    Environment* env,
570
    ManagedEVPPKey our_key,
571
    ManagedEVPPKey their_key) {
572
  size_t out_size;
573
574
10
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr));
575

15
  if (!ctx ||
576
10
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
577

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

68
  CHECK(args[0]->IsObject() && args[1]->IsObject());
600
  KeyObjectHandle* our_key_object;
601
38
  ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
602
17
  CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate);
603
  KeyObjectHandle* their_key_object;
604
34
  ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
605
17
  CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);
606
607
30
  ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
608
30
  ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();
609
610
30
  AllocatedBuffer out = StatelessDiffieHellman(env, our_key, their_key);
611
17
  if (out.size() == 0)
612
4
    return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
613
614
39
  args.GetReturnValue().Set(out.ToBuffer().FromMaybe(Local<Value>()));
615
}
616
617
5
Maybe<bool> DHBitsTraits::AdditionalConfig(
618
    CryptoJobMode mode,
619
    const FunctionCallbackInfo<Value>& args,
620
    unsigned int offset,
621
    DHBitsConfig* params) {
622
5
  Environment* env = Environment::GetCurrent(args);
623
624
15
  CHECK(args[offset]->IsObject());  // public key
625
15
  CHECK(args[offset + 1]->IsObject());  // private key
626
627
  KeyObjectHandle* private_key;
628
  KeyObjectHandle* public_key;
629
630
10
  ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<bool>());
631
10
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<bool>());
632
633

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

14097
}  // namespace node