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: 321 370 86.8 %
Date: 2020-11-20 19:51:53 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
57
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
57
  const size_t prime_size = length;
43
57
  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
57
}
50
52
static void ZeroPadDiffieHellmanSecret(size_t remainder_size,
51
                                       AllocatedBuffer* ret) {
52
52
  ZeroPadDiffieHellmanSecret(remainder_size, ret->data(), ret->size());
53
52
}
54
}  // namespace
55
56
56
DiffieHellman::DiffieHellman(Environment* env, Local<Object> wrap)
57
56
    : BaseObject(env, wrap), verifyError_(0) {
58
56
  MakeWeak();
59
56
}
60
61
652
void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
62
1307
  auto make = [&] (Local<String> name, FunctionCallback callback) {
63
19613
    Local<FunctionTemplate> t = env->NewFunctionTemplate(callback);
64
65
    const PropertyAttribute attributes =
66
1308
        static_cast<PropertyAttribute>(ReadOnly | DontDelete);
67
68
3923
    t->InstanceTemplate()->SetInternalFieldCount(
69
1308
        DiffieHellman::kInternalFieldCount);
70
2613
    t->Inherit(BaseObject::GetConstructorTemplate(env));
71
72
1306
    env->SetProtoMethod(t, "generateKeys", GenerateKeys);
73
1307
    env->SetProtoMethod(t, "computeSecret", ComputeSecret);
74
1307
    env->SetProtoMethodNoSideEffect(t, "getPrime", GetPrime);
75
1308
    env->SetProtoMethodNoSideEffect(t, "getGenerator", GetGenerator);
76
1308
    env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey);
77
1308
    env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey);
78
1308
    env->SetProtoMethod(t, "setPublicKey", SetPublicKey);
79
1308
    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
2616
                              SideEffectType::kHasNoSideEffect);
89
90
5232
    t->InstanceTemplate()->SetAccessorProperty(
91
        env->verify_error_string(),
92
        verify_error_getter_templ,
93
        Local<FunctionTemplate>(),
94
1308
        attributes);
95
96
3921
    target->Set(env->context(),
97
                name,
98
6537
                t->GetFunction(env->context()).ToLocalChecked()).Check();
99
1959
  };
100
101
652
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellman"), New);
102
653
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
103
654
       DiffieHellmanGroup);
104
105
654
  env->SetMethodNoSideEffect(target, "statelessDH", DiffieHellman::Stateless);
106
654
  DHKeyPairGenJob::Initialize(env, target);
107
653
  DHKeyExportJob::Initialize(env, target);
108
654
  DHBitsJob::Initialize(env, target);
109
654
}
110
111
8
bool DiffieHellman::Init(int primeLength, int g) {
112
8
  dh_.reset(DH_new());
113
8
  if (!DH_generate_parameters_ex(dh_.get(), primeLength, g, nullptr))
114
3
    return false;
115
5
  return VerifyContext();
116
}
117
118
void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
119
  tracker->TrackFieldWithSize("dh", dh_ ? kSizeOf_DH : 0);
120
}
121
122
26
bool DiffieHellman::Init(const char* p, int p_len, int g) {
123
26
  dh_.reset(DH_new());
124
26
  if (p_len <= 0) {
125
    BNerr(BN_F_BN_GENERATE_PRIME_EX, BN_R_BITS_TOO_SMALL);
126
    return false;
127
  }
128
26
  if (g <= 1) {
129
4
    DHerr(DH_F_DH_BUILTIN_GENPARAMS, DH_R_BAD_GENERATOR);
130
4
    return false;
131
  }
132
  BIGNUM* bn_p =
133
22
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
134
22
  BIGNUM* bn_g = BN_new();
135

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

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

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

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

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

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

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

51
  if (!ctx ||
547
34
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
548

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

15
  if (!ctx ||
578
10
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
579

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

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

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

14034
}  // namespace node