GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 428 552 77.5 %
Date: 2022-08-17 04:19:55 Branches: 188 350 53.7 %

Line Branch Exec Source
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
41
384
int GetCurveFromName(const char* name) {
42
384
  int nid = EC_curve_nist2nid(name);
43
384
  if (nid == NID_undef)
44
19
    nid = OBJ_sn2nid(name);
45
384
  return nid;
46
}
47
48
182
int GetOKPCurveFromName(const char* name) {
49
  int nid;
50
182
  if (strcmp(name, "Ed25519") == 0) {
51
15
    nid = EVP_PKEY_ED25519;
52
167
  } else if (strcmp(name, "Ed448") == 0) {
53
15
    nid = EVP_PKEY_ED448;
54
152
  } else if (strcmp(name, "X25519") == 0) {
55
39
    nid = EVP_PKEY_X25519;
56
113
  } else if (strcmp(name, "X448") == 0) {
57
39
    nid = EVP_PKEY_X448;
58
  } else {
59
74
    nid = NID_undef;
60
  }
61
182
  return nid;
62
}
63
64
772
void ECDH::Initialize(Environment* env, Local<Object> target) {
65
772
  Isolate* isolate = env->isolate();
66
772
  Local<Context> context = env->context();
67
68
772
  Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New);
69
772
  t->Inherit(BaseObject::GetConstructorTemplate(env));
70
71
1544
  t->InstanceTemplate()->SetInternalFieldCount(ECDH::kInternalFieldCount);
72
73
772
  SetProtoMethod(isolate, t, "generateKeys", GenerateKeys);
74
772
  SetProtoMethod(isolate, t, "computeSecret", ComputeSecret);
75
772
  SetProtoMethodNoSideEffect(isolate, t, "getPublicKey", GetPublicKey);
76
772
  SetProtoMethodNoSideEffect(isolate, t, "getPrivateKey", GetPrivateKey);
77
772
  SetProtoMethod(isolate, t, "setPublicKey", SetPublicKey);
78
772
  SetProtoMethod(isolate, t, "setPrivateKey", SetPrivateKey);
79
80
772
  SetConstructorFunction(context, target, "ECDH", t);
81
82
772
  SetMethodNoSideEffect(context, target, "ECDHConvertKey", ECDH::ConvertKey);
83
772
  SetMethodNoSideEffect(context, target, "getCurves", ECDH::GetCurves);
84
85
772
  ECDHBitsJob::Initialize(env, target);
86
772
  ECKeyPairGenJob::Initialize(env, target);
87
772
  ECKeyExportJob::Initialize(env, target);
88
89
2316
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
90
1544
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
91
772
}
92
93
5345
void ECDH::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
94
5345
  registry->Register(New);
95
5345
  registry->Register(GenerateKeys);
96
5345
  registry->Register(ComputeSecret);
97
5345
  registry->Register(GetPublicKey);
98
5345
  registry->Register(GetPrivateKey);
99
5345
  registry->Register(SetPublicKey);
100
5345
  registry->Register(SetPrivateKey);
101
5345
  registry->Register(ECDH::ConvertKey);
102
5345
  registry->Register(ECDH::GetCurves);
103
104
5345
  ECDHBitsJob::RegisterExternalReferences(registry);
105
5345
  ECKeyPairGenJob::RegisterExternalReferences(registry);
106
5345
  ECKeyExportJob::RegisterExternalReferences(registry);
107
5345
}
108
109
6
void ECDH::GetCurves(const FunctionCallbackInfo<Value>& args) {
110
6
  Environment* env = Environment::GetCurrent(args);
111
6
  const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
112
113
6
  if (num_curves) {
114
6
    std::vector<EC_builtin_curve> curves(num_curves);
115
116
6
    if (EC_get_builtin_curves(curves.data(), num_curves)) {
117
6
      std::vector<Local<Value>> arr(num_curves);
118
119
498
      for (size_t i = 0; i < num_curves; i++)
120
984
        arr[i] = OneByteString(env->isolate(), OBJ_nid2sn(curves[i].nid));
121
122
6
      args.GetReturnValue().Set(
123
          Array::New(env->isolate(), arr.data(), arr.size()));
124
6
      return;
125
    }
126
  }
127
128
  args.GetReturnValue().Set(Array::New(env->isolate()));
129
}
130
131
10
ECDH::ECDH(Environment* env, Local<Object> wrap, ECKeyPointer&& key)
132
    : BaseObject(env, wrap),
133
10
    key_(std::move(key)),
134
10
    group_(EC_KEY_get0_group(key_.get())) {
135
10
  MakeWeak();
136
10
  CHECK_NOT_NULL(group_);
137
10
}
138
139
void ECDH::MemoryInfo(MemoryTracker* tracker) const {
140
  tracker->TrackFieldWithSize("key", key_ ? kSizeOf_EC_KEY : 0);
141
}
142
143
40
ECDH::~ECDH() {}
144
145
10
void ECDH::New(const FunctionCallbackInfo<Value>& args) {
146
10
  Environment* env = Environment::GetCurrent(args);
147
148
10
  MarkPopErrorOnReturn mark_pop_error_on_return;
149
150
  // TODO(indutny): Support raw curves?
151
20
  CHECK(args[0]->IsString());
152
10
  node::Utf8Value curve(env->isolate(), args[0]);
153
154
10
  int nid = OBJ_sn2nid(*curve);
155
10
  if (nid == NID_undef)
156
    return THROW_ERR_CRYPTO_INVALID_CURVE(env);
157
158
10
  ECKeyPointer key(EC_KEY_new_by_curve_name(nid));
159
10
  if (!key)
160
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
161
      "Failed to create key using named curve");
162
163
10
  new ECDH(env, args.This(), std::move(key));
164
}
165
166
5
void ECDH::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
167
5
  Environment* env = Environment::GetCurrent(args);
168
169
  ECDH* ecdh;
170
5
  ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
171
172
5
  if (!EC_KEY_generate_key(ecdh->key_.get()))
173
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to generate key");
174
}
175
176
30
ECPointPointer ECDH::BufferToPoint(Environment* env,
177
                                   const EC_GROUP* group,
178
                                   Local<Value> buf) {
179
  int r;
180
181
60
  ECPointPointer pub(EC_POINT_new(group));
182
30
  if (!pub) {
183
    THROW_ERR_CRYPTO_OPERATION_FAILED(env,
184
        "Failed to allocate EC_POINT for a public key");
185
    return pub;
186
  }
187
188
30
  ArrayBufferOrViewContents<unsigned char> input(buf);
189
30
  if (UNLIKELY(!input.CheckSizeInt32())) {
190
    THROW_ERR_OUT_OF_RANGE(env, "buffer is too big");
191
    return ECPointPointer();
192
  }
193
60
  r = EC_POINT_oct2point(
194
      group,
195
      pub.get(),
196
30
      input.data(),
197
      input.size(),
198
      nullptr);
199
30
  if (!r)
200
4
    return ECPointPointer();
201
202
26
  return pub;
203
}
204
205
7
void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
206
7
  Environment* env = Environment::GetCurrent(args);
207
208
7
  CHECK(IsAnyByteSource(args[0]));
209
210
  ECDH* ecdh;
211
10
  ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
212
213
7
  MarkPopErrorOnReturn mark_pop_error_on_return;
214
215
7
  if (!ecdh->IsKeyPairValid())
216
1
    return THROW_ERR_CRYPTO_INVALID_KEYPAIR(env);
217
218
  ECPointPointer pub(
219
      ECDH::BufferToPoint(env,
220
                          ecdh->group_,
221
6
                          args[0]));
222
6
  if (!pub) {
223
2
    args.GetReturnValue().Set(
224
        FIXED_ONE_BYTE_STRING(env->isolate(),
225
        "ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY"));
226
2
    return;
227
  }
228
229
4
  std::unique_ptr<BackingStore> bs;
230
  {
231
4
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
232
    // NOTE: field_size is in bits
233
4
    int field_size = EC_GROUP_get_degree(ecdh->group_);
234
4
    size_t out_len = (field_size + 7) / 8;
235
4
    bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len);
236
  }
237
238
4
  if (!ECDH_compute_key(
239
4
          bs->Data(), bs->ByteLength(), pub.get(), ecdh->key_.get(), nullptr))
240
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to compute ECDH key");
241
242
4
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
243
  Local<Value> buffer;
244
8
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
245
8
  args.GetReturnValue().Set(buffer);
246
}
247
248
21
void ECDH::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
249
21
  Environment* env = Environment::GetCurrent(args);
250
251
  // Conversion form
252
21
  CHECK_EQ(args.Length(), 1);
253
254
  ECDH* ecdh;
255
22
  ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
256
257
21
  const EC_GROUP* group = EC_KEY_get0_group(ecdh->key_.get());
258
21
  const EC_POINT* pub = EC_KEY_get0_public_key(ecdh->key_.get());
259
21
  if (pub == nullptr)
260
1
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
261
1
        "Failed to get ECDH public key");
262
263
20
  CHECK(args[0]->IsUint32());
264
40
  uint32_t val = args[0].As<Uint32>()->Value();
265
20
  point_conversion_form_t form = static_cast<point_conversion_form_t>(val);
266
267
  const char* error;
268
  Local<Object> buf;
269
40
  if (!ECPointToBuffer(env, group, pub, form, &error).ToLocal(&buf))
270
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env, error);
271
40
  args.GetReturnValue().Set(buf);
272
}
273
274
7
void ECDH::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
275
7
  Environment* env = Environment::GetCurrent(args);
276
277
  ECDH* ecdh;
278
8
  ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
279
280
7
  const BIGNUM* b = EC_KEY_get0_private_key(ecdh->key_.get());
281
7
  if (b == nullptr)
282
1
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
283
1
        "Failed to get ECDH private key");
284
285
6
  std::unique_ptr<BackingStore> bs;
286
  {
287
6
    NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
288
6
    bs = ArrayBuffer::NewBackingStore(env->isolate(), BN_num_bytes(b));
289
  }
290
6
  CHECK_EQ(static_cast<int>(bs->ByteLength()),
291
           BN_bn2binpad(
292
               b, static_cast<unsigned char*>(bs->Data()), bs->ByteLength()));
293
294
6
  Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs));
295
  Local<Value> buffer;
296
12
  if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&buffer)) return;
297
12
  args.GetReturnValue().Set(buffer);
298
}
299
300
7
void ECDH::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
301
7
  Environment* env = Environment::GetCurrent(args);
302
303
  ECDH* ecdh;
304
10
  ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
305
306
7
  ArrayBufferOrViewContents<unsigned char> priv_buffer(args[0]);
307
7
  if (UNLIKELY(!priv_buffer.CheckSizeInt32()))
308
    return THROW_ERR_OUT_OF_RANGE(env, "key is too big");
309
310
  BignumPointer priv(BN_bin2bn(
311
7
      priv_buffer.data(), priv_buffer.size(), nullptr));
312
7
  if (!priv) {
313
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
314
        "Failed to convert Buffer to BN");
315
  }
316
317
7
  if (!ecdh->IsKeyValidForCurve(priv)) {
318
3
    return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env,
319
3
        "Private key is not valid for specified curve.");
320
  }
321
322
4
  ECKeyPointer new_key(EC_KEY_dup(ecdh->key_.get()));
323
4
  CHECK(new_key);
324
325
4
  int result = EC_KEY_set_private_key(new_key.get(), priv.get());
326
4
  priv.reset();
327
328
4
  if (!result) {
329
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
330
        "Failed to convert BN to a private key");
331
  }
332
333
4
  MarkPopErrorOnReturn mark_pop_error_on_return;
334
4
  USE(&mark_pop_error_on_return);
335
336
4
  const BIGNUM* priv_key = EC_KEY_get0_private_key(new_key.get());
337
4
  CHECK_NOT_NULL(priv_key);
338
339
4
  ECPointPointer pub(EC_POINT_new(ecdh->group_));
340
4
  CHECK(pub);
341
342
4
  if (!EC_POINT_mul(ecdh->group_, pub.get(), priv_key,
343
                    nullptr, nullptr, nullptr)) {
344
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
345
        "Failed to generate ECDH public key");
346
  }
347
348
4
  if (!EC_KEY_set_public_key(new_key.get(), pub.get()))
349
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
350
        "Failed to set generated public key");
351
352
4
  ecdh->key_ = std::move(new_key);
353
4
  ecdh->group_ = EC_KEY_get0_group(ecdh->key_.get());
354
}
355
356
5
void ECDH::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
357
5
  Environment* env = Environment::GetCurrent(args);
358
359
  ECDH* ecdh;
360
6
  ASSIGN_OR_RETURN_UNWRAP(&ecdh, args.Holder());
361
362
5
  CHECK(IsAnyByteSource(args[0]));
363
364
5
  MarkPopErrorOnReturn mark_pop_error_on_return;
365
366
  ECPointPointer pub(
367
      ECDH::BufferToPoint(env,
368
                          ecdh->group_,
369
5
                          args[0]));
370
5
  if (!pub) {
371
1
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
372
1
        "Failed to convert Buffer to EC_POINT");
373
  }
374
375
4
  int r = EC_KEY_set_public_key(ecdh->key_.get(), pub.get());
376
4
  if (!r) {
377
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
378
        "Failed to set EC_POINT as the public key");
379
  }
380
}
381
382
7
bool ECDH::IsKeyValidForCurve(const BignumPointer& private_key) {
383
7
  CHECK(group_);
384
7
  CHECK(private_key);
385
  // Private keys must be in the range [1, n-1].
386
  // Ref: Section 3.2.1 - http://www.secg.org/sec1-v2.pdf
387
7
  if (BN_cmp(private_key.get(), BN_value_one()) < 0) {
388
1
    return false;
389
  }
390
6
  BignumPointer order(BN_new());
391
6
  CHECK(order);
392

12
  return EC_GROUP_get_order(group_, order.get(), nullptr) &&
393
12
         BN_cmp(private_key.get(), order.get()) < 0;
394
}
395
396
7
bool ECDH::IsKeyPairValid() {
397
7
  MarkPopErrorOnReturn mark_pop_error_on_return;
398
7
  USE(&mark_pop_error_on_return);
399
7
  return 1 == EC_KEY_check_key(key_.get());
400
}
401
402
// Convert the input public key to compressed, uncompressed, or hybrid formats.
403
8
void ECDH::ConvertKey(const FunctionCallbackInfo<Value>& args) {
404
8
  MarkPopErrorOnReturn mark_pop_error_on_return;
405
8
  Environment* env = Environment::GetCurrent(args);
406
407
8
  CHECK_EQ(args.Length(), 3);
408
8
  CHECK(IsAnyByteSource(args[0]));
409
410
8
  ArrayBufferOrViewContents<char> args0(args[0]);
411
8
  if (UNLIKELY(!args0.CheckSizeInt32()))
412
    return THROW_ERR_OUT_OF_RANGE(env, "key is too big");
413
8
  if (args0.size() == 0)
414
    return args.GetReturnValue().SetEmptyString();
415
416
8
  node::Utf8Value curve(env->isolate(), args[1]);
417
418
8
  int nid = OBJ_sn2nid(*curve);
419
8
  if (nid == NID_undef)
420
1
    return THROW_ERR_CRYPTO_INVALID_CURVE(env);
421
422
  ECGroupPointer group(
423
7
      EC_GROUP_new_by_curve_name(nid));
424
7
  if (group == nullptr)
425
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get EC_GROUP");
426
427
  ECPointPointer pub(
428
      ECDH::BufferToPoint(env,
429
7
                          group.get(),
430
7
                          args[0]));
431
432
7
  if (pub == nullptr) {
433
1
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
434
1
        "Failed to convert Buffer to EC_POINT");
435
  }
436
437
6
  CHECK(args[2]->IsUint32());
438
12
  uint32_t val = args[2].As<Uint32>()->Value();
439
6
  point_conversion_form_t form = static_cast<point_conversion_form_t>(val);
440
441
  const char* error;
442
  Local<Object> buf;
443
12
  if (!ECPointToBuffer(env, group.get(), pub.get(), form, &error).ToLocal(&buf))
444
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env, error);
445
12
  args.GetReturnValue().Set(buf);
446
}
447
448
void ECDHBitsConfig::MemoryInfo(MemoryTracker* tracker) const {
449
  tracker->TrackField("public", public_);
450
  tracker->TrackField("private", private_);
451
}
452
453
114
Maybe<bool> ECDHBitsTraits::EncodeOutput(
454
    Environment* env,
455
    const ECDHBitsConfig& params,
456
    ByteSource* out,
457
    v8::Local<v8::Value>* result) {
458
228
  *result = out->ToArrayBuffer(env);
459
114
  return Just(!result->IsEmpty());
460
}
461
462
114
Maybe<bool> ECDHBitsTraits::AdditionalConfig(
463
    CryptoJobMode mode,
464
    const FunctionCallbackInfo<Value>& args,
465
    unsigned int offset,
466
    ECDHBitsConfig* params) {
467
114
  Environment* env = Environment::GetCurrent(args);
468
469

342
  CHECK(args[offset]->IsString());  // curve name
470

228
  CHECK(args[offset + 1]->IsObject());  // public key
471

228
  CHECK(args[offset + 2]->IsObject());  // private key
472
473
  KeyObjectHandle* private_key;
474
  KeyObjectHandle* public_key;
475
476
342
  Utf8Value name(env->isolate(), args[offset]);
477
478

228
  ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset + 1], Nothing<bool>());
479

228
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 2], Nothing<bool>());
480
481

228
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
482
114
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
483
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
484
    return Nothing<bool>();
485
  }
486
487
114
  params->id_ = GetOKPCurveFromName(*name);
488
114
  params->private_ = private_key->Data();
489
114
  params->public_ = public_key->Data();
490
491
114
  return Just(true);
492
}
493
494
114
bool ECDHBitsTraits::DeriveBits(Environment* env,
495
                                const ECDHBitsConfig& params,
496
                                ByteSource* out) {
497
114
  size_t len = 0;
498
228
  ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
499
228
  ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
500
501
114
  switch (params.id_) {
502
40
    case EVP_PKEY_X25519:
503
      // Fall through
504
    case EVP_PKEY_X448: {
505
40
      EVPKeyCtxPointer ctx = nullptr;
506
      {
507
40
        ctx.reset(EVP_PKEY_CTX_new(m_privkey.get(), nullptr));
508
      }
509
40
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
510
80
      if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
511
40
          EVP_PKEY_derive_set_peer(
512
              ctx.get(),
513

80
              m_pubkey.get()) <= 0 ||
514
40
          EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
515
        return false;
516
      }
517
518
40
      ByteSource::Builder buf(len);
519
520
40
      if (EVP_PKEY_derive(ctx.get(), buf.data<unsigned char>(), &len) <= 0) {
521
        return false;
522
      }
523
524
40
      *out = std::move(buf).release(len);
525
526
40
      break;
527
    }
528
74
    default: {
529
      const EC_KEY* private_key;
530
      {
531
74
        Mutex::ScopedLock priv_lock(*m_privkey.mutex());
532
74
        private_key = EVP_PKEY_get0_EC_KEY(m_privkey.get());
533
      }
534
535
74
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
536
74
      const EC_KEY* public_key = EVP_PKEY_get0_EC_KEY(m_pubkey.get());
537
538
74
      const EC_GROUP* group = EC_KEY_get0_group(private_key);
539
74
      if (group == nullptr)
540
        return false;
541
542
74
      CHECK_EQ(EC_KEY_check_key(private_key), 1);
543
74
      CHECK_EQ(EC_KEY_check_key(public_key), 1);
544
74
      const EC_POINT* pub = EC_KEY_get0_public_key(public_key);
545
74
      int field_size = EC_GROUP_get_degree(group);
546
74
      len = (field_size + 7) / 8;
547
74
      ByteSource::Builder buf(len);
548
74
      CHECK_NOT_NULL(pub);
549
74
      CHECK_NOT_NULL(private_key);
550
74
      if (ECDH_compute_key(buf.data<char>(), len, pub, private_key, nullptr) <=
551
          0) {
552
        return false;
553
      }
554
555
148
      *out = std::move(buf).release();
556
    }
557
  }
558
559
114
  return true;
560
}
561
562
227
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
563
227
  EVPKeyCtxPointer key_ctx;
564
227
  switch (params->params.curve_nid) {
565
    case EVP_PKEY_ED25519:
566
      // Fall through
567
    case EVP_PKEY_ED448:
568
      // Fall through
569
    case EVP_PKEY_X25519:
570
      // Fall through
571
    case EVP_PKEY_X448:
572
      key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr));
573
      break;
574
227
    default: {
575
227
      EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
576
227
      EVP_PKEY* raw_params = nullptr;
577
454
      if (!param_ctx ||
578
454
          EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
579
227
          EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
580
227
              param_ctx.get(), params->params.curve_nid) <= 0 ||
581
227
          EVP_PKEY_CTX_set_ec_param_enc(
582

454
              param_ctx.get(), params->params.param_encoding) <= 0 ||
583
227
          EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
584
        return EVPKeyCtxPointer();
585
      }
586
454
      EVPKeyPointer key_params(raw_params);
587
454
      key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
588
    }
589
  }
590
591

227
  if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
592
    key_ctx.reset();
593
594
227
  return key_ctx;
595
}
596
597
// EcKeyPairGenJob input arguments
598
//   1. CryptoJobMode
599
//   2. Curve Name
600
//   3. Param Encoding
601
//   4. Public Format
602
//   5. Public Type
603
//   6. Private Format
604
//   7. Private Type
605
//   8. Cipher
606
//   9. Passphrase
607
228
Maybe<bool> EcKeyGenTraits::AdditionalConfig(
608
    CryptoJobMode mode,
609
    const FunctionCallbackInfo<Value>& args,
610
    unsigned int* offset,
611
    EcKeyPairGenConfig* params) {
612
228
  Environment* env = Environment::GetCurrent(args);
613

684
  CHECK(args[*offset]->IsString());  // curve name
614

456
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
615
616
684
  Utf8Value curve_name(env->isolate(), args[*offset]);
617
228
  params->params.curve_nid = GetCurveFromName(*curve_name);
618
228
  if (params->params.curve_nid == NID_undef) {
619
1
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
620
1
    return Nothing<bool>();
621
  }
622
623
681
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
624
227
  if (params->params.param_encoding != OPENSSL_EC_NAMED_CURVE &&
625
3
      params->params.param_encoding != OPENSSL_EC_EXPLICIT_CURVE) {
626
    THROW_ERR_OUT_OF_RANGE(env, "Invalid param_encoding specified");
627
    return Nothing<bool>();
628
  }
629
630
227
  *offset += 2;
631
632
227
  return Just(true);
633
}
634
635
namespace {
636
WebCryptoKeyExportStatus EC_Raw_Export(
637
    KeyObjectData* key_data,
638
    const ECKeyExportConfig& params,
639
    ByteSource* out) {
640
  ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
641
  CHECK(m_pkey);
642
  Mutex::ScopedLock lock(*m_pkey.mutex());
643
644
  const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
645
646
  size_t len = 0;
647
648
  if (ec_key == nullptr) {
649
    typedef int (*export_fn)(const EVP_PKEY*, unsigned char*, size_t* len);
650
    export_fn fn = nullptr;
651
    switch (key_data->GetKeyType()) {
652
      case kKeyTypePrivate:
653
        fn = EVP_PKEY_get_raw_private_key;
654
        break;
655
      case kKeyTypePublic:
656
        fn = EVP_PKEY_get_raw_public_key;
657
        break;
658
      case kKeyTypeSecret:
659
        UNREACHABLE();
660
    }
661
    CHECK_NOT_NULL(fn);
662
    // Get the size of the raw key data
663
    if (fn(m_pkey.get(), nullptr, &len) == 0)
664
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
665
    ByteSource::Builder data(len);
666
    if (fn(m_pkey.get(), data.data<unsigned char>(), &len) == 0)
667
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
668
    *out = std::move(data).release(len);
669
  } else {
670
    if (key_data->GetKeyType() != kKeyTypePublic)
671
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
672
    const EC_GROUP* group = EC_KEY_get0_group(ec_key);
673
    const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
674
    point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
675
676
    // Get the allocated data size...
677
    len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr);
678
    if (len == 0)
679
      return WebCryptoKeyExportStatus::FAILED;
680
    ByteSource::Builder data(len);
681
    size_t check_len = EC_POINT_point2oct(
682
        group, point, form, data.data<unsigned char>(), len, nullptr);
683
    if (check_len == 0)
684
      return WebCryptoKeyExportStatus::FAILED;
685
686
    CHECK_EQ(len, check_len);
687
    *out = std::move(data).release();
688
  }
689
690
  return WebCryptoKeyExportStatus::OK;
691
}
692
}  // namespace
693
694
168
Maybe<bool> ECKeyExportTraits::AdditionalConfig(
695
    const FunctionCallbackInfo<Value>& args,
696
    unsigned int offset,
697
    ECKeyExportConfig* params) {
698
168
  return Just(true);
699
}
700
701
168
WebCryptoKeyExportStatus ECKeyExportTraits::DoExport(
702
    std::shared_ptr<KeyObjectData> key_data,
703
    WebCryptoKeyFormat format,
704
    const ECKeyExportConfig& params,
705
    ByteSource* out) {
706
168
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
707
708

168
  switch (format) {
709
    case kWebCryptoKeyFormatRaw:
710
      return EC_Raw_Export(key_data.get(), params, out);
711
91
    case kWebCryptoKeyFormatPKCS8:
712
91
      if (key_data->GetKeyType() != kKeyTypePrivate)
713
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
714
91
      return PKEY_PKCS8_Export(key_data.get(), out);
715
77
    case kWebCryptoKeyFormatSPKI:
716
77
      if (key_data->GetKeyType() != kKeyTypePublic)
717
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
718
77
      return PKEY_SPKI_Export(key_data.get(), out);
719
    default:
720
      UNREACHABLE();
721
  }
722
}
723
724
199
Maybe<void> ExportJWKEcKey(
725
    Environment* env,
726
    std::shared_ptr<KeyObjectData> key,
727
    Local<Object> target) {
728
398
  ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
729
398
  Mutex::ScopedLock lock(*m_pkey.mutex());
730
199
  CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
731
732
199
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
733
199
  CHECK_NOT_NULL(ec);
734
735
199
  const EC_POINT* pub = EC_KEY_get0_public_key(ec);
736
199
  const EC_GROUP* group = EC_KEY_get0_group(ec);
737
738
199
  int degree_bits = EC_GROUP_get_degree(group);
739
199
  int degree_bytes =
740
199
    (degree_bits / CHAR_BIT) + (7 + (degree_bits % CHAR_BIT)) / 8;
741
742
398
  BignumPointer x(BN_new());
743
398
  BignumPointer y(BN_new());
744
745
199
  if (!EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr)) {
746
    ThrowCryptoError(env, ERR_get_error(),
747
                     "Failed to get elliptic-curve point coordinates");
748
    return Nothing<void>();
749
  }
750
751
398
  if (target->Set(
752
          env->context(),
753
          env->jwk_kty_string(),
754
796
          env->jwk_ec_string()).IsNothing()) {
755
    return Nothing<void>();
756
  }
757
758
199
  if (SetEncodedValue(
759
          env,
760
          target,
761
          env->jwk_x_string(),
762
199
          x.get(),
763
597
          degree_bytes).IsNothing() ||
764
199
      SetEncodedValue(
765
          env,
766
          target,
767
          env->jwk_y_string(),
768
199
          y.get(),
769
398
          degree_bytes).IsNothing()) {
770
    return Nothing<void>();
771
  }
772
773
  Local<String> crv_name;
774
199
  const int nid = EC_GROUP_get_curve_name(group);
775

199
  switch (nid) {
776
97
    case NID_X9_62_prime256v1:
777
97
      crv_name = OneByteString(env->isolate(), "P-256");
778
97
      break;
779
7
    case NID_secp256k1:
780
7
      crv_name = OneByteString(env->isolate(), "secp256k1");
781
7
      break;
782
75
    case NID_secp384r1:
783
75
      crv_name = OneByteString(env->isolate(), "P-384");
784
75
      break;
785
17
    case NID_secp521r1:
786
17
      crv_name = OneByteString(env->isolate(), "P-521");
787
17
      break;
788
3
    default: {
789
3
      THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
790
3
          env, "Unsupported JWK EC curve: %s.", OBJ_nid2sn(nid));
791
3
      return Nothing<void>();
792
    }
793
  }
794
392
  if (target->Set(
795
      env->context(),
796
      env->jwk_crv_string(),
797
588
      crv_name).IsNothing()) {
798
    return Nothing<void>();
799
  }
800
801
196
  if (key->GetKeyType() == kKeyTypePrivate) {
802
111
    const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
803
111
    return SetEncodedValue(
804
      env,
805
      target,
806
      env->jwk_d_string(),
807
      pvt,
808
222
      degree_bytes).IsJust() ? JustVoid() : Nothing<void>();
809
  }
810
811
85
  return JustVoid();
812
}
813
814
48
Maybe<bool> ExportJWKEdKey(
815
    Environment* env,
816
    std::shared_ptr<KeyObjectData> key,
817
    Local<Object> target) {
818
96
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
819
96
  Mutex::ScopedLock lock(*pkey.mutex());
820
821
48
  const char* curve = nullptr;
822

48
  switch (EVP_PKEY_id(pkey.get())) {
823
11
    case EVP_PKEY_ED25519:
824
11
      curve = "Ed25519";
825
11
      break;
826
11
    case EVP_PKEY_ED448:
827
11
      curve = "Ed448";
828
11
      break;
829
13
    case EVP_PKEY_X25519:
830
13
      curve = "X25519";
831
13
      break;
832
13
    case EVP_PKEY_X448:
833
13
      curve = "X448";
834
13
      break;
835
    default:
836
      UNREACHABLE();
837
  }
838
96
  if (target->Set(
839
          env->context(),
840
          env->jwk_crv_string(),
841
192
          OneByteString(env->isolate(), curve)).IsNothing()) {
842
    return Nothing<bool>();
843
  }
844
845
48
  size_t len = 0;
846
  Local<Value> encoded;
847
  Local<Value> error;
848
849
48
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
850
    return Nothing<bool>();
851
852
96
  ByteSource::Builder out(len);
853
854
48
  if (key->GetKeyType() == kKeyTypePrivate) {
855
48
    if (!EVP_PKEY_get_raw_private_key(
856
24
            pkey.get(), out.data<unsigned char>(), &len) ||
857
48
        !StringBytes::Encode(
858
24
             env->isolate(), out.data<const char>(), len, BASE64URL, &error)
859

72
             .ToLocal(&encoded) ||
860

96
        !target->Set(env->context(), env->jwk_d_string(), encoded).IsJust()) {
861
      if (!error.IsEmpty())
862
        env->isolate()->ThrowException(error);
863
      return Nothing<bool>();
864
    }
865
  }
866
867
96
  if (!EVP_PKEY_get_raw_public_key(
868
48
          pkey.get(), out.data<unsigned char>(), &len) ||
869
96
      !StringBytes::Encode(
870
48
           env->isolate(), out.data<const char>(), len, BASE64URL, &error)
871

144
           .ToLocal(&encoded) ||
872

192
      !target->Set(env->context(), env->jwk_x_string(), encoded).IsJust()) {
873
    if (!error.IsEmpty())
874
      env->isolate()->ThrowException(error);
875
    return Nothing<bool>();
876
  }
877
878
96
  if (target->Set(
879
          env->context(),
880
          env->jwk_kty_string(),
881
192
          env->jwk_okp_string()).IsNothing()) {
882
    return Nothing<bool>();
883
  }
884
885
48
  return Just(true);
886
}
887
888
156
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
889
    Environment* env,
890
    Local<Object> jwk,
891
    const FunctionCallbackInfo<Value>& args,
892
    unsigned int offset) {
893

468
  CHECK(args[offset]->IsString());  // curve name
894
624
  Utf8Value curve(env->isolate(), args[offset].As<String>());
895
896
156
  int nid = GetCurveFromName(*curve);
897
156
  if (nid == NID_undef) {  // Unknown curve
898
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
899
    return std::shared_ptr<KeyObjectData>();
900
  }
901
902
  Local<Value> x_value;
903
  Local<Value> y_value;
904
  Local<Value> d_value;
905
906
312
  if (!jwk->Get(env->context(), env->jwk_x_string()).ToLocal(&x_value) ||
907

780
      !jwk->Get(env->context(), env->jwk_y_string()).ToLocal(&y_value) ||
908

624
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
909
    return std::shared_ptr<KeyObjectData>();
910
  }
911
912
156
  if (!x_value->IsString() ||
913

624
      !y_value->IsString() ||
914

482
      (!d_value->IsUndefined() && !d_value->IsString())) {
915
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
916
    return std::shared_ptr<KeyObjectData>();
917
  }
918
919
312
  KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic;
920
921
312
  ECKeyPointer ec(EC_KEY_new_by_curve_name(nid));
922
156
  if (!ec) {
923
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
924
    return std::shared_ptr<KeyObjectData>();
925
  }
926
927
312
  ByteSource x = ByteSource::FromEncodedString(env, x_value.As<String>());
928
312
  ByteSource y = ByteSource::FromEncodedString(env, y_value.As<String>());
929
930
312
  if (!EC_KEY_set_public_key_affine_coordinates(
931
          ec.get(),
932
312
          x.ToBN().get(),
933
312
          y.ToBN().get())) {
934
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
935
    return std::shared_ptr<KeyObjectData>();
936
  }
937
938
156
  if (type == kKeyTypePrivate) {
939
85
    ByteSource d = ByteSource::FromEncodedString(env, d_value.As<String>());
940
85
    if (!EC_KEY_set_private_key(ec.get(), d.ToBN().get())) {
941
      THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
942
      return std::shared_ptr<KeyObjectData>();
943
    }
944
  }
945
946
156
  EVPKeyPointer pkey(EVP_PKEY_new());
947
156
  CHECK_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()), 1);
948
949
156
  return KeyObjectData::CreateAsymmetric(type, ManagedEVPPKey(std::move(pkey)));
950
}
951
952
656
Maybe<bool> GetEcKeyDetail(
953
    Environment* env,
954
    std::shared_ptr<KeyObjectData> key,
955
    Local<Object> target) {
956
1312
  ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
957
656
  Mutex::ScopedLock lock(*m_pkey.mutex());
958
656
  CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
959
960
656
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
961
656
  CHECK_NOT_NULL(ec);
962
963
656
  const EC_GROUP* group = EC_KEY_get0_group(ec);
964
656
  int nid = EC_GROUP_get_curve_name(group);
965
966
  return target->Set(
967
      env->context(),
968
      env->named_curve_string(),
969
1968
      OneByteString(env->isolate(), OBJ_nid2sn(nid)));
970
}
971
972
// WebCrypto requires a different format for ECDSA signatures than
973
// what OpenSSL produces, so we need to convert between them. The
974
// implementation here is a adapted from Chromium's impl here:
975
// https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc
976
977
size_t GroupOrderSize(const ManagedEVPPKey& key) {
978
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
979
  CHECK_NOT_NULL(ec);
980
  const EC_GROUP* group = EC_KEY_get0_group(ec);
981
  BignumPointer order(BN_new());
982
  CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
983
  return BN_num_bytes(order.get());
984
}
985
}  // namespace crypto
986
}  // namespace node