GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 430 552 77.9 %
Date: 2022-06-12 04:16:28 Branches: 189 352 53.7 %

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

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

294
  CHECK(args[offset]->IsString());  // curve name
465

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

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

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

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

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

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

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

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

291
  CHECK(args[*offset]->IsString());  // curve name
617

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

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

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

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

48
            &error).ToLocal(&encoded) ||
867
32
        !target->Set(
868
            env->context(),
869
            env->jwk_d_string(),
870

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

108
          &error).ToLocal(&encoded) ||
884
72
      !target->Set(
885
          env->context(),
886
          env->jwk_x_string(),
887

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

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

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

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

624
      !y_value->IsString() ||
929

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