GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_dh.cc Lines: 300 351 85.5 %
Date: 2021-10-04 04:12:38 Branches: 126 223 56.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
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
2
    CHECK_LT(remainder_size, prime_size);
47
2
    const size_t padding = prime_size - remainder_size;
48
2
    memmove(data + padding, data, remainder_size);
49
2
    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
58
DiffieHellman::DiffieHellman(Environment* env, Local<Object> wrap)
59
58
    : BaseObject(env, wrap), verifyError_(0) {
60
58
  MakeWeak();
61
58
}
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
9
bool DiffieHellman::Init(int primeLength, int g) {
112
9
  dh_.reset(DH_new());
113
9
  if (!DH_generate_parameters_ex(dh_.get(), primeLength, g, nullptr))
114
3
    return false;
115
6
  return VerifyContext();
116
}
117
118
void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
119
  tracker->TrackFieldWithSize("dh", dh_ ? kSizeOf_DH : 0);
120
}
121
122
27
bool DiffieHellman::Init(const char* p, int p_len, int g) {
123
27
  dh_.reset(DH_new());
124
27
  if (p_len <= 0) {
125
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
126
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
127
    return false;
128
  }
129
27
  if (g <= 1) {
130
4
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
131
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
132
4
    return false;
133
  }
134
  BIGNUM* bn_p =
135
23
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
136
23
  BIGNUM* bn_g = BN_new();
137

46
  if (!BN_set_word(bn_g, g) ||
138
23
      !DH_set0_pqg(dh_.get(), bn_p, nullptr, bn_g)) {
139
    BN_free(bn_p);
140
    BN_free(bn_g);
141
    return false;
142
  }
143
23
  return VerifyContext();
144
}
145
146
21
bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
147
21
  dh_.reset(DH_new());
148
21
  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
21
  if (g_len <= 0) {
154
2
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
155
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
156
2
    return false;
157
  }
158
  BIGNUM* bn_g =
159
19
      BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr);
160

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

39
  if (args[*offset]->IsString()) {
433
16
    Utf8Value group_name(env->isolate(), args[*offset]);
434
8
    const modp_group* group = FindDiffieHellmanGroup(*group_name);
435
8
    if (group == nullptr) {
436
1
      THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
437
1
      return Nothing<bool>();
438
    }
439
440
14
    params->params.prime_fixed_value = BignumPointer(
441
7
        BN_bin2bn(reinterpret_cast<const unsigned char*>(group->prime),
442
14
                  group->prime_size, nullptr));
443
7
    params->params.generator = group->gen;
444
7
    *offset += 1;
445
  } else {
446

10
    if (args[*offset]->IsInt32()) {
447
9
      int size = args[*offset].As<Int32>()->Value();
448
3
      if (size < 0) {
449
        THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size");
450
        return Nothing<bool>();
451
      }
452
3
      params->params.prime_size = size;
453
    } else {
454
4
      ArrayBufferOrViewContents<unsigned char> input(args[*offset]);
455
2
      if (UNLIKELY(!input.CheckSizeInt32())) {
456
        THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
457
        return Nothing<bool>();
458
      }
459
4
      params->params.prime_fixed_value = BignumPointer(
460
4
          BN_bin2bn(input.data(), input.size(), nullptr));
461
    }
462
463

10
    CHECK(args[*offset + 1]->IsInt32());
464
15
    params->params.generator = args[*offset + 1].As<Int32>()->Value();
465
5
    *offset += 2;
466
  }
467
468
12
  return Just(true);
469
}
470
471
12
EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) {
472
12
  EVPKeyPointer key_params;
473
12
  if (params->params.prime_fixed_value) {
474
9
    DHPointer dh(DH_new());
475
9
    if (!dh)
476
      return EVPKeyCtxPointer();
477
478
9
    BIGNUM* prime = params->params.prime_fixed_value.get();
479
9
    BignumPointer bn_g(BN_new());
480

18
    if (!BN_set_word(bn_g.get(), params->params.generator) ||
481
9
        !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get()))
482
      return EVPKeyCtxPointer();
483
484
9
    params->params.prime_fixed_value.release();
485
9
    bn_g.release();
486
487
9
    key_params = EVPKeyPointer(EVP_PKEY_new());
488
9
    CHECK(key_params);
489
9
    EVP_PKEY_assign_DH(key_params.get(), dh.release());
490
  } else {
491
3
    EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr));
492
3
    EVP_PKEY* raw_params = nullptr;
493
6
    if (!param_ctx ||
494
6
        EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
495
3
        EVP_PKEY_CTX_set_dh_paramgen_prime_len(
496
            param_ctx.get(),
497
3
            params->params.prime_size) <= 0 ||
498
3
        EVP_PKEY_CTX_set_dh_paramgen_generator(
499
            param_ctx.get(),
500

6
            params->params.generator) <= 0 ||
501
3
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
502
      return EVPKeyCtxPointer();
503
    }
504
505
3
    key_params = EVPKeyPointer(raw_params);
506
  }
507
508
24
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
509

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

66
      EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 ||
553
18
      EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0)
554
4
    return ByteSource();
555
556
18
  char* buf = MallocOpenSSL<char>(out_size);
557
36
  ByteSource out = ByteSource::Allocated(buf, out_size);
558
559
18
  if (EVP_PKEY_derive(
560
          ctx.get(),
561
          reinterpret_cast<unsigned char*>(buf),
562
18
          &out_size) <= 0) {
563
    return ByteSource();
564
  }
565
566
18
  ZeroPadDiffieHellmanSecret(out_size, buf, out.size());
567
18
  return out;
568
}
569
}  // namespace
570
571
17
void DiffieHellman::Stateless(const FunctionCallbackInfo<Value>& args) {
572
17
  Environment* env = Environment::GetCurrent(args);
573
574

34
  CHECK(args[0]->IsObject() && args[1]->IsObject());
575
  KeyObjectHandle* our_key_object;
576
38
  ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
577
17
  CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate);
578
  KeyObjectHandle* their_key_object;
579
34
  ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
580
17
  CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);
581
582
17
  ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
583
17
  ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();
584
585
  Local<Value> out;
586
  {
587
34
    Local<ArrayBuffer> ab = StatelessDiffieHellmanThreadsafe(our_key, their_key)
588
17
        .ToArrayBuffer(env);
589
34
    out = Buffer::New(env, ab, 0, ab->ByteLength())
590
        .FromMaybe(Local<Uint8Array>());
591
  }
592
593
17
  if (Buffer::Length(out) == 0)
594
4
    return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
595
596
26
  args.GetReturnValue().Set(out);
597
}
598
599
5
Maybe<bool> DHBitsTraits::AdditionalConfig(
600
    CryptoJobMode mode,
601
    const FunctionCallbackInfo<Value>& args,
602
    unsigned int offset,
603
    DHBitsConfig* params) {
604
5
  Environment* env = Environment::GetCurrent(args);
605
606

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

10
  CHECK(args[offset + 1]->IsObject());  // private key
608
609
  KeyObjectHandle* private_key;
610
  KeyObjectHandle* public_key;
611
612

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

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

10
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
616
5
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
617
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
618
    return Nothing<bool>();
619
  }
620
621
5
  params->public_key = public_key->Data();
622
5
  params->private_key = private_key->Data();
623
624
5
  return Just(true);
625
}
626
627
5
Maybe<bool> DHBitsTraits::EncodeOutput(
628
    Environment* env,
629
    const DHBitsConfig& params,
630
    ByteSource* out,
631
    v8::Local<v8::Value>* result) {
632
10
  *result = out->ToArrayBuffer(env);
633
5
  return Just(!result->IsEmpty());
634
}
635
636
5
bool DHBitsTraits::DeriveBits(
637
    Environment* env,
638
    const DHBitsConfig& params,
639
    ByteSource* out) {
640
10
  *out = StatelessDiffieHellmanThreadsafe(
641
10
      params.private_key->GetAsymmetricKey(),
642
15
      params.public_key->GetAsymmetricKey());
643
5
  return true;
644
}
645
646
3
Maybe<bool> GetDhKeyDetail(
647
    Environment* env,
648
    std::shared_ptr<KeyObjectData> key,
649
    Local<Object> target) {
650
3
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
651
3
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_DH);
652
3
  return Just(true);
653
}
654
655
}  // namespace crypto
656
}  // namespace node