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