GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 428 551 77.7 %
Date: 2022-08-30 04:20:47 Branches: 185 346 53.5 %

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

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

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

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

228
  CHECK(args[offset + 2]->IsObject());  // private key
463
464
  KeyObjectHandle* private_key;
465
  KeyObjectHandle* public_key;
466
467
342
  Utf8Value name(env->isolate(), args[offset]);
468
469

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

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

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

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

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

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

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

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

168
  switch (format) {
700
    case kWebCryptoKeyFormatRaw:
701
      return EC_Raw_Export(key_data.get(), params, out);
702
91
    case kWebCryptoKeyFormatPKCS8:
703
91
      if (key_data->GetKeyType() != kKeyTypePrivate)
704
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
705
91
      return PKEY_PKCS8_Export(key_data.get(), out);
706
77
    case kWebCryptoKeyFormatSPKI:
707
77
      if (key_data->GetKeyType() != kKeyTypePublic)
708
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
709
77
      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
  if (!EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr)) {
737
    ThrowCryptoError(env, ERR_get_error(),
738
                     "Failed to get elliptic-curve point coordinates");
739
    return Nothing<void>();
740
  }
741
742
398
  if (target->Set(
743
          env->context(),
744
          env->jwk_kty_string(),
745
796
          env->jwk_ec_string()).IsNothing()) {
746
    return Nothing<void>();
747
  }
748
749
199
  if (SetEncodedValue(
750
          env,
751
          target,
752
          env->jwk_x_string(),
753
199
          x.get(),
754
597
          degree_bytes).IsNothing() ||
755
199
      SetEncodedValue(
756
          env,
757
          target,
758
          env->jwk_y_string(),
759
199
          y.get(),
760
398
          degree_bytes).IsNothing()) {
761
    return Nothing<void>();
762
  }
763
764
  Local<String> crv_name;
765
199
  const int nid = EC_GROUP_get_curve_name(group);
766

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

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

72
             .ToLocal(&encoded) ||
851

96
        !target->Set(env->context(), env->jwk_d_string(), encoded).IsJust()) {
852
      if (!error.IsEmpty())
853
        env->isolate()->ThrowException(error);
854
      return Nothing<bool>();
855
    }
856
  }
857
858
96
  if (!EVP_PKEY_get_raw_public_key(
859
48
          pkey.get(), out.data<unsigned char>(), &len) ||
860
96
      !StringBytes::Encode(
861
48
           env->isolate(), out.data<const char>(), len, BASE64URL, &error)
862

144
           .ToLocal(&encoded) ||
863

192
      !target->Set(env->context(), env->jwk_x_string(), encoded).IsJust()) {
864
    if (!error.IsEmpty())
865
      env->isolate()->ThrowException(error);
866
    return Nothing<bool>();
867
  }
868
869
96
  if (target->Set(
870
          env->context(),
871
          env->jwk_kty_string(),
872
192
          env->jwk_okp_string()).IsNothing()) {
873
    return Nothing<bool>();
874
  }
875
876
48
  return Just(true);
877
}
878
879
156
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
880
    Environment* env,
881
    Local<Object> jwk,
882
    const FunctionCallbackInfo<Value>& args,
883
    unsigned int offset) {
884

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

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

624
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
900
    return std::shared_ptr<KeyObjectData>();
901
  }
902
903
156
  if (!x_value->IsString() ||
904

624
      !y_value->IsString() ||
905

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