Coverage Report

Created: 2025-07-04 09:33

/src/node/src/crypto/crypto_dh.cc
Line
Count
Source (jump to first uncovered line)
1
#include "crypto/crypto_dh.h"
2
#include "async_wrap-inl.h"
3
#include "base_object-inl.h"
4
#include "crypto/crypto_keys.h"
5
#include "crypto/crypto_util.h"
6
#include "env-inl.h"
7
#include "memory_tracker-inl.h"
8
#include "threadpoolwork-inl.h"
9
#include "v8.h"
10
11
#include <variant>
12
13
namespace node {
14
15
using v8::ArrayBuffer;
16
using v8::BackingStore;
17
using v8::ConstructorBehavior;
18
using v8::Context;
19
using v8::DontDelete;
20
using v8::FunctionCallback;
21
using v8::FunctionCallbackInfo;
22
using v8::FunctionTemplate;
23
using v8::HandleScope;
24
using v8::Int32;
25
using v8::Isolate;
26
using v8::Just;
27
using v8::Local;
28
using v8::Maybe;
29
using v8::Nothing;
30
using v8::Object;
31
using v8::PropertyAttribute;
32
using v8::ReadOnly;
33
using v8::SideEffectType;
34
using v8::Signature;
35
using v8::String;
36
using v8::Value;
37
38
namespace crypto {
39
namespace {
40
void ZeroPadDiffieHellmanSecret(size_t remainder_size,
41
                                char* data,
42
2
                                size_t length) {
43
  // DH_size returns number of bytes in a prime number.
44
  // DH_compute_key returns number of bytes in a remainder of exponent, which
45
  // may have less bytes than a prime number. Therefore add 0-padding to the
46
  // allocated buffer.
47
2
  const size_t prime_size = length;
48
2
  if (remainder_size != prime_size) {
49
0
    CHECK_LT(remainder_size, prime_size);
50
0
    const size_t padding = prime_size - remainder_size;
51
0
    memmove(data + padding, data, remainder_size);
52
0
    memset(data, 0, padding);
53
0
  }
54
2
}
55
}  // namespace
56
57
DiffieHellman::DiffieHellman(Environment* env, Local<Object> wrap)
58
2
    : BaseObject(env, wrap), verifyError_(0) {
59
2
  MakeWeak();
60
2
}
61
62
9.32k
void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
63
9.32k
  Isolate* isolate = env->isolate();
64
9.32k
  Local<Context> context = env->context();
65
18.6k
  auto make = [&](Local<String> name, FunctionCallback callback) {
66
18.6k
    Local<FunctionTemplate> t = NewFunctionTemplate(isolate, callback);
67
68
18.6k
    const PropertyAttribute attributes =
69
18.6k
        static_cast<PropertyAttribute>(ReadOnly | DontDelete);
70
71
18.6k
    t->InstanceTemplate()->SetInternalFieldCount(
72
18.6k
        DiffieHellman::kInternalFieldCount);
73
74
18.6k
    SetProtoMethod(isolate, t, "generateKeys", GenerateKeys);
75
18.6k
    SetProtoMethod(isolate, t, "computeSecret", ComputeSecret);
76
18.6k
    SetProtoMethodNoSideEffect(isolate, t, "getPrime", GetPrime);
77
18.6k
    SetProtoMethodNoSideEffect(isolate, t, "getGenerator", GetGenerator);
78
18.6k
    SetProtoMethodNoSideEffect(isolate, t, "getPublicKey", GetPublicKey);
79
18.6k
    SetProtoMethodNoSideEffect(isolate, t, "getPrivateKey", GetPrivateKey);
80
18.6k
    SetProtoMethod(isolate, t, "setPublicKey", SetPublicKey);
81
18.6k
    SetProtoMethod(isolate, t, "setPrivateKey", SetPrivateKey);
82
83
18.6k
    Local<FunctionTemplate> verify_error_getter_templ =
84
18.6k
        FunctionTemplate::New(isolate,
85
18.6k
                              DiffieHellman::VerifyErrorGetter,
86
18.6k
                              Local<Value>(),
87
18.6k
                              Signature::New(env->isolate(), t),
88
18.6k
                              /* length */ 0,
89
18.6k
                              ConstructorBehavior::kThrow,
90
18.6k
                              SideEffectType::kHasNoSideEffect);
91
92
18.6k
    t->InstanceTemplate()->SetAccessorProperty(
93
18.6k
        env->verify_error_string(),
94
18.6k
        verify_error_getter_templ,
95
18.6k
        Local<FunctionTemplate>(),
96
18.6k
        attributes);
97
98
18.6k
    SetConstructorFunction(context, target, name, t);
99
18.6k
  };
100
101
9.32k
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellman"), New);
102
9.32k
  make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
103
9.32k
       DiffieHellmanGroup);
104
105
9.32k
  SetMethodNoSideEffect(
106
9.32k
      context, target, "statelessDH", DiffieHellman::Stateless);
107
9.32k
  DHKeyPairGenJob::Initialize(env, target);
108
9.32k
  DHKeyExportJob::Initialize(env, target);
109
9.32k
  DHBitsJob::Initialize(env, target);
110
9.32k
}
111
112
void DiffieHellman::RegisterExternalReferences(
113
0
    ExternalReferenceRegistry* registry) {
114
0
  registry->Register(New);
115
0
  registry->Register(DiffieHellmanGroup);
116
117
0
  registry->Register(GenerateKeys);
118
0
  registry->Register(ComputeSecret);
119
0
  registry->Register(GetPrime);
120
0
  registry->Register(GetGenerator);
121
0
  registry->Register(GetPublicKey);
122
0
  registry->Register(GetPrivateKey);
123
0
  registry->Register(SetPublicKey);
124
0
  registry->Register(SetPrivateKey);
125
126
0
  registry->Register(DiffieHellman::VerifyErrorGetter);
127
0
  registry->Register(DiffieHellman::Stateless);
128
129
0
  DHKeyPairGenJob::RegisterExternalReferences(registry);
130
0
  DHKeyExportJob::RegisterExternalReferences(registry);
131
0
  DHBitsJob::RegisterExternalReferences(registry);
132
0
}
133
134
0
bool DiffieHellman::Init(int primeLength, int g) {
135
0
  dh_.reset(DH_new());
136
0
  if (!DH_generate_parameters_ex(dh_.get(), primeLength, g, nullptr))
137
0
    return false;
138
0
  return VerifyContext();
139
0
}
140
141
0
void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
142
0
  tracker->TrackFieldWithSize("dh", dh_ ? kSizeOf_DH : 0);
143
0
}
144
145
2
bool DiffieHellman::Init(BignumPointer&& bn_p, int g) {
146
2
  dh_.reset(DH_new());
147
2
  CHECK_GE(g, 2);
148
2
  BignumPointer bn_g(BN_new());
149
2
  return bn_g && BN_set_word(bn_g.get(), g) &&
150
2
         DH_set0_pqg(dh_.get(), bn_p.release(), nullptr, bn_g.release()) &&
151
2
         VerifyContext();
152
2
}
153
154
0
bool DiffieHellman::Init(const char* p, int p_len, int g) {
155
0
  dh_.reset(DH_new());
156
0
  if (p_len <= 0) {
157
0
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
158
0
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
159
0
    return false;
160
0
  }
161
0
  if (g <= 1) {
162
0
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
163
0
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
164
0
    return false;
165
0
  }
166
0
  BignumPointer bn_p(
167
0
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr));
168
0
  BignumPointer bn_g(BN_new());
169
0
  if (bn_p == nullptr || bn_g == nullptr || !BN_set_word(bn_g.get(), g) ||
170
0
      !DH_set0_pqg(dh_.get(), bn_p.release(), nullptr, bn_g.release())) {
171
0
    return false;
172
0
  }
173
0
  return VerifyContext();
174
0
}
175
176
0
bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
177
0
  dh_.reset(DH_new());
178
0
  if (p_len <= 0) {
179
0
    ERR_put_error(ERR_LIB_BN, BN_F_BN_GENERATE_PRIME_EX,
180
0
      BN_R_BITS_TOO_SMALL, __FILE__, __LINE__);
181
0
    return false;
182
0
  }
183
0
  if (g_len <= 0) {
184
0
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
185
0
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
186
0
    return false;
187
0
  }
188
0
  BignumPointer bn_g(
189
0
      BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, nullptr));
190
0
  if (BN_is_zero(bn_g.get()) || BN_is_one(bn_g.get())) {
191
0
    ERR_put_error(ERR_LIB_DH, DH_F_DH_BUILTIN_GENPARAMS,
192
0
      DH_R_BAD_GENERATOR, __FILE__, __LINE__);
193
0
    return false;
194
0
  }
195
0
  BignumPointer bn_p(
196
0
      BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, nullptr));
197
0
  if (!DH_set0_pqg(dh_.get(), bn_p.get(), nullptr, bn_g.get())) {
198
0
    return false;
199
0
  }
200
  // The DH_set0_pqg call above takes ownership of the bignums on success,
201
  // so we should release them here so we don't end with a possible
202
  // use-after-free or double free.
203
0
  bn_p.release();
204
0
  bn_g.release();
205
0
  return VerifyContext();
206
0
}
207
208
constexpr int kStandardizedGenerator = 2;
209
210
template <BIGNUM* (*p)(BIGNUM*)>
211
2
BignumPointer InstantiateStandardizedGroup() {
212
2
  return BignumPointer(p(nullptr));
213
2
}
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc2409_prime_768>()
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc2409_prime_1024>()
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_1536>()
std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_2048>()
Line
Count
Source
211
2
BignumPointer InstantiateStandardizedGroup() {
212
2
  return BignumPointer(p(nullptr));
213
2
}
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_3072>()
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_4096>()
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_6144>()
Unexecuted instantiation: std::__1::unique_ptr<bignum_st, node::FunctionDeleter<bignum_st, &BN_clear_free> > node::crypto::InstantiateStandardizedGroup<&BN_get_rfc3526_prime_8192>()
214
215
typedef BignumPointer (*StandardizedGroupInstantiator)();
216
217
// Returns a function that can be used to create an instance of a standardized
218
// Diffie-Hellman group. The generator is always kStandardizedGenerator.
219
2
inline StandardizedGroupInstantiator FindDiffieHellmanGroup(const char* name) {
220
2
#define V(n, p)                                                                \
221
8
  if (StringEqualNoCase(name, n)) return InstantiateStandardizedGroup<p>
222
2
  V("modp1", BN_get_rfc2409_prime_768);
223
2
  V("modp2", BN_get_rfc2409_prime_1024);
224
2
  V("modp5", BN_get_rfc3526_prime_1536);
225
2
  V("modp14", BN_get_rfc3526_prime_2048);
226
0
  V("modp15", BN_get_rfc3526_prime_3072);
227
0
  V("modp16", BN_get_rfc3526_prime_4096);
228
0
  V("modp17", BN_get_rfc3526_prime_6144);
229
0
  V("modp18", BN_get_rfc3526_prime_8192);
230
0
#undef V
231
0
  return nullptr;
232
0
}
233
234
void DiffieHellman::DiffieHellmanGroup(
235
2
    const FunctionCallbackInfo<Value>& args) {
236
2
  Environment* env = Environment::GetCurrent(args);
237
2
  DiffieHellman* diffieHellman = new DiffieHellman(env, args.This());
238
239
2
  CHECK_EQ(args.Length(), 1);
240
2
  THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "Group name");
241
242
2
  bool initialized = false;
243
244
2
  const node::Utf8Value group_name(env->isolate(), args[0]);
245
2
  auto group = FindDiffieHellmanGroup(*group_name);
246
2
  if (group == nullptr)
247
0
    return THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
248
249
2
  initialized = diffieHellman->Init(group(), kStandardizedGenerator);
250
2
  if (!initialized)
251
0
    THROW_ERR_CRYPTO_INITIALIZATION_FAILED(env);
252
2
}
253
254
255
0
void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
256
0
  Environment* env = Environment::GetCurrent(args);
257
0
  DiffieHellman* diffieHellman =
258
0
      new DiffieHellman(env, args.This());
259
0
  bool initialized = false;
260
261
0
  if (args.Length() == 2) {
262
0
    if (args[0]->IsInt32()) {
263
0
      if (args[1]->IsInt32()) {
264
0
        initialized = diffieHellman->Init(args[0].As<Int32>()->Value(),
265
0
                                          args[1].As<Int32>()->Value());
266
0
      }
267
0
    } else {
268
0
      ArrayBufferOrViewContents<char> arg0(args[0]);
269
0
      if (UNLIKELY(!arg0.CheckSizeInt32()))
270
0
        return THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
271
0
      if (args[1]->IsInt32()) {
272
0
        initialized = diffieHellman->Init(arg0.data(),
273
0
                                          arg0.size(),
274
0
                                          args[1].As<Int32>()->Value());
275
0
      } else {
276
0
        ArrayBufferOrViewContents<char> arg1(args[1]);
277
0
        if (UNLIKELY(!arg1.CheckSizeInt32()))
278
0
          return THROW_ERR_OUT_OF_RANGE(env, "generator is too big");
279
0
        initialized = diffieHellman->Init(arg0.data(), arg0.size(),
280
0
                                          arg1.data(), arg1.size());
281
0
      }
282
0
    }
283
0
  }
284
285
0
  if (!initialized) {
286
0
    return ThrowCryptoError(env, ERR_get_error(), "Initialization failed");
287
0
  }
288
0
}
289
290
291
2
void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
292
2
  Environment* env = Environment::GetCurrent(args);
293
294
2
  DiffieHellman* diffieHellman;
295
2
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
296
297
2
  if (!DH_generate_key(diffieHellman->dh_.get())) {
298
0
    return ThrowCryptoError(env, ERR_get_error(), "Key generation failed");
299
0
  }
300
301
2
  const BIGNUM* pub_key;
302
2
  DH_get0_key(diffieHellman->dh_.get(), &pub_key, nullptr);
303
304
2
  std::unique_ptr<BackingStore> bs;
305
2
  {
306
2
    const int size = BN_num_bytes(pub_key);
307
2
    CHECK_GE(size, 0);
308
2
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
309
2
    bs = ArrayBuffer::NewBackingStore(env->isolate(), size);
310
2
  }
311
312
2
  CHECK_EQ(static_cast<int>(bs->ByteLength()),
313
2
           BN_bn2binpad(pub_key,
314
2
                        static_cast<unsigned char*>(bs->Data()),
315
2
                        bs->ByteLength()));
316
317
2
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
318
2
  Local<Value> buffer;
319
2
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
320
2
  args.GetReturnValue().Set(buffer);
321
2
}
322
323
324
void DiffieHellman::GetField(const FunctionCallbackInfo<Value>& args,
325
                             const BIGNUM* (*get_field)(const DH*),
326
2
                             const char* err_if_null) {
327
2
  Environment* env = Environment::GetCurrent(args);
328
329
2
  DiffieHellman* dh;
330
2
  ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder());
331
332
2
  const BIGNUM* num = get_field(dh->dh_.get());
333
2
  if (num == nullptr)
334
0
    return THROW_ERR_CRYPTO_INVALID_STATE(env, err_if_null);
335
336
2
  std::unique_ptr<BackingStore> bs;
337
2
  {
338
2
    const int size = BN_num_bytes(num);
339
2
    CHECK_GE(size, 0);
340
2
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
341
2
    bs = ArrayBuffer::NewBackingStore(env->isolate(), size);
342
2
  }
343
344
2
  CHECK_EQ(static_cast<int>(bs->ByteLength()),
345
2
           BN_bn2binpad(num,
346
2
                        static_cast<unsigned char*>(bs->Data()),
347
2
                        bs->ByteLength()));
348
349
2
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
350
2
  Local<Value> buffer;
351
2
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
352
2
  args.GetReturnValue().Set(buffer);
353
2
}
354
355
0
void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) {
356
0
  GetField(args, [](const DH* dh) -> const BIGNUM* {
357
0
    const BIGNUM* p;
358
0
    DH_get0_pqg(dh, &p, nullptr, nullptr);
359
0
    return p;
360
0
  }, "p is null");
361
0
}
362
363
0
void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) {
364
0
  GetField(args, [](const DH* dh) -> const BIGNUM* {
365
0
    const BIGNUM* g;
366
0
    DH_get0_pqg(dh, nullptr, nullptr, &g);
367
0
    return g;
368
0
  }, "g is null");
369
0
}
370
371
2
void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
372
2
  GetField(args, [](const DH* dh) -> const BIGNUM* {
373
2
    const BIGNUM* pub_key;
374
2
    DH_get0_key(dh, &pub_key, nullptr);
375
2
    return pub_key;
376
2
  }, "No public key - did you forget to generate one?");
377
2
}
378
379
0
void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
380
0
  GetField(args, [](const DH* dh) -> const BIGNUM* {
381
0
    const BIGNUM* priv_key;
382
0
    DH_get0_key(dh, nullptr, &priv_key);
383
0
    return priv_key;
384
0
  }, "No private key - did you forget to generate one?");
385
0
}
386
387
2
void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
388
2
  Environment* env = Environment::GetCurrent(args);
389
390
2
  DiffieHellman* diffieHellman;
391
2
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
392
393
2
  ClearErrorOnReturn clear_error_on_return;
394
395
2
  CHECK_EQ(args.Length(), 1);
396
2
  ArrayBufferOrViewContents<unsigned char> key_buf(args[0]);
397
2
  if (UNLIKELY(!key_buf.CheckSizeInt32()))
398
0
    return THROW_ERR_OUT_OF_RANGE(env, "secret is too big");
399
2
  BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.size(), nullptr));
400
401
2
  std::unique_ptr<BackingStore> bs;
402
2
  {
403
2
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
404
2
    bs = ArrayBuffer::NewBackingStore(env->isolate(),
405
2
                                      DH_size(diffieHellman->dh_.get()));
406
2
  }
407
408
2
  int size = DH_compute_key(static_cast<unsigned char*>(bs->Data()),
409
2
                            key.get(),
410
2
                            diffieHellman->dh_.get());
411
412
2
  if (size == -1) {
413
0
    int checkResult;
414
0
    int checked;
415
416
0
    checked = DH_check_pub_key(diffieHellman->dh_.get(),
417
0
                               key.get(),
418
0
                               &checkResult);
419
420
0
    if (!checked) {
421
0
      return ThrowCryptoError(env, ERR_get_error(), "Invalid Key");
422
0
    } else if (checkResult) {
423
0
      if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
424
0
        return THROW_ERR_CRYPTO_INVALID_KEYLEN(env,
425
0
            "Supplied key is too small");
426
0
      } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
427
0
        return THROW_ERR_CRYPTO_INVALID_KEYLEN(env,
428
0
            "Supplied key is too large");
429
0
      }
430
0
    }
431
432
0
    return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
433
0
  }
434
435
2
  CHECK_GE(size, 0);
436
2
  ZeroPadDiffieHellmanSecret(size,
437
2
                             static_cast<char*>(bs->Data()),
438
2
                             bs->ByteLength());
439
440
2
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
441
2
  Local<Value> buffer;
442
2
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
443
2
  args.GetReturnValue().Set(buffer);
444
2
}
445
446
void DiffieHellman::SetKey(const FunctionCallbackInfo<Value>& args,
447
0
                           int (*set_field)(DH*, BIGNUM*), const char* what) {
448
0
  Environment* env = Environment::GetCurrent(args);
449
0
  DiffieHellman* dh;
450
0
  ASSIGN_OR_RETURN_UNWRAP(&dh, args.Holder());
451
0
  CHECK_EQ(args.Length(), 1);
452
0
  ArrayBufferOrViewContents<unsigned char> buf(args[0]);
453
0
  if (UNLIKELY(!buf.CheckSizeInt32()))
454
0
    return THROW_ERR_OUT_OF_RANGE(env, "buf is too big");
455
0
  BIGNUM* num = BN_bin2bn(buf.data(), buf.size(), nullptr);
456
0
  CHECK_NOT_NULL(num);
457
0
  CHECK_EQ(1, set_field(dh->dh_.get(), num));
458
0
}
459
460
0
void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
461
0
  SetKey(args,
462
0
         [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, num, nullptr); },
463
0
         "Public key");
464
0
}
465
466
0
void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
467
0
  SetKey(args,
468
0
         [](DH* dh, BIGNUM* num) { return DH_set0_key(dh, nullptr, num); },
469
0
         "Private key");
470
0
}
471
472
2
void DiffieHellman::VerifyErrorGetter(const FunctionCallbackInfo<Value>& args) {
473
2
  HandleScope scope(args.GetIsolate());
474
475
2
  DiffieHellman* diffieHellman;
476
2
  ASSIGN_OR_RETURN_UNWRAP(&diffieHellman, args.Holder());
477
478
2
  args.GetReturnValue().Set(diffieHellman->verifyError_);
479
2
}
480
481
2
bool DiffieHellman::VerifyContext() {
482
2
  int codes;
483
2
  if (!DH_check(dh_.get(), &codes))
484
0
    return false;
485
2
  verifyError_ = codes;
486
2
  return true;
487
2
}
488
489
// The input arguments to DhKeyPairGenJob can vary
490
//   1. CryptoJobMode
491
// and either
492
//   2. Group name (as a string)
493
// or
494
//   2. Prime or Prime Length
495
//   3. Generator
496
// Followed by the public and private key encoding parameters:
497
//   * Public format
498
//   * Public type
499
//   * Private format
500
//   * Private type
501
//   * Cipher
502
//   * Passphrase
503
Maybe<bool> DhKeyGenTraits::AdditionalConfig(
504
    CryptoJobMode mode,
505
    const FunctionCallbackInfo<Value>& args,
506
    unsigned int* offset,
507
0
    DhKeyPairGenConfig* params) {
508
0
  Environment* env = Environment::GetCurrent(args);
509
510
0
  if (args[*offset]->IsString()) {
511
0
    Utf8Value group_name(env->isolate(), args[*offset]);
512
0
    auto group = FindDiffieHellmanGroup(*group_name);
513
0
    if (group == nullptr) {
514
0
      THROW_ERR_CRYPTO_UNKNOWN_DH_GROUP(env);
515
0
      return Nothing<bool>();
516
0
    }
517
518
0
    params->params.prime = group();
519
0
    params->params.generator = kStandardizedGenerator;
520
0
    *offset += 1;
521
0
  } else {
522
0
    if (args[*offset]->IsInt32()) {
523
0
      int size = args[*offset].As<Int32>()->Value();
524
0
      if (size < 0) {
525
0
        THROW_ERR_OUT_OF_RANGE(env, "Invalid prime size");
526
0
        return Nothing<bool>();
527
0
      }
528
0
      params->params.prime = size;
529
0
    } else {
530
0
      ArrayBufferOrViewContents<unsigned char> input(args[*offset]);
531
0
      if (UNLIKELY(!input.CheckSizeInt32())) {
532
0
        THROW_ERR_OUT_OF_RANGE(env, "prime is too big");
533
0
        return Nothing<bool>();
534
0
      }
535
0
      params->params.prime = BignumPointer(
536
0
          BN_bin2bn(input.data(), input.size(), nullptr));
537
0
    }
538
539
0
    CHECK(args[*offset + 1]->IsInt32());
540
0
    params->params.generator = args[*offset + 1].As<Int32>()->Value();
541
0
    *offset += 2;
542
0
  }
543
544
0
  return Just(true);
545
0
}
546
547
0
EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) {
548
0
  EVPKeyPointer key_params;
549
0
  if (BignumPointer* prime_fixed_value =
550
0
          std::get_if<BignumPointer>(&params->params.prime)) {
551
0
    DHPointer dh(DH_new());
552
0
    if (!dh)
553
0
      return EVPKeyCtxPointer();
554
555
0
    BIGNUM* prime = prime_fixed_value->get();
556
0
    BignumPointer bn_g(BN_new());
557
0
    if (!BN_set_word(bn_g.get(), params->params.generator) ||
558
0
        !DH_set0_pqg(dh.get(), prime, nullptr, bn_g.get())) {
559
0
      return EVPKeyCtxPointer();
560
0
    }
561
562
0
    prime_fixed_value->release();
563
0
    bn_g.release();
564
565
0
    key_params = EVPKeyPointer(EVP_PKEY_new());
566
0
    CHECK(key_params);
567
0
    CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1);
568
0
  } else if (int* prime_size = std::get_if<int>(&params->params.prime)) {
569
0
    EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr));
570
0
    EVP_PKEY* raw_params = nullptr;
571
0
    if (!param_ctx ||
572
0
        EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
573
0
        EVP_PKEY_CTX_set_dh_paramgen_prime_len(
574
0
            param_ctx.get(),
575
0
            *prime_size) <= 0 ||
576
0
        EVP_PKEY_CTX_set_dh_paramgen_generator(
577
0
            param_ctx.get(),
578
0
            params->params.generator) <= 0 ||
579
0
        EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
580
0
      return EVPKeyCtxPointer();
581
0
    }
582
583
0
    key_params = EVPKeyPointer(raw_params);
584
0
  } else {
585
0
    UNREACHABLE();
586
0
  }
587
588
0
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
589
0
  if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
590
0
    return EVPKeyCtxPointer();
591
592
0
  return ctx;
593
0
}
594
595
Maybe<bool> DHKeyExportTraits::AdditionalConfig(
596
    const FunctionCallbackInfo<Value>& args,
597
    unsigned int offset,
598
0
    DHKeyExportConfig* params) {
599
0
  return Just(true);
600
0
}
601
602
WebCryptoKeyExportStatus DHKeyExportTraits::DoExport(
603
    std::shared_ptr<KeyObjectData> key_data,
604
    WebCryptoKeyFormat format,
605
    const DHKeyExportConfig& params,
606
0
    ByteSource* out) {
607
0
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
608
609
0
  switch (format) {
610
0
    case kWebCryptoKeyFormatPKCS8:
611
0
      if (key_data->GetKeyType() != kKeyTypePrivate)
612
0
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
613
0
      return PKEY_PKCS8_Export(key_data.get(), out);
614
0
    case kWebCryptoKeyFormatSPKI:
615
0
      if (key_data->GetKeyType() != kKeyTypePublic)
616
0
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
617
0
      return PKEY_SPKI_Export(key_data.get(), out);
618
0
    default:
619
0
      UNREACHABLE();
620
0
  }
621
0
}
622
623
namespace {
624
ByteSource StatelessDiffieHellmanThreadsafe(
625
    const ManagedEVPPKey& our_key,
626
0
    const ManagedEVPPKey& their_key) {
627
0
  size_t out_size;
628
629
0
  EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr));
630
0
  if (!ctx ||
631
0
      EVP_PKEY_derive_init(ctx.get()) <= 0 ||
632
0
      EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 ||
633
0
      EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0)
634
0
    return ByteSource();
635
636
0
  ByteSource::Builder out(out_size);
637
0
  if (EVP_PKEY_derive(ctx.get(), out.data<unsigned char>(), &out_size) <= 0) {
638
0
    return ByteSource();
639
0
  }
640
641
0
  ZeroPadDiffieHellmanSecret(out_size, out.data<char>(), out.size());
642
0
  return std::move(out).release();
643
0
}
644
}  // namespace
645
646
0
void DiffieHellman::Stateless(const FunctionCallbackInfo<Value>& args) {
647
0
  Environment* env = Environment::GetCurrent(args);
648
649
0
  CHECK(args[0]->IsObject() && args[1]->IsObject());
650
0
  KeyObjectHandle* our_key_object;
651
0
  ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
652
0
  CHECK_EQ(our_key_object->Data()->GetKeyType(), kKeyTypePrivate);
653
0
  KeyObjectHandle* their_key_object;
654
0
  ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
655
0
  CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);
656
657
0
  ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
658
0
  ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();
659
660
0
  Local<Value> out;
661
0
  if (!StatelessDiffieHellmanThreadsafe(our_key, their_key)
662
0
          .ToBuffer(env)
663
0
              .ToLocal(&out)) return;
664
665
0
  if (Buffer::Length(out) == 0)
666
0
    return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
667
668
0
  args.GetReturnValue().Set(out);
669
0
}
670
671
Maybe<bool> DHBitsTraits::AdditionalConfig(
672
    CryptoJobMode mode,
673
    const FunctionCallbackInfo<Value>& args,
674
    unsigned int offset,
675
0
    DHBitsConfig* params) {
676
0
  Environment* env = Environment::GetCurrent(args);
677
678
0
  CHECK(args[offset]->IsObject());  // public key
679
0
  CHECK(args[offset + 1]->IsObject());  // private key
680
681
0
  KeyObjectHandle* private_key;
682
0
  KeyObjectHandle* public_key;
683
684
0
  ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<bool>());
685
0
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<bool>());
686
687
0
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
688
0
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
689
0
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
690
0
    return Nothing<bool>();
691
0
  }
692
693
0
  params->public_key = public_key->Data();
694
0
  params->private_key = private_key->Data();
695
696
0
  return Just(true);
697
0
}
698
699
Maybe<bool> DHBitsTraits::EncodeOutput(
700
    Environment* env,
701
    const DHBitsConfig& params,
702
    ByteSource* out,
703
0
    v8::Local<v8::Value>* result) {
704
0
  *result = out->ToArrayBuffer(env);
705
0
  return Just(!result->IsEmpty());
706
0
}
707
708
bool DHBitsTraits::DeriveBits(
709
    Environment* env,
710
    const DHBitsConfig& params,
711
0
    ByteSource* out) {
712
0
  *out = StatelessDiffieHellmanThreadsafe(
713
0
      params.private_key->GetAsymmetricKey(),
714
0
      params.public_key->GetAsymmetricKey());
715
0
  return true;
716
0
}
717
718
Maybe<bool> GetDhKeyDetail(
719
    Environment* env,
720
    std::shared_ptr<KeyObjectData> key,
721
0
    Local<Object> target) {
722
0
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
723
0
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_DH);
724
0
  return Just(true);
725
0
}
726
727
}  // namespace crypto
728
}  // namespace node