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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

624
      !y_value->IsString() ||
912

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