GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 443 543 81.6 %
Date: 2022-02-22 04:15:11 Branches: 192 344 55.8 %

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

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

228
  CHECK(args[offset]->IsString());  // curve name
451

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

152
  CHECK(args[offset + 2]->IsObject());  // private key
453
454
  KeyObjectHandle* private_key;
455
  KeyObjectHandle* public_key;
456
457
228
  Utf8Value name(env->isolate(), args[offset]);
458
459

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

152
  ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 2], Nothing<bool>());
461
462

152
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
463
76
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
464
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
465
    return Nothing<bool>();
466
  }
467
468
76
  params->id_ = GetOKPCurveFromName(*name);
469
76
  params->private_ = private_key->Data();
470
76
  params->public_ = public_key->Data();
471
472
76
  return Just(true);
473
}
474
475
76
bool ECDHBitsTraits::DeriveBits(
476
    Environment* env,
477
    const ECDHBitsConfig& params,
478
    ByteSource* out) {
479
480
76
  char* data = nullptr;
481
76
  size_t len = 0;
482
152
  ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
483
152
  ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
484
485
76
  switch (params.id_) {
486
8
    case EVP_PKEY_X25519:
487
      // Fall through
488
    case EVP_PKEY_X448: {
489
8
      EVPKeyCtxPointer ctx = nullptr;
490
      {
491
8
        ctx.reset(EVP_PKEY_CTX_new(m_privkey.get(), nullptr));
492
      }
493
8
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
494
16
      if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
495
8
          EVP_PKEY_derive_set_peer(
496
              ctx.get(),
497

16
              m_pubkey.get()) <= 0 ||
498
8
          EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
499
        return false;
500
      }
501
502
8
      data = MallocOpenSSL<char>(len);
503
504
8
      if (EVP_PKEY_derive(
505
              ctx.get(),
506
              reinterpret_cast<unsigned char*>(data),
507
8
              &len) <= 0) {
508
        return false;
509
      }
510
511
8
      break;
512
    }
513
68
    default: {
514
      const EC_KEY* private_key;
515
      {
516
68
        Mutex::ScopedLock priv_lock(*m_privkey.mutex());
517
68
        private_key = EVP_PKEY_get0_EC_KEY(m_privkey.get());
518
      }
519
520
68
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
521
68
      const EC_KEY* public_key = EVP_PKEY_get0_EC_KEY(m_pubkey.get());
522
523
68
      const EC_GROUP* group = EC_KEY_get0_group(private_key);
524
68
      if (group == nullptr)
525
        return false;
526
527
68
      CHECK_EQ(EC_KEY_check_key(private_key), 1);
528
68
      CHECK_EQ(EC_KEY_check_key(public_key), 1);
529
68
      const EC_POINT* pub = EC_KEY_get0_public_key(public_key);
530
68
      int field_size = EC_GROUP_get_degree(group);
531
68
      len = (field_size + 7) / 8;
532
68
      data = MallocOpenSSL<char>(len);
533
68
      CHECK_NOT_NULL(data);
534
68
      CHECK_NOT_NULL(pub);
535
68
      CHECK_NOT_NULL(private_key);
536
68
      if (ECDH_compute_key(
537
              data,
538
              len,
539
              pub,
540
              private_key,
541
68
              nullptr) <= 0) {
542
        return false;
543
68
      }
544
    }
545
  }
546
76
  ByteSource buf = ByteSource::Allocated(data, len);
547
76
  *out = std::move(buf);
548
76
  return true;
549
}
550
551
100
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
552
100
  EVPKeyCtxPointer key_ctx;
553
100
  switch (params->params.curve_nid) {
554
    case EVP_PKEY_ED25519:
555
      // Fall through
556
    case EVP_PKEY_ED448:
557
      // Fall through
558
    case EVP_PKEY_X25519:
559
      // Fall through
560
    case EVP_PKEY_X448:
561
      key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr));
562
      break;
563
100
    default: {
564
100
      EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
565
100
      EVP_PKEY* raw_params = nullptr;
566
200
      if (!param_ctx ||
567
200
          EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
568
100
          EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
569
100
              param_ctx.get(), params->params.curve_nid) <= 0 ||
570
100
          EVP_PKEY_CTX_set_ec_param_enc(
571

200
              param_ctx.get(), params->params.param_encoding) <= 0 ||
572
100
          EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
573
        return EVPKeyCtxPointer();
574
      }
575
200
      EVPKeyPointer key_params(raw_params);
576
200
      key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
577
    }
578
  }
579
580

100
  if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
581
    key_ctx.reset();
582
583
100
  return key_ctx;
584
}
585
586
// EcKeyPairGenJob input arguments
587
//   1. CryptoJobMode
588
//   2. Curve Name
589
//   3. Param Encoding
590
//   4. Public Format
591
//   5. Public Type
592
//   6. Private Format
593
//   7. Private Type
594
//   8. Cipher
595
//   9. Passphrase
596
105
Maybe<bool> EcKeyGenTraits::AdditionalConfig(
597
    CryptoJobMode mode,
598
    const FunctionCallbackInfo<Value>& args,
599
    unsigned int* offset,
600
    EcKeyPairGenConfig* params) {
601
105
  Environment* env = Environment::GetCurrent(args);
602

315
  CHECK(args[*offset]->IsString());  // curve name
603

210
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
604
605
315
  Utf8Value curve_name(env->isolate(), args[*offset]);
606
105
  params->params.curve_nid = GetCurveFromName(*curve_name);
607
105
  if (params->params.curve_nid == NID_undef) {
608
5
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
609
5
    return Nothing<bool>();
610
  }
611
612
300
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
613
100
  if (params->params.param_encoding != OPENSSL_EC_NAMED_CURVE &&
614
3
      params->params.param_encoding != OPENSSL_EC_EXPLICIT_CURVE) {
615
    THROW_ERR_OUT_OF_RANGE(env, "Invalid param_encoding specified");
616
    return Nothing<bool>();
617
  }
618
619
100
  *offset += 2;
620
621
100
  return Just(true);
622
}
623
624
namespace {
625
7
WebCryptoKeyExportStatus EC_Raw_Export(
626
    KeyObjectData* key_data,
627
    const ECKeyExportConfig& params,
628
    ByteSource* out) {
629
14
  ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
630
7
  CHECK(m_pkey);
631
14
  Mutex::ScopedLock lock(*m_pkey.mutex());
632
633
7
  const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
634
635
  unsigned char* data;
636
7
  size_t len = 0;
637
638
7
  if (ec_key == nullptr) {
639
    typedef int (*export_fn)(const EVP_PKEY*, unsigned char*, size_t* len);
640
7
    export_fn fn = nullptr;
641

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

155
  switch (format) {
699
7
    case kWebCryptoKeyFormatRaw:
700
7
      return EC_Raw_Export(key_data.get(), params, out);
701
79
    case kWebCryptoKeyFormatPKCS8:
702
79
      if (key_data->GetKeyType() != kKeyTypePrivate)
703
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
704
79
      return PKEY_PKCS8_Export(key_data.get(), out);
705
69
    case kWebCryptoKeyFormatSPKI:
706
69
      if (key_data->GetKeyType() != kKeyTypePublic)
707
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
708
69
      return PKEY_SPKI_Export(key_data.get(), out);
709
    default:
710
      UNREACHABLE();
711
  }
712
}
713
714
199
Maybe<bool> ExportJWKEcKey(
715
    Environment* env,
716
    std::shared_ptr<KeyObjectData> key,
717
    Local<Object> target) {
718
398
  ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
719
398
  Mutex::ScopedLock lock(*m_pkey.mutex());
720
199
  CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
721
722
199
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
723
199
  CHECK_NOT_NULL(ec);
724
725
199
  const EC_POINT* pub = EC_KEY_get0_public_key(ec);
726
199
  const EC_GROUP* group = EC_KEY_get0_group(ec);
727
728
199
  int degree_bits = EC_GROUP_get_degree(group);
729
199
  int degree_bytes =
730
199
    (degree_bits / CHAR_BIT) + (7 + (degree_bits % CHAR_BIT)) / 8;
731
732
398
  BignumPointer x(BN_new());
733
398
  BignumPointer y(BN_new());
734
735
199
  EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr);
736
737
398
  if (target->Set(
738
          env->context(),
739
          env->jwk_kty_string(),
740
796
          env->jwk_ec_string()).IsNothing()) {
741
    return Nothing<bool>();
742
  }
743
744
199
  if (SetEncodedValue(
745
          env,
746
          target,
747
          env->jwk_x_string(),
748
199
          x.get(),
749
597
          degree_bytes).IsNothing() ||
750
199
      SetEncodedValue(
751
          env,
752
          target,
753
          env->jwk_y_string(),
754
199
          y.get(),
755
398
          degree_bytes).IsNothing()) {
756
    return Nothing<bool>();
757
  }
758
759
  Local<String> crv_name;
760
199
  const int nid = EC_GROUP_get_curve_name(group);
761

199
  switch (nid) {
762
97
    case NID_X9_62_prime256v1:
763
97
      crv_name = OneByteString(env->isolate(), "P-256");
764
97
      break;
765
7
    case NID_secp256k1:
766
7
      crv_name = OneByteString(env->isolate(), "secp256k1");
767
7
      break;
768
75
    case NID_secp384r1:
769
75
      crv_name = OneByteString(env->isolate(), "P-384");
770
75
      break;
771
17
    case NID_secp521r1:
772
17
      crv_name = OneByteString(env->isolate(), "P-521");
773
17
      break;
774
3
    default: {
775
3
      THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
776
3
          env, "Unsupported JWK EC curve: %s.", OBJ_nid2sn(nid));
777
3
      return Nothing<bool>();
778
    }
779
  }
780
392
  if (target->Set(
781
      env->context(),
782
      env->jwk_crv_string(),
783
588
      crv_name).IsNothing()) {
784
    return Nothing<bool>();
785
  }
786
787
196
  if (key->GetKeyType() == kKeyTypePrivate) {
788
111
    const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
789
    return SetEncodedValue(
790
      env,
791
      target,
792
      env->jwk_d_string(),
793
      pvt,
794
111
      degree_bytes);
795
  }
796
797
85
  return Just(true);
798
}
799
800
46
Maybe<bool> ExportJWKEdKey(
801
    Environment* env,
802
    std::shared_ptr<KeyObjectData> key,
803
    Local<Object> target) {
804
92
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
805
92
  Mutex::ScopedLock lock(*pkey.mutex());
806
807
46
  const char* curve = nullptr;
808

46
  switch (EVP_PKEY_id(pkey.get())) {
809
15
    case EVP_PKEY_ED25519:
810
15
      curve = "Ed25519";
811
15
      break;
812
13
    case EVP_PKEY_ED448:
813
13
      curve = "Ed448";
814
13
      break;
815
9
    case EVP_PKEY_X25519:
816
9
      curve = "X25519";
817
9
      break;
818
9
    case EVP_PKEY_X448:
819
9
      curve = "X448";
820
9
      break;
821
    default:
822
      UNREACHABLE();
823
  }
824
92
  if (target->Set(
825
          env->context(),
826
          env->jwk_crv_string(),
827
184
          OneByteString(env->isolate(), curve)).IsNothing()) {
828
    return Nothing<bool>();
829
  }
830
831
46
  size_t len = 0;
832
  Local<Value> encoded;
833
  Local<Value> error;
834
835
46
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
836
    return Nothing<bool>();
837
838
46
  unsigned char* data = MallocOpenSSL<unsigned char>(len);
839
92
  ByteSource out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
840
841
46
  if (key->GetKeyType() == kKeyTypePrivate) {
842
21
    if (!EVP_PKEY_get_raw_private_key(pkey.get(), data, &len) ||
843
42
        !StringBytes::Encode(
844
            env->isolate(),
845
            reinterpret_cast<const char*>(data),
846
            len,
847
            BASE64URL,
848

63
            &error).ToLocal(&encoded) ||
849
42
        !target->Set(
850
            env->context(),
851
            env->jwk_d_string(),
852

84
            encoded).IsJust()) {
853
      if (!error.IsEmpty())
854
        env->isolate()->ThrowException(error);
855
      return Nothing<bool>();
856
    }
857
  }
858
859
46
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), data, &len) ||
860
92
      !StringBytes::Encode(
861
          env->isolate(),
862
          reinterpret_cast<const char*>(data),
863
          len,
864
          BASE64URL,
865

138
          &error).ToLocal(&encoded) ||
866
92
      !target->Set(
867
          env->context(),
868
          env->jwk_x_string(),
869

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

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

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

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

624
      !y_value->IsString() ||
911

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