• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>(&params->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>(&params->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