• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "crypto/crypto_ec.h"
2 #include "async_wrap-inl.h"
3 #include "base_object-inl.h"
4 #include "crypto/crypto_common.h"
5 #include "crypto/crypto_util.h"
6 #include "env-inl.h"
7 #include "memory_tracker-inl.h"
8 #include "node_buffer.h"
9 #include "string_bytes.h"
10 #include "threadpoolwork-inl.h"
11 #include "v8.h"
12 
13 #include <openssl/bn.h>
14 #include <openssl/ec.h>
15 #include <openssl/ecdh.h>
16 
17 #include <algorithm>
18 
19 namespace node {
20 
21 using v8::Array;
22 using v8::ArrayBuffer;
23 using v8::BackingStore;
24 using v8::Context;
25 using v8::FunctionCallbackInfo;
26 using v8::FunctionTemplate;
27 using v8::Int32;
28 using v8::Isolate;
29 using v8::Just;
30 using v8::JustVoid;
31 using v8::Local;
32 using v8::Maybe;
33 using v8::Nothing;
34 using v8::Object;
35 using v8::String;
36 using v8::Uint32;
37 using v8::Value;
38 
39 namespace crypto {
40 
GetCurveFromName(const char * name)41 int GetCurveFromName(const char* name) {
42   int nid = EC_curve_nist2nid(name);
43   if (nid == NID_undef)
44     nid = OBJ_sn2nid(name);
45   return nid;
46 }
47 
GetOKPCurveFromName(const char * name)48 int GetOKPCurveFromName(const char* name) {
49   int nid;
50   if (strcmp(name, "Ed25519") == 0) {
51     nid = EVP_PKEY_ED25519;
52   } else if (strcmp(name, "Ed448") == 0) {
53     nid = EVP_PKEY_ED448;
54   } else if (strcmp(name, "X25519") == 0) {
55     nid = EVP_PKEY_X25519;
56   } else if (strcmp(name, "X448") == 0) {
57     nid = EVP_PKEY_X448;
58   } else {
59     nid = NID_undef;
60   }
61   return nid;
62 }
63 
Initialize(Environment * env,Local<Object> target)64 void ECDH::Initialize(Environment* env, Local<Object> target) {
65   Isolate* isolate = env->isolate();
66   Local<Context> context = env->context();
67 
68   Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New);
69   t->Inherit(BaseObject::GetConstructorTemplate(env));
70 
71   t->InstanceTemplate()->SetInternalFieldCount(ECDH::kInternalFieldCount);
72 
73   SetProtoMethod(isolate, t, "generateKeys", GenerateKeys);
74   SetProtoMethod(isolate, t, "computeSecret", ComputeSecret);
75   SetProtoMethodNoSideEffect(isolate, t, "getPublicKey", GetPublicKey);
76   SetProtoMethodNoSideEffect(isolate, t, "getPrivateKey", GetPrivateKey);
77   SetProtoMethod(isolate, t, "setPublicKey", SetPublicKey);
78   SetProtoMethod(isolate, t, "setPrivateKey", SetPrivateKey);
79 
80   SetConstructorFunction(context, target, "ECDH", t);
81 
82   SetMethodNoSideEffect(context, target, "ECDHConvertKey", ECDH::ConvertKey);
83   SetMethodNoSideEffect(context, target, "getCurves", ECDH::GetCurves);
84 
85   ECDHBitsJob::Initialize(env, target);
86   ECKeyPairGenJob::Initialize(env, target);
87   ECKeyExportJob::Initialize(env, target);
88 
89   NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
90   NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
91 }
92 
RegisterExternalReferences(ExternalReferenceRegistry * registry)93 void ECDH::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
94   registry->Register(New);
95   registry->Register(GenerateKeys);
96   registry->Register(ComputeSecret);
97   registry->Register(GetPublicKey);
98   registry->Register(GetPrivateKey);
99   registry->Register(SetPublicKey);
100   registry->Register(SetPrivateKey);
101   registry->Register(ECDH::ConvertKey);
102   registry->Register(ECDH::GetCurves);
103 
104   ECDHBitsJob::RegisterExternalReferences(registry);
105   ECKeyPairGenJob::RegisterExternalReferences(registry);
106   ECKeyExportJob::RegisterExternalReferences(registry);
107 }
108 
GetCurves(const FunctionCallbackInfo<Value> & args)109 void ECDH::GetCurves(const FunctionCallbackInfo<Value>& args) {
110   Environment* env = Environment::GetCurrent(args);
111   const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
112   std::vector<EC_builtin_curve> curves(num_curves);
113   CHECK_EQ(EC_get_builtin_curves(curves.data(), num_curves), num_curves);
114 
115   std::vector<Local<Value>> arr(num_curves);
116   std::transform(curves.begin(), curves.end(), arr.begin(), [env](auto& curve) {
117     return OneByteString(env->isolate(), OBJ_nid2sn(curve.nid));
118   });
119   args.GetReturnValue().Set(Array::New(env->isolate(), arr.data(), arr.size()));
120 }
121 
ECDH(Environment * env,Local<Object> wrap,ECKeyPointer && key)122 ECDH::ECDH(Environment* env, Local<Object> wrap, ECKeyPointer&& key)
123     : BaseObject(env, wrap),
124     key_(std::move(key)),
125     group_(EC_KEY_get0_group(key_.get())) {
126   MakeWeak();
127   CHECK_NOT_NULL(group_);
128 }
129 
MemoryInfo(MemoryTracker * tracker) const130 void ECDH::MemoryInfo(MemoryTracker* tracker) const {
131   tracker->TrackFieldWithSize("key", key_ ? kSizeOf_EC_KEY : 0);
132 }
133 
~ECDH()134 ECDH::~ECDH() {}
135 
New(const FunctionCallbackInfo<Value> & args)136 void ECDH::New(const FunctionCallbackInfo<Value>& args) {
137   Environment* env = Environment::GetCurrent(args);
138 
139   MarkPopErrorOnReturn mark_pop_error_on_return;
140 
141   // TODO(indutny): Support raw curves?
142   CHECK(args[0]->IsString());
143   node::Utf8Value curve(env->isolate(), args[0]);
144 
145   int nid = OBJ_sn2nid(*curve);
146   if (nid == NID_undef)
147     return THROW_ERR_CRYPTO_INVALID_CURVE(env);
148 
149   ECKeyPointer key(EC_KEY_new_by_curve_name(nid));
150   if (!key)
151     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
152       "Failed to create key using named curve");
153 
154   new ECDH(env, args.This(), std::move(key));
155 }
156 
GenerateKeys(const FunctionCallbackInfo<Value> & args)157 void ECDH::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
158   Environment* env = Environment::GetCurrent(args);
159 
160   ECDH* ecdh;
161   ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
162 
163   if (!EC_KEY_generate_key(ecdh->key_.get()))
164     return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to generate key");
165 }
166 
BufferToPoint(Environment * env,const EC_GROUP * group,Local<Value> buf)167 ECPointPointer ECDH::BufferToPoint(Environment* env,
168                                    const EC_GROUP* group,
169                                    Local<Value> buf) {
170   int r;
171 
172   ECPointPointer pub(EC_POINT_new(group));
173   if (!pub) {
174     THROW_ERR_CRYPTO_OPERATION_FAILED(env,
175         "Failed to allocate EC_POINT for a public key");
176     return pub;
177   }
178 
179   ArrayBufferOrViewContents<unsigned char> input(buf);
180   if (UNLIKELY(!input.CheckSizeInt32())) {
181     THROW_ERR_OUT_OF_RANGE(env, "buffer is too big");
182     return ECPointPointer();
183   }
184   r = EC_POINT_oct2point(
185       group,
186       pub.get(),
187       input.data(),
188       input.size(),
189       nullptr);
190   if (!r)
191     return ECPointPointer();
192 
193   return pub;
194 }
195 
ComputeSecret(const FunctionCallbackInfo<Value> & args)196 void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
197   Environment* env = Environment::GetCurrent(args);
198 
199   CHECK(IsAnyByteSource(args[0]));
200 
201   ECDH* ecdh;
202   ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
203 
204   MarkPopErrorOnReturn mark_pop_error_on_return;
205 
206   if (!ecdh->IsKeyPairValid())
207     return THROW_ERR_CRYPTO_INVALID_KEYPAIR(env);
208 
209   ECPointPointer pub(
210       ECDH::BufferToPoint(env,
211                           ecdh->group_,
212                           args[0]));
213   if (!pub) {
214     args.GetReturnValue().Set(
215         FIXED_ONE_BYTE_STRING(env->isolate(),
216         "ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY"));
217     return;
218   }
219 
220   std::unique_ptr<BackingStore> bs;
221   {
222     NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
223     // NOTE: field_size is in bits
224     int field_size = EC_GROUP_get_degree(ecdh->group_);
225     size_t out_len = (field_size + 7) / 8;
226     bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len);
227   }
228 
229   if (!ECDH_compute_key(
230           bs->Data(), bs->ByteLength(), pub.get(), ecdh->key_.get(), nullptr))
231     return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to compute ECDH key");
232 
233   Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
234   Local<Value> buffer;
235   if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
236   args.GetReturnValue().Set(buffer);
237 }
238 
GetPublicKey(const FunctionCallbackInfo<Value> & args)239 void ECDH::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
240   Environment* env = Environment::GetCurrent(args);
241 
242   // Conversion form
243   CHECK_EQ(args.Length(), 1);
244 
245   ECDH* ecdh;
246   ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
247 
248   const EC_GROUP* group = EC_KEY_get0_group(ecdh->key_.get());
249   const EC_POINT* pub = EC_KEY_get0_public_key(ecdh->key_.get());
250   if (pub == nullptr)
251     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
252         "Failed to get ECDH public key");
253 
254   CHECK(args[0]->IsUint32());
255   uint32_t val = args[0].As<Uint32>()->Value();
256   point_conversion_form_t form = static_cast<point_conversion_form_t>(val);
257 
258   const char* error;
259   Local<Object> buf;
260   if (!ECPointToBuffer(env, group, pub, form, &error).ToLocal(&buf))
261     return THROW_ERR_CRYPTO_OPERATION_FAILED(env, error);
262   args.GetReturnValue().Set(buf);
263 }
264 
GetPrivateKey(const FunctionCallbackInfo<Value> & args)265 void ECDH::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
266   Environment* env = Environment::GetCurrent(args);
267 
268   ECDH* ecdh;
269   ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
270 
271   const BIGNUM* b = EC_KEY_get0_private_key(ecdh->key_.get());
272   if (b == nullptr)
273     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
274         "Failed to get ECDH private key");
275 
276   std::unique_ptr<BackingStore> bs;
277   {
278     NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
279     bs = ArrayBuffer::NewBackingStore(env->isolate(), BN_num_bytes(b));
280   }
281   CHECK_EQ(static_cast<int>(bs->ByteLength()),
282            BN_bn2binpad(
283                b, static_cast<unsigned char*>(bs->Data()), bs->ByteLength()));
284 
285   Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
286   Local<Value> buffer;
287   if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
288   args.GetReturnValue().Set(buffer);
289 }
290 
SetPrivateKey(const FunctionCallbackInfo<Value> & args)291 void ECDH::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
292   Environment* env = Environment::GetCurrent(args);
293 
294   ECDH* ecdh;
295   ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
296 
297   ArrayBufferOrViewContents<unsigned char> priv_buffer(args[0]);
298   if (UNLIKELY(!priv_buffer.CheckSizeInt32()))
299     return THROW_ERR_OUT_OF_RANGE(env, "key is too big");
300 
301   BignumPointer priv(BN_bin2bn(
302       priv_buffer.data(), priv_buffer.size(), nullptr));
303   if (!priv) {
304     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
305         "Failed to convert Buffer to BN");
306   }
307 
308   if (!ecdh->IsKeyValidForCurve(priv)) {
309     return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env,
310         "Private key is not valid for specified curve.");
311   }
312 
313   ECKeyPointer new_key(EC_KEY_dup(ecdh->key_.get()));
314   CHECK(new_key);
315 
316   int result = EC_KEY_set_private_key(new_key.get(), priv.get());
317   priv.reset();
318 
319   if (!result) {
320     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
321         "Failed to convert BN to a private key");
322   }
323 
324   MarkPopErrorOnReturn mark_pop_error_on_return;
325   USE(&mark_pop_error_on_return);
326 
327   const BIGNUM* priv_key = EC_KEY_get0_private_key(new_key.get());
328   CHECK_NOT_NULL(priv_key);
329 
330   ECPointPointer pub(EC_POINT_new(ecdh->group_));
331   CHECK(pub);
332 
333   if (!EC_POINT_mul(ecdh->group_, pub.get(), priv_key,
334                     nullptr, nullptr, nullptr)) {
335     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
336         "Failed to generate ECDH public key");
337   }
338 
339   if (!EC_KEY_set_public_key(new_key.get(), pub.get()))
340     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
341         "Failed to set generated public key");
342 
343   ecdh->key_ = std::move(new_key);
344   ecdh->group_ = EC_KEY_get0_group(ecdh->key_.get());
345 }
346 
SetPublicKey(const FunctionCallbackInfo<Value> & args)347 void ECDH::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
348   Environment* env = Environment::GetCurrent(args);
349 
350   ECDH* ecdh;
351   ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
352 
353   CHECK(IsAnyByteSource(args[0]));
354 
355   MarkPopErrorOnReturn mark_pop_error_on_return;
356 
357   ECPointPointer pub(
358       ECDH::BufferToPoint(env,
359                           ecdh->group_,
360                           args[0]));
361   if (!pub) {
362     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
363         "Failed to convert Buffer to EC_POINT");
364   }
365 
366   int r = EC_KEY_set_public_key(ecdh->key_.get(), pub.get());
367   if (!r) {
368     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
369         "Failed to set EC_POINT as the public key");
370   }
371 }
372 
IsKeyValidForCurve(const BignumPointer & private_key)373 bool ECDH::IsKeyValidForCurve(const BignumPointer& private_key) {
374   CHECK(group_);
375   CHECK(private_key);
376   // Private keys must be in the range [1, n-1].
377   // Ref: Section 3.2.1 - http://www.secg.org/sec1-v2.pdf
378   if (BN_cmp(private_key.get(), BN_value_one()) < 0) {
379     return false;
380   }
381   BignumPointer order(BN_new());
382   CHECK(order);
383   return EC_GROUP_get_order(group_, order.get(), nullptr) &&
384          BN_cmp(private_key.get(), order.get()) < 0;
385 }
386 
IsKeyPairValid()387 bool ECDH::IsKeyPairValid() {
388   MarkPopErrorOnReturn mark_pop_error_on_return;
389   USE(&mark_pop_error_on_return);
390   return 1 == EC_KEY_check_key(key_.get());
391 }
392 
393 // Convert the input public key to compressed, uncompressed, or hybrid formats.
ConvertKey(const FunctionCallbackInfo<Value> & args)394 void ECDH::ConvertKey(const FunctionCallbackInfo<Value>& args) {
395   MarkPopErrorOnReturn mark_pop_error_on_return;
396   Environment* env = Environment::GetCurrent(args);
397 
398   CHECK_EQ(args.Length(), 3);
399   CHECK(IsAnyByteSource(args[0]));
400 
401   ArrayBufferOrViewContents<char> args0(args[0]);
402   if (UNLIKELY(!args0.CheckSizeInt32()))
403     return THROW_ERR_OUT_OF_RANGE(env, "key is too big");
404   if (args0.size() == 0)
405     return args.GetReturnValue().SetEmptyString();
406 
407   node::Utf8Value curve(env->isolate(), args[1]);
408 
409   int nid = OBJ_sn2nid(*curve);
410   if (nid == NID_undef)
411     return THROW_ERR_CRYPTO_INVALID_CURVE(env);
412 
413   ECGroupPointer group(
414       EC_GROUP_new_by_curve_name(nid));
415   if (group == nullptr)
416     return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get EC_GROUP");
417 
418   ECPointPointer pub(
419       ECDH::BufferToPoint(env,
420                           group.get(),
421                           args[0]));
422 
423   if (pub == nullptr) {
424     return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
425         "Failed to convert Buffer to EC_POINT");
426   }
427 
428   CHECK(args[2]->IsUint32());
429   uint32_t val = args[2].As<Uint32>()->Value();
430   point_conversion_form_t form = static_cast<point_conversion_form_t>(val);
431 
432   const char* error;
433   Local<Object> buf;
434   if (!ECPointToBuffer(env, group.get(), pub.get(), form, &error).ToLocal(&buf))
435     return THROW_ERR_CRYPTO_OPERATION_FAILED(env, error);
436   args.GetReturnValue().Set(buf);
437 }
438 
MemoryInfo(MemoryTracker * tracker) const439 void ECDHBitsConfig::MemoryInfo(MemoryTracker* tracker) const {
440   tracker->TrackField("public", public_);
441   tracker->TrackField("private", private_);
442 }
443 
EncodeOutput(Environment * env,const ECDHBitsConfig & params,ByteSource * out,v8::Local<v8::Value> * result)444 Maybe<bool> ECDHBitsTraits::EncodeOutput(
445     Environment* env,
446     const ECDHBitsConfig& params,
447     ByteSource* out,
448     v8::Local<v8::Value>* result) {
449   *result = out->ToArrayBuffer(env);
450   return Just(!result->IsEmpty());
451 }
452 
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int offset,ECDHBitsConfig * params)453 Maybe<bool> ECDHBitsTraits::AdditionalConfig(
454     CryptoJobMode mode,
455     const FunctionCallbackInfo<Value>& args,
456     unsigned int offset,
457     ECDHBitsConfig* params) {
458   Environment* env = Environment::GetCurrent(args);
459 
460   CHECK(args[offset]->IsString());  // curve name
461   CHECK(args[offset + 1]->IsObject());  // public key
462   CHECK(args[offset + 2]->IsObject());  // private key
463 
464   KeyObjectHandle* private_key;
465   KeyObjectHandle* public_key;
466 
467   Utf8Value name(env->isolate(), args[offset]);
468 
469   ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset + 1], Nothing<bool>());
470   ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 2], Nothing<bool>());
471 
472   if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
473       public_key->Data()->GetKeyType() != kKeyTypePublic) {
474     THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
475     return Nothing<bool>();
476   }
477 
478   params->id_ = GetOKPCurveFromName(*name);
479   params->private_ = private_key->Data();
480   params->public_ = public_key->Data();
481 
482   return Just(true);
483 }
484 
DeriveBits(Environment * env,const ECDHBitsConfig & params,ByteSource * out)485 bool ECDHBitsTraits::DeriveBits(Environment* env,
486                                 const ECDHBitsConfig& params,
487                                 ByteSource* out) {
488   size_t len = 0;
489   ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
490   ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
491 
492   switch (params.id_) {
493     case EVP_PKEY_X25519:
494       // Fall through
495     case EVP_PKEY_X448: {
496       EVPKeyCtxPointer ctx = nullptr;
497       {
498         ctx.reset(EVP_PKEY_CTX_new(m_privkey.get(), nullptr));
499       }
500       Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
501       if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
502           EVP_PKEY_derive_set_peer(
503               ctx.get(),
504               m_pubkey.get()) <= 0 ||
505           EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
506         return false;
507       }
508 
509       ByteSource::Builder buf(len);
510 
511       if (EVP_PKEY_derive(ctx.get(), buf.data<unsigned char>(), &len) <= 0) {
512         return false;
513       }
514 
515       *out = std::move(buf).release(len);
516 
517       break;
518     }
519     default: {
520       const EC_KEY* private_key;
521       {
522         Mutex::ScopedLock priv_lock(*m_privkey.mutex());
523         private_key = EVP_PKEY_get0_EC_KEY(m_privkey.get());
524       }
525 
526       Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
527       const EC_KEY* public_key = EVP_PKEY_get0_EC_KEY(m_pubkey.get());
528 
529       const EC_GROUP* group = EC_KEY_get0_group(private_key);
530       if (group == nullptr)
531         return false;
532 
533       CHECK_EQ(EC_KEY_check_key(private_key), 1);
534       CHECK_EQ(EC_KEY_check_key(public_key), 1);
535       const EC_POINT* pub = EC_KEY_get0_public_key(public_key);
536       int field_size = EC_GROUP_get_degree(group);
537       len = (field_size + 7) / 8;
538       ByteSource::Builder buf(len);
539       CHECK_NOT_NULL(pub);
540       CHECK_NOT_NULL(private_key);
541       if (ECDH_compute_key(buf.data<char>(), len, pub, private_key, nullptr) <=
542           0) {
543         return false;
544       }
545 
546       *out = std::move(buf).release();
547     }
548   }
549 
550   return true;
551 }
552 
Setup(EcKeyPairGenConfig * params)553 EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
554   EVPKeyCtxPointer key_ctx;
555   switch (params->params.curve_nid) {
556     case EVP_PKEY_ED25519:
557       // Fall through
558     case EVP_PKEY_ED448:
559       // Fall through
560     case EVP_PKEY_X25519:
561       // Fall through
562     case EVP_PKEY_X448:
563       key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr));
564       break;
565     default: {
566       EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
567       EVP_PKEY* raw_params = nullptr;
568       if (!param_ctx ||
569           EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
570           EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
571               param_ctx.get(), params->params.curve_nid) <= 0 ||
572           EVP_PKEY_CTX_set_ec_param_enc(
573               param_ctx.get(), params->params.param_encoding) <= 0 ||
574           EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
575         return EVPKeyCtxPointer();
576       }
577       EVPKeyPointer key_params(raw_params);
578       key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
579     }
580   }
581 
582   if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
583     key_ctx.reset();
584 
585   return key_ctx;
586 }
587 
588 // EcKeyPairGenJob input arguments
589 //   1. CryptoJobMode
590 //   2. Curve Name
591 //   3. Param Encoding
592 //   4. Public Format
593 //   5. Public Type
594 //   6. Private Format
595 //   7. Private Type
596 //   8. Cipher
597 //   9. Passphrase
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int * offset,EcKeyPairGenConfig * params)598 Maybe<bool> EcKeyGenTraits::AdditionalConfig(
599     CryptoJobMode mode,
600     const FunctionCallbackInfo<Value>& args,
601     unsigned int* offset,
602     EcKeyPairGenConfig* params) {
603   Environment* env = Environment::GetCurrent(args);
604   CHECK(args[*offset]->IsString());  // curve name
605   CHECK(args[*offset + 1]->IsInt32());  // param encoding
606 
607   Utf8Value curve_name(env->isolate(), args[*offset]);
608   params->params.curve_nid = GetCurveFromName(*curve_name);
609   if (params->params.curve_nid == NID_undef) {
610     THROW_ERR_CRYPTO_INVALID_CURVE(env);
611     return Nothing<bool>();
612   }
613 
614   params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
615   if (params->params.param_encoding != OPENSSL_EC_NAMED_CURVE &&
616       params->params.param_encoding != OPENSSL_EC_EXPLICIT_CURVE) {
617     THROW_ERR_OUT_OF_RANGE(env, "Invalid param_encoding specified");
618     return Nothing<bool>();
619   }
620 
621   *offset += 2;
622 
623   return Just(true);
624 }
625 
626 namespace {
EC_Raw_Export(KeyObjectData * key_data,const ECKeyExportConfig & params,ByteSource * out)627 WebCryptoKeyExportStatus EC_Raw_Export(
628     KeyObjectData* key_data,
629     const ECKeyExportConfig& params,
630     ByteSource* out) {
631   ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
632   CHECK(m_pkey);
633   Mutex::ScopedLock lock(*m_pkey.mutex());
634 
635   const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
636 
637   size_t len = 0;
638 
639   if (ec_key == nullptr) {
640     typedef int (*export_fn)(const EVP_PKEY*, unsigned char*, size_t* len);
641     export_fn fn = nullptr;
642     switch (key_data->GetKeyType()) {
643       case kKeyTypePrivate:
644         fn = EVP_PKEY_get_raw_private_key;
645         break;
646       case kKeyTypePublic:
647         fn = EVP_PKEY_get_raw_public_key;
648         break;
649       case kKeyTypeSecret:
650         UNREACHABLE();
651     }
652     CHECK_NOT_NULL(fn);
653     // Get the size of the raw key data
654     if (fn(m_pkey.get(), nullptr, &len) == 0)
655       return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
656     ByteSource::Builder data(len);
657     if (fn(m_pkey.get(), data.data<unsigned char>(), &len) == 0)
658       return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
659     *out = std::move(data).release(len);
660   } else {
661     if (key_data->GetKeyType() != kKeyTypePublic)
662       return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
663     const EC_GROUP* group = EC_KEY_get0_group(ec_key);
664     const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
665     point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
666 
667     // Get the allocated data size...
668     len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr);
669     if (len == 0)
670       return WebCryptoKeyExportStatus::FAILED;
671     ByteSource::Builder data(len);
672     size_t check_len = EC_POINT_point2oct(
673         group, point, form, data.data<unsigned char>(), len, nullptr);
674     if (check_len == 0)
675       return WebCryptoKeyExportStatus::FAILED;
676 
677     CHECK_EQ(len, check_len);
678     *out = std::move(data).release();
679   }
680 
681   return WebCryptoKeyExportStatus::OK;
682 }
683 }  // namespace
684 
AdditionalConfig(const FunctionCallbackInfo<Value> & args,unsigned int offset,ECKeyExportConfig * params)685 Maybe<bool> ECKeyExportTraits::AdditionalConfig(
686     const FunctionCallbackInfo<Value>& args,
687     unsigned int offset,
688     ECKeyExportConfig* params) {
689   return Just(true);
690 }
691 
DoExport(std::shared_ptr<KeyObjectData> key_data,WebCryptoKeyFormat format,const ECKeyExportConfig & params,ByteSource * out)692 WebCryptoKeyExportStatus ECKeyExportTraits::DoExport(
693     std::shared_ptr<KeyObjectData> key_data,
694     WebCryptoKeyFormat format,
695     const ECKeyExportConfig& params,
696     ByteSource* out) {
697   CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
698 
699   switch (format) {
700     case kWebCryptoKeyFormatRaw:
701       return EC_Raw_Export(key_data.get(), params, out);
702     case kWebCryptoKeyFormatPKCS8:
703       if (key_data->GetKeyType() != kKeyTypePrivate)
704         return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
705       return PKEY_PKCS8_Export(key_data.get(), out);
706     case kWebCryptoKeyFormatSPKI: {
707       if (key_data->GetKeyType() != kKeyTypePublic)
708         return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
709 
710       ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
711       if (EVP_PKEY_id(m_pkey.get()) != EVP_PKEY_EC) {
712         return PKEY_SPKI_Export(key_data.get(), out);
713       } else {
714         // Ensure exported key is in uncompressed point format.
715         // The temporary EC key is so we can have i2d_PUBKEY_bio() write out
716         // the header but it is a somewhat silly hoop to jump through because
717         // the header is for all practical purposes a static 26 byte sequence
718         // where only the second byte changes.
719         Mutex::ScopedLock lock(*m_pkey.mutex());
720         const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
721         const EC_GROUP* group = EC_KEY_get0_group(ec_key);
722         const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
723         const point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
724         const size_t need =
725             EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr);
726         if (need == 0) return WebCryptoKeyExportStatus::FAILED;
727         ByteSource::Builder data(need);
728         const size_t have = EC_POINT_point2oct(
729             group, point, form, data.data<unsigned char>(), need, nullptr);
730         if (have == 0) return WebCryptoKeyExportStatus::FAILED;
731         ECKeyPointer ec(EC_KEY_new());
732         CHECK_EQ(1, EC_KEY_set_group(ec.get(), group));
733         ECPointPointer uncompressed(EC_POINT_new(group));
734         CHECK_EQ(1,
735                  EC_POINT_oct2point(group,
736                                     uncompressed.get(),
737                                     data.data<unsigned char>(),
738                                     data.size(),
739                                     nullptr));
740         CHECK_EQ(1, EC_KEY_set_public_key(ec.get(), uncompressed.get()));
741         EVPKeyPointer pkey(EVP_PKEY_new());
742         CHECK_EQ(1, EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()));
743         BIOPointer bio(BIO_new(BIO_s_mem()));
744         CHECK(bio);
745         if (!i2d_PUBKEY_bio(bio.get(), pkey.get()))
746           return WebCryptoKeyExportStatus::FAILED;
747         *out = ByteSource::FromBIO(bio);
748         return WebCryptoKeyExportStatus::OK;
749       }
750     }
751     default:
752       UNREACHABLE();
753   }
754 }
755 
ExportJWKEcKey(Environment * env,std::shared_ptr<KeyObjectData> key,Local<Object> target)756 Maybe<void> ExportJWKEcKey(
757     Environment* env,
758     std::shared_ptr<KeyObjectData> key,
759     Local<Object> target) {
760   ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
761   Mutex::ScopedLock lock(*m_pkey.mutex());
762   CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
763 
764   const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
765   CHECK_NOT_NULL(ec);
766 
767   const EC_POINT* pub = EC_KEY_get0_public_key(ec);
768   const EC_GROUP* group = EC_KEY_get0_group(ec);
769 
770   int degree_bits = EC_GROUP_get_degree(group);
771   int degree_bytes =
772     (degree_bits / CHAR_BIT) + (7 + (degree_bits % CHAR_BIT)) / 8;
773 
774   BignumPointer x(BN_new());
775   BignumPointer y(BN_new());
776 
777   if (!EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr)) {
778     ThrowCryptoError(env, ERR_get_error(),
779                      "Failed to get elliptic-curve point coordinates");
780     return Nothing<void>();
781   }
782 
783   if (target->Set(
784           env->context(),
785           env->jwk_kty_string(),
786           env->jwk_ec_string()).IsNothing()) {
787     return Nothing<void>();
788   }
789 
790   if (SetEncodedValue(
791           env,
792           target,
793           env->jwk_x_string(),
794           x.get(),
795           degree_bytes).IsNothing() ||
796       SetEncodedValue(
797           env,
798           target,
799           env->jwk_y_string(),
800           y.get(),
801           degree_bytes).IsNothing()) {
802     return Nothing<void>();
803   }
804 
805   Local<String> crv_name;
806   const int nid = EC_GROUP_get_curve_name(group);
807   switch (nid) {
808     case NID_X9_62_prime256v1:
809       crv_name = OneByteString(env->isolate(), "P-256");
810       break;
811     case NID_secp256k1:
812       crv_name = OneByteString(env->isolate(), "secp256k1");
813       break;
814     case NID_secp384r1:
815       crv_name = OneByteString(env->isolate(), "P-384");
816       break;
817     case NID_secp521r1:
818       crv_name = OneByteString(env->isolate(), "P-521");
819       break;
820     default: {
821       THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
822           env, "Unsupported JWK EC curve: %s.", OBJ_nid2sn(nid));
823       return Nothing<void>();
824     }
825   }
826   if (target->Set(
827       env->context(),
828       env->jwk_crv_string(),
829       crv_name).IsNothing()) {
830     return Nothing<void>();
831   }
832 
833   if (key->GetKeyType() == kKeyTypePrivate) {
834     const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
835     return SetEncodedValue(
836       env,
837       target,
838       env->jwk_d_string(),
839       pvt,
840       degree_bytes).IsJust() ? JustVoid() : Nothing<void>();
841   }
842 
843   return JustVoid();
844 }
845 
ExportJWKEdKey(Environment * env,std::shared_ptr<KeyObjectData> key,Local<Object> target)846 Maybe<bool> ExportJWKEdKey(
847     Environment* env,
848     std::shared_ptr<KeyObjectData> key,
849     Local<Object> target) {
850   ManagedEVPPKey pkey = key->GetAsymmetricKey();
851   Mutex::ScopedLock lock(*pkey.mutex());
852 
853   const char* curve = nullptr;
854   switch (EVP_PKEY_id(pkey.get())) {
855     case EVP_PKEY_ED25519:
856       curve = "Ed25519";
857       break;
858     case EVP_PKEY_ED448:
859       curve = "Ed448";
860       break;
861     case EVP_PKEY_X25519:
862       curve = "X25519";
863       break;
864     case EVP_PKEY_X448:
865       curve = "X448";
866       break;
867     default:
868       UNREACHABLE();
869   }
870   if (target->Set(
871           env->context(),
872           env->jwk_crv_string(),
873           OneByteString(env->isolate(), curve)).IsNothing()) {
874     return Nothing<bool>();
875   }
876 
877   size_t len = 0;
878   Local<Value> encoded;
879   Local<Value> error;
880 
881   if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
882     return Nothing<bool>();
883 
884   ByteSource::Builder out(len);
885 
886   if (key->GetKeyType() == kKeyTypePrivate) {
887     if (!EVP_PKEY_get_raw_private_key(
888             pkey.get(), out.data<unsigned char>(), &len) ||
889         !StringBytes::Encode(
890              env->isolate(), out.data<const char>(), len, BASE64URL, &error)
891              .ToLocal(&encoded) ||
892         !target->Set(env->context(), env->jwk_d_string(), encoded).IsJust()) {
893       if (!error.IsEmpty())
894         env->isolate()->ThrowException(error);
895       return Nothing<bool>();
896     }
897   }
898 
899   if (!EVP_PKEY_get_raw_public_key(
900           pkey.get(), out.data<unsigned char>(), &len) ||
901       !StringBytes::Encode(
902            env->isolate(), out.data<const char>(), len, BASE64URL, &error)
903            .ToLocal(&encoded) ||
904       !target->Set(env->context(), env->jwk_x_string(), encoded).IsJust()) {
905     if (!error.IsEmpty())
906       env->isolate()->ThrowException(error);
907     return Nothing<bool>();
908   }
909 
910   if (target->Set(
911           env->context(),
912           env->jwk_kty_string(),
913           env->jwk_okp_string()).IsNothing()) {
914     return Nothing<bool>();
915   }
916 
917   return Just(true);
918 }
919 
ImportJWKEcKey(Environment * env,Local<Object> jwk,const FunctionCallbackInfo<Value> & args,unsigned int offset)920 std::shared_ptr<KeyObjectData> ImportJWKEcKey(
921     Environment* env,
922     Local<Object> jwk,
923     const FunctionCallbackInfo<Value>& args,
924     unsigned int offset) {
925   CHECK(args[offset]->IsString());  // curve name
926   Utf8Value curve(env->isolate(), args[offset].As<String>());
927 
928   int nid = GetCurveFromName(*curve);
929   if (nid == NID_undef) {  // Unknown curve
930     THROW_ERR_CRYPTO_INVALID_CURVE(env);
931     return std::shared_ptr<KeyObjectData>();
932   }
933 
934   Local<Value> x_value;
935   Local<Value> y_value;
936   Local<Value> d_value;
937 
938   if (!jwk->Get(env->context(), env->jwk_x_string()).ToLocal(&x_value) ||
939       !jwk->Get(env->context(), env->jwk_y_string()).ToLocal(&y_value) ||
940       !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
941     return std::shared_ptr<KeyObjectData>();
942   }
943 
944   if (!x_value->IsString() ||
945       !y_value->IsString() ||
946       (!d_value->IsUndefined() && !d_value->IsString())) {
947     THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
948     return std::shared_ptr<KeyObjectData>();
949   }
950 
951   KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic;
952 
953   ECKeyPointer ec(EC_KEY_new_by_curve_name(nid));
954   if (!ec) {
955     THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
956     return std::shared_ptr<KeyObjectData>();
957   }
958 
959   ByteSource x = ByteSource::FromEncodedString(env, x_value.As<String>());
960   ByteSource y = ByteSource::FromEncodedString(env, y_value.As<String>());
961 
962   if (!EC_KEY_set_public_key_affine_coordinates(
963           ec.get(),
964           x.ToBN().get(),
965           y.ToBN().get())) {
966     THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
967     return std::shared_ptr<KeyObjectData>();
968   }
969 
970   if (type == kKeyTypePrivate) {
971     ByteSource d = ByteSource::FromEncodedString(env, d_value.As<String>());
972     if (!EC_KEY_set_private_key(ec.get(), d.ToBN().get())) {
973       THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
974       return std::shared_ptr<KeyObjectData>();
975     }
976   }
977 
978   EVPKeyPointer pkey(EVP_PKEY_new());
979   CHECK_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()), 1);
980 
981   return KeyObjectData::CreateAsymmetric(type, ManagedEVPPKey(std::move(pkey)));
982 }
983 
GetEcKeyDetail(Environment * env,std::shared_ptr<KeyObjectData> key,Local<Object> target)984 Maybe<bool> GetEcKeyDetail(
985     Environment* env,
986     std::shared_ptr<KeyObjectData> key,
987     Local<Object> target) {
988   ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
989   Mutex::ScopedLock lock(*m_pkey.mutex());
990   CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
991 
992   const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
993   CHECK_NOT_NULL(ec);
994 
995   const EC_GROUP* group = EC_KEY_get0_group(ec);
996   int nid = EC_GROUP_get_curve_name(group);
997 
998   return target->Set(
999       env->context(),
1000       env->named_curve_string(),
1001       OneByteString(env->isolate(), OBJ_nid2sn(nid)));
1002 }
1003 
1004 // WebCrypto requires a different format for ECDSA signatures than
1005 // what OpenSSL produces, so we need to convert between them. The
1006 // implementation here is a adapted from Chromium's impl here:
1007 // https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc
1008 
GroupOrderSize(const ManagedEVPPKey & key)1009 size_t GroupOrderSize(const ManagedEVPPKey& key) {
1010   const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
1011   CHECK_NOT_NULL(ec);
1012   const EC_GROUP* group = EC_KEY_get0_group(ec);
1013   BignumPointer order(BN_new());
1014   CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
1015   return BN_num_bytes(order.get());
1016 }
1017 }  // namespace crypto
1018 }  // namespace node
1019