GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 450 552 81.5 %
Date: 2022-04-29 04:15:07 Branches: 196 352 55.7 %

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

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

246
  CHECK(args[offset]->IsString());  // curve name
466

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

164
  CHECK(args[offset + 2]->IsObject());  // private key
468
469
  KeyObjectHandle* private_key;
470
  KeyObjectHandle* public_key;
471
472
246
  Utf8Value name(env->isolate(), args[offset]);
473
474

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

164
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 2], Nothing<bool>());
476
477

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

16
              m_pubkey.get()) <= 0 ||
513
8
          EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
514
        return false;
515
      }
516
517
8
      data = MallocOpenSSL<char>(len);
518
519
8
      if (EVP_PKEY_derive(
520
              ctx.get(),
521
              reinterpret_cast<unsigned char*>(data),
522
8
              &len) <= 0) {
523
        return false;
524
      }
525
526
8
      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
      data = MallocOpenSSL<char>(len);
548
74
      CHECK_NOT_NULL(data);
549
74
      CHECK_NOT_NULL(pub);
550
74
      CHECK_NOT_NULL(private_key);
551
74
      if (ECDH_compute_key(
552
              data,
553
              len,
554
              pub,
555
              private_key,
556
74
              nullptr) <= 0) {
557
        return false;
558
74
      }
559
    }
560
  }
561
82
  ByteSource buf = ByteSource::Allocated(data, len);
562
82
  *out = std::move(buf);
563
82
  return true;
564
}
565
566
102
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
567
102
  EVPKeyCtxPointer key_ctx;
568
102
  switch (params->params.curve_nid) {
569
    case EVP_PKEY_ED25519:
570
      // Fall through
571
    case EVP_PKEY_ED448:
572
      // Fall through
573
    case EVP_PKEY_X25519:
574
      // Fall through
575
    case EVP_PKEY_X448:
576
      key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr));
577
      break;
578
102
    default: {
579
102
      EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
580
102
      EVP_PKEY* raw_params = nullptr;
581
204
      if (!param_ctx ||
582
204
          EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
583
102
          EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
584
102
              param_ctx.get(), params->params.curve_nid) <= 0 ||
585
102
          EVP_PKEY_CTX_set_ec_param_enc(
586

204
              param_ctx.get(), params->params.param_encoding) <= 0 ||
587
102
          EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
588
        return EVPKeyCtxPointer();
589
      }
590
204
      EVPKeyPointer key_params(raw_params);
591
204
      key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
592
    }
593
  }
594
595

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

321
  CHECK(args[*offset]->IsString());  // curve name
618

214
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
619
620
321
  Utf8Value curve_name(env->isolate(), args[*offset]);
621
107
  params->params.curve_nid = GetCurveFromName(*curve_name);
622
107
  if (params->params.curve_nid == NID_undef) {
623
5
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
624
5
    return Nothing<bool>();
625
  }
626
627
306
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
628
102
  if (params->params.param_encoding != OPENSSL_EC_NAMED_CURVE &&
629
3
      params->params.param_encoding != OPENSSL_EC_EXPLICIT_CURVE) {
630
    THROW_ERR_OUT_OF_RANGE(env, "Invalid param_encoding specified");
631
    return Nothing<bool>();
632
  }
633
634
102
  *offset += 2;
635
636
102
  return Just(true);
637
}
638
639
namespace {
640
7
WebCryptoKeyExportStatus EC_Raw_Export(
641
    KeyObjectData* key_data,
642
    const ECKeyExportConfig& params,
643
    ByteSource* out) {
644
14
  ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
645
7
  CHECK(m_pkey);
646
14
  Mutex::ScopedLock lock(*m_pkey.mutex());
647
648
7
  const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
649
650
  unsigned char* data;
651
7
  size_t len = 0;
652
653
7
  if (ec_key == nullptr) {
654
    typedef int (*export_fn)(const EVP_PKEY*, unsigned char*, size_t* len);
655
7
    export_fn fn = nullptr;
656

7
    switch (key_data->GetKeyType()) {
657
      case kKeyTypePrivate:
658
        fn = EVP_PKEY_get_raw_private_key;
659
        break;
660
7
      case kKeyTypePublic:
661
7
        fn = EVP_PKEY_get_raw_public_key;
662
7
        break;
663
      case kKeyTypeSecret:
664
        UNREACHABLE();
665
    }
666
7
    CHECK_NOT_NULL(fn);
667
    // Get the size of the raw key data
668
7
    if (fn(m_pkey.get(), nullptr, &len) == 0)
669
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
670
7
    data = MallocOpenSSL<unsigned char>(len);
671
7
    if (fn(m_pkey.get(), data, &len) == 0)
672
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
673
  } else {
674
    if (key_data->GetKeyType() != kKeyTypePublic)
675
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
676
    const EC_GROUP* group = EC_KEY_get0_group(ec_key);
677
    const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
678
    point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
679
680
    // Get the allocated data size...
681
    len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr);
682
    if (len == 0)
683
      return WebCryptoKeyExportStatus::FAILED;
684
    data = MallocOpenSSL<unsigned char>(len);
685
    size_t check_len =
686
        EC_POINT_point2oct(group, point, form, data, len, nullptr);
687
    if (check_len == 0)
688
      return WebCryptoKeyExportStatus::FAILED;
689
690
    CHECK_EQ(len, check_len);
691
  }
692
693
7
  *out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
694
695
7
  return WebCryptoKeyExportStatus::OK;
696
}
697
}  // namespace
698
699
155
Maybe<bool> ECKeyExportTraits::AdditionalConfig(
700
    const FunctionCallbackInfo<Value>& args,
701
    unsigned int offset,
702
    ECKeyExportConfig* params) {
703
155
  return Just(true);
704
}
705
706
155
WebCryptoKeyExportStatus ECKeyExportTraits::DoExport(
707
    std::shared_ptr<KeyObjectData> key_data,
708
    WebCryptoKeyFormat format,
709
    const ECKeyExportConfig& params,
710
    ByteSource* out) {
711
155
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
712
713

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

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

46
  switch (EVP_PKEY_id(pkey.get())) {
828
15
    case EVP_PKEY_ED25519:
829
15
      curve = "Ed25519";
830
15
      break;
831
13
    case EVP_PKEY_ED448:
832
13
      curve = "Ed448";
833
13
      break;
834
9
    case EVP_PKEY_X25519:
835
9
      curve = "X25519";
836
9
      break;
837
9
    case EVP_PKEY_X448:
838
9
      curve = "X448";
839
9
      break;
840
    default:
841
      UNREACHABLE();
842
  }
843
92
  if (target->Set(
844
          env->context(),
845
          env->jwk_crv_string(),
846
184
          OneByteString(env->isolate(), curve)).IsNothing()) {
847
    return Nothing<bool>();
848
  }
849
850
46
  size_t len = 0;
851
  Local<Value> encoded;
852
  Local<Value> error;
853
854
46
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
855
    return Nothing<bool>();
856
857
46
  unsigned char* data = MallocOpenSSL<unsigned char>(len);
858
92
  ByteSource out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
859
860
46
  if (key->GetKeyType() == kKeyTypePrivate) {
861
21
    if (!EVP_PKEY_get_raw_private_key(pkey.get(), data, &len) ||
862
42
        !StringBytes::Encode(
863
            env->isolate(),
864
            reinterpret_cast<const char*>(data),
865
            len,
866
            BASE64URL,
867

63
            &error).ToLocal(&encoded) ||
868
42
        !target->Set(
869
            env->context(),
870
            env->jwk_d_string(),
871

84
            encoded).IsJust()) {
872
      if (!error.IsEmpty())
873
        env->isolate()->ThrowException(error);
874
      return Nothing<bool>();
875
    }
876
  }
877
878
46
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), data, &len) ||
879
92
      !StringBytes::Encode(
880
          env->isolate(),
881
          reinterpret_cast<const char*>(data),
882
          len,
883
          BASE64URL,
884

138
          &error).ToLocal(&encoded) ||
885
92
      !target->Set(
886
          env->context(),
887
          env->jwk_x_string(),
888

184
          encoded).IsJust()) {
889
    if (!error.IsEmpty())
890
      env->isolate()->ThrowException(error);
891
    return Nothing<bool>();
892
  }
893
894
92
  if (target->Set(
895
          env->context(),
896
          env->jwk_kty_string(),
897
184
          env->jwk_okp_string()).IsNothing()) {
898
    return Nothing<bool>();
899
  }
900
901
46
  return Just(true);
902
}
903
904
156
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
905
    Environment* env,
906
    Local<Object> jwk,
907
    const FunctionCallbackInfo<Value>& args,
908
    unsigned int offset) {
909

468
  CHECK(args[offset]->IsString());  // curve name
910
624
  Utf8Value curve(env->isolate(), args[offset].As<String>());
911
912
156
  int nid = GetCurveFromName(*curve);
913
156
  if (nid == NID_undef) {  // Unknown curve
914
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
915
    return std::shared_ptr<KeyObjectData>();
916
  }
917
918
  Local<Value> x_value;
919
  Local<Value> y_value;
920
  Local<Value> d_value;
921
922
312
  if (!jwk->Get(env->context(), env->jwk_x_string()).ToLocal(&x_value) ||
923

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

624
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
925
    return std::shared_ptr<KeyObjectData>();
926
  }
927
928
156
  if (!x_value->IsString() ||
929

624
      !y_value->IsString() ||
930

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