GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_dh.cc Lines: 310 363 85.4 %
Date: 2021-09-21 04:13:38 Branches: 133 237 56.1 %

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
4276
void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
62
8552
  auto make = [&] (Local<String> name, FunctionCallback callback) {
63
17104
    Local<FunctionTemplate> t = env->NewFunctionTemplate(callback);
64
65
8552
    const PropertyAttribute attributes =
66
        static_cast<PropertyAttribute>(ReadOnly | DontDelete);
67
68
17104
    t->InstanceTemplate()->SetInternalFieldCount(
69
        DiffieHellman::kInternalFieldCount);
70
8552
    t->Inherit(BaseObject::GetConstructorTemplate(env));
71
72
8552
    env->SetProtoMethod(t, "generateKeys", GenerateKeys);
73
8552
    env->SetProtoMethod(t, "computeSecret", ComputeSecret);
74
8552
    env->SetProtoMethodNoSideEffect(t, "getPrime", GetPrime);
75
8552
    env->SetProtoMethodNoSideEffect(t, "getGenerator", GetGenerator);
76
8552
    env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey);
77
8552
    env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey);
78
8552
    env->SetProtoMethod(t, "setPublicKey", SetPublicKey);
79
8552
    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
8552
                              SideEffectType::kHasNoSideEffect);
89
90
34208
    t->InstanceTemplate()->SetAccessorProperty(
91
        env->verify_error_string(),
92
        verify_error_getter_templ,
93
        Local<FunctionTemplate>(),
94
        attributes);
95
96
8552
    env->SetConstructorFunction(target, name, t);
97
12828
  };
98
99
4276
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellman"), New);
100
4276
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
101
       DiffieHellmanGroup);
102
103
4276
  env->SetMethodNoSideEffect(target, "statelessDH", DiffieHellman::Stateless);
104
4276
  DHKeyPairGenJob::Initialize(env, target);
105
4276
  DHKeyExportJob::Initialize(env, target);
106
4276
  DHBitsJob::Initialize(env, target);
107
4276
}
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
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
124
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
125
    return false;
126
  }
127
27
  if (g <= 1) {
128
4
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
129
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
130
4
    return false;
131
  }
132
  BIGNUM* bn_p =
133
23
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr);
134
23
  BIGNUM* bn_g = BN_new();
135

46
  if (!BN_set_word(bn_g, g) ||
136
23
      !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
23
  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
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
148
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
149
    return false;
150
  }
151
21
  if (g_len <= 0) {
152
2
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
153
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
154
2
    return false;
155
  }
156
  BIGNUM* bn_g =
157
19
      BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr);
158

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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