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