GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/crypto/crypto_ec.cc Lines: 393 479 82.0 %
Date: 2021-04-25 04:11:48 Branches: 212 365 58.1 %

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

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

56
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
447
28
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
448
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
449
    return Nothing<bool>();
450
  }
451
452
28
  params->id_ = GetOKPCurveFromName(*name);
453
28
  params->private_ = private_key->Data();
454
28
  params->public_ = public_key->Data();
455
456
28
  return Just(true);
457
}
458
459
28
bool ECDHBitsTraits::DeriveBits(
460
    Environment* env,
461
    const ECDHBitsConfig& params,
462
    ByteSource* out) {
463
464
28
  char* data = nullptr;
465
28
  size_t len = 0;
466
56
  ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
467
56
  ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
468
469
28
  switch (params.id_) {
470
    case EVP_PKEY_X25519:
471
      // Fall through
472
    case EVP_PKEY_X448: {
473
8
      EVPKeyCtxPointer ctx = nullptr;
474
      {
475
8
        ctx.reset(EVP_PKEY_CTX_new(m_privkey.get(), nullptr));
476
      }
477
8
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
478

24
      if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
479
8
          EVP_PKEY_derive_set_peer(
480
              ctx.get(),
481

16
              m_pubkey.get()) <= 0 ||
482
8
          EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
483
        return false;
484
      }
485
486
8
      data = MallocOpenSSL<char>(len);
487
488
8
      if (EVP_PKEY_derive(
489
              ctx.get(),
490
              reinterpret_cast<unsigned char*>(data),
491
              &len) <= 0) {
492
        return false;
493
      }
494
495

8
      break;
496
    }
497
    default: {
498
      const EC_KEY* private_key;
499
      {
500
40
        Mutex::ScopedLock priv_lock(*m_privkey.mutex());
501
20
        private_key = EVP_PKEY_get0_EC_KEY(m_privkey.get());
502
      }
503
504
40
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
505
20
      const EC_KEY* public_key = EVP_PKEY_get0_EC_KEY(m_pubkey.get());
506
507
20
      const EC_GROUP* group = EC_KEY_get0_group(private_key);
508
20
      if (group == nullptr)
509
        return false;
510
511
20
      CHECK_EQ(EC_KEY_check_key(private_key), 1);
512
20
      CHECK_EQ(EC_KEY_check_key(public_key), 1);
513
20
      const EC_POINT* pub = EC_KEY_get0_public_key(public_key);
514
20
      int field_size = EC_GROUP_get_degree(group);
515
20
      len = (field_size + 7) / 8;
516
20
      data = MallocOpenSSL<char>(len);
517
20
      CHECK_NOT_NULL(data);
518
20
      CHECK_NOT_NULL(pub);
519
20
      CHECK_NOT_NULL(private_key);
520
20
      if (ECDH_compute_key(
521
              data,
522
              len,
523
              pub,
524
              private_key,
525
              nullptr) <= 0) {
526
        return false;
527
      }
528
    }
529
  }
530
56
  ByteSource buf = ByteSource::Allocated(data, len);
531
28
  *out = std::move(buf);
532
28
  return true;
533
}
534
535
66
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
536
132
  EVPKeyCtxPointer key_ctx;
537
66
  switch (params->params.curve_nid) {
538
    case EVP_PKEY_ED25519:
539
      // Fall through
540
    case EVP_PKEY_ED448:
541
      // Fall through
542
    case EVP_PKEY_X25519:
543
      // Fall through
544
    case EVP_PKEY_X448:
545
      key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr));
546
      break;
547
    default: {
548
132
      EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
549
66
      EVP_PKEY* raw_params = nullptr;
550

198
      if (!param_ctx ||
551
132
          EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
552
66
          EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
553
66
              param_ctx.get(), params->params.curve_nid) <= 0 ||
554
66
          EVP_PKEY_CTX_set_ec_param_enc(
555

132
              param_ctx.get(), params->params.param_encoding) <= 0 ||
556
66
          EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
557
        return EVPKeyCtxPointer();
558
      }
559
132
      EVPKeyPointer key_params(raw_params);
560
66
      key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
561
    }
562
  }
563
564

66
  if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
565
    key_ctx.reset();
566
567
66
  return key_ctx;
568
}
569
570
// EcKeyPairGenJob input arguments
571
//   1. CryptoJobMode
572
//   2. Curve Name
573
//   3. Param Encoding
574
//   4. Public Format
575
//   5. Public Type
576
//   6. Private Format
577
//   7. Private Type
578
//   8. Cipher
579
//   9. Passphrase
580
71
Maybe<bool> EcKeyGenTraits::AdditionalConfig(
581
    CryptoJobMode mode,
582
    const FunctionCallbackInfo<Value>& args,
583
    unsigned int* offset,
584
    EcKeyPairGenConfig* params) {
585
71
  Environment* env = Environment::GetCurrent(args);
586
284
  CHECK(args[*offset]->IsString());  // curve name
587
213
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
588
589
213
  Utf8Value curve_name(env->isolate(), args[*offset]);
590
71
  params->params.curve_nid = GetCurveFromName(*curve_name);
591
71
  if (params->params.curve_nid == NID_undef) {
592
5
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
593
5
    return Nothing<bool>();
594
  }
595
596
264
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
597

69
  if (params->params.param_encoding != OPENSSL_EC_NAMED_CURVE &&
598
3
      params->params.param_encoding != OPENSSL_EC_EXPLICIT_CURVE) {
599
    THROW_ERR_OUT_OF_RANGE(env, "Invalid param_encoding specified");
600
    return Nothing<bool>();
601
  }
602
603
66
  *offset += 2;
604
605
66
  return Just(true);
606
}
607
608
namespace {
609
14
WebCryptoKeyExportStatus EC_Raw_Export(
610
    KeyObjectData* key_data,
611
    const ECKeyExportConfig& params,
612
    ByteSource* out) {
613
28
  ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
614
14
  CHECK(m_pkey);
615
28
  Mutex::ScopedLock lock(*m_pkey.mutex());
616
617
14
  const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
618
619
  unsigned char* data;
620
14
  size_t len = 0;
621
622
14
  if (ec_key == nullptr) {
623
    typedef int (*export_fn)(const EVP_PKEY*, unsigned char*, size_t* len);
624
14
    export_fn fn = nullptr;
625

14
    switch (key_data->GetKeyType()) {
626
      case kKeyTypePrivate:
627
7
        fn = EVP_PKEY_get_raw_private_key;
628
7
        break;
629
      case kKeyTypePublic:
630
7
        fn = EVP_PKEY_get_raw_public_key;
631
7
        break;
632
      case kKeyTypeSecret:
633
        UNREACHABLE();
634
    }
635
14
    CHECK_NOT_NULL(fn);
636
    // Get the size of the raw key data
637
14
    if (fn(m_pkey.get(), nullptr, &len) == 0)
638
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
639
14
    data = MallocOpenSSL<unsigned char>(len);
640
14
    if (fn(m_pkey.get(), data, &len) == 0)
641
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
642
  } else {
643
    if (key_data->GetKeyType() != kKeyTypePublic)
644
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
645
    const EC_GROUP* group = EC_KEY_get0_group(ec_key);
646
    const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
647
    point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
648
649
    // Get the allocated data size...
650
    len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr);
651
    if (len == 0)
652
      return WebCryptoKeyExportStatus::FAILED;
653
    data = MallocOpenSSL<unsigned char>(len);
654
    size_t check_len =
655
        EC_POINT_point2oct(group, point, form, data, len, nullptr);
656
    if (check_len == 0)
657
      return WebCryptoKeyExportStatus::FAILED;
658
659
    CHECK_EQ(len, check_len);
660
  }
661
662
14
  *out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
663
664
14
  return WebCryptoKeyExportStatus::OK;
665
}
666
}  // namespace
667
668
84
Maybe<bool> ECKeyExportTraits::AdditionalConfig(
669
    const FunctionCallbackInfo<Value>& args,
670
    unsigned int offset,
671
    ECKeyExportConfig* params) {
672
84
  return Just(true);
673
}
674
675
84
WebCryptoKeyExportStatus ECKeyExportTraits::DoExport(
676
    std::shared_ptr<KeyObjectData> key_data,
677
    WebCryptoKeyFormat format,
678
    const ECKeyExportConfig& params,
679
    ByteSource* out) {
680
84
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
681
682

84
  switch (format) {
683
    case kWebCryptoKeyFormatRaw:
684
14
      return EC_Raw_Export(key_data.get(), params, out);
685
    case kWebCryptoKeyFormatPKCS8:
686
33
      if (key_data->GetKeyType() != kKeyTypePrivate)
687
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
688
33
      return PKEY_PKCS8_Export(key_data.get(), out);
689
    case kWebCryptoKeyFormatSPKI:
690
37
      if (key_data->GetKeyType() != kKeyTypePublic)
691
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
692
37
      return PKEY_SPKI_Export(key_data.get(), out);
693
    default:
694
      UNREACHABLE();
695
  }
696
}
697
698
90
Maybe<bool> ExportJWKEcKey(
699
    Environment* env,
700
    std::shared_ptr<KeyObjectData> key,
701
    Local<Object> target) {
702
180
  ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
703
180
  Mutex::ScopedLock lock(*m_pkey.mutex());
704
90
  CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
705
706
90
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
707
90
  CHECK_NOT_NULL(ec);
708
709
90
  const EC_POINT* pub = EC_KEY_get0_public_key(ec);
710
90
  const EC_GROUP* group = EC_KEY_get0_group(ec);
711
712
90
  int degree_bits = EC_GROUP_get_degree(group);
713
  int degree_bytes =
714
90
    (degree_bits / CHAR_BIT) + (7 + (degree_bits % CHAR_BIT)) / 8;
715
716
180
  BignumPointer x(BN_new());
717
180
  BignumPointer y(BN_new());
718
719
90
  EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr);
720
721
270
  if (target->Set(
722
          env->context(),
723
          env->jwk_kty_string(),
724
450
          env->jwk_ec_string()).IsNothing()) {
725
    return Nothing<bool>();
726
  }
727
728
270
  if (SetEncodedValue(
729
          env,
730
          target,
731
          env->jwk_x_string(),
732
90
          x.get(),
733

270
          degree_bytes).IsNothing() ||
734
180
      SetEncodedValue(
735
          env,
736
          target,
737
          env->jwk_y_string(),
738
90
          y.get(),
739
180
          degree_bytes).IsNothing()) {
740
    return Nothing<bool>();
741
  }
742
743
90
  if (key->GetKeyType() == kKeyTypePrivate) {
744
41
    const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
745
    return SetEncodedValue(
746
      env,
747
      target,
748
      env->jwk_d_string(),
749
      pvt,
750
41
      degree_bytes);
751
  }
752
753
49
  return Just(true);
754
}
755
756
38
Maybe<bool> ExportJWKEdKey(
757
    Environment* env,
758
    std::shared_ptr<KeyObjectData> key,
759
    Local<Object> target) {
760
76
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
761
76
  Mutex::ScopedLock lock(*pkey.mutex());
762
763
38
  const char* curve = nullptr;
764

38
  switch (EVP_PKEY_id(pkey.get())) {
765
    case EVP_PKEY_ED25519:
766
13
      curve = "Ed25519";
767
13
      break;
768
    case EVP_PKEY_ED448:
769
11
      curve = "Ed448";
770
11
      break;
771
    case EVP_PKEY_X25519:
772
7
      curve = "X25519";
773
7
      break;
774
    case EVP_PKEY_X448:
775
7
      curve = "X448";
776
7
      break;
777
    default:
778
      UNREACHABLE();
779
  }
780
114
  if (target->Set(
781
          env->context(),
782
          env->jwk_crv_string(),
783
190
          OneByteString(env->isolate(), curve)).IsNothing()) {
784
    return Nothing<bool>();
785
  }
786
787
38
  size_t len = 0;
788
  Local<Value> encoded;
789
  Local<Value> error;
790
791
38
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
792
    return Nothing<bool>();
793
794
38
  unsigned char* data = MallocOpenSSL<unsigned char>(len);
795
76
  ByteSource out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
796
797
38
  if (key->GetKeyType() == kKeyTypePrivate) {
798

68
    if (!EVP_PKEY_get_raw_private_key(pkey.get(), data, &len) ||
799
51
        !StringBytes::Encode(
800
            env->isolate(),
801
            reinterpret_cast<const char*>(data),
802
            len,
803
            BASE64URL,
804

51
            &error).ToLocal(&encoded) ||
805
51
        !target->Set(
806
            env->context(),
807
            env->jwk_d_string(),
808
85
            encoded).IsJust()) {
809
      if (!error.IsEmpty())
810
        env->isolate()->ThrowException(error);
811
      return Nothing<bool>();
812
    }
813
  }
814
815

152
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), data, &len) ||
816
114
      !StringBytes::Encode(
817
          env->isolate(),
818
          reinterpret_cast<const char*>(data),
819
          len,
820
          BASE64URL,
821

114
          &error).ToLocal(&encoded) ||
822
114
      !target->Set(
823
          env->context(),
824
          env->jwk_x_string(),
825
190
          encoded).IsJust()) {
826
    if (!error.IsEmpty())
827
      env->isolate()->ThrowException(error);
828
    return Nothing<bool>();
829
  }
830
831
114
  if (target->Set(
832
          env->context(),
833
          env->jwk_kty_string(),
834
190
          env->jwk_okp_string()).IsNothing()) {
835
    return Nothing<bool>();
836
  }
837
838
38
  return Just(true);
839
}
840
841
76
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
842
    Environment* env,
843
    Local<Object> jwk,
844
    const FunctionCallbackInfo<Value>& args,
845
    unsigned int offset) {
846
304
  CHECK(args[offset]->IsString());  // curve name
847
304
  Utf8Value curve(env->isolate(), args[offset].As<String>());
848
849
76
  int nid = GetCurveFromName(*curve);
850
76
  if (nid == NID_undef) {  // Unknown curve
851
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
852
    return std::shared_ptr<KeyObjectData>();
853
  }
854
855
  Local<Value> x_value;
856
  Local<Value> y_value;
857
  Local<Value> d_value;
858
859

532
  if (!jwk->Get(env->context(), env->jwk_x_string()).ToLocal(&x_value) ||
860

456
      !jwk->Get(env->context(), env->jwk_y_string()).ToLocal(&y_value) ||
861
380
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
862
    return std::shared_ptr<KeyObjectData>();
863
  }
864
865

304
  if (!x_value->IsString() ||
866

304
      !y_value->IsString() ||
867
226
      (!d_value->IsUndefined() && !d_value->IsString())) {
868
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
869
    return std::shared_ptr<KeyObjectData>();
870
  }
871
872
152
  KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic;
873
874
152
  ECKeyPointer ec(EC_KEY_new_by_curve_name(nid));
875
76
  if (!ec) {
876
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
877
    return std::shared_ptr<KeyObjectData>();
878
  }
879
880
152
  ByteSource x = ByteSource::FromEncodedString(env, x_value.As<String>());
881
152
  ByteSource y = ByteSource::FromEncodedString(env, y_value.As<String>());
882
883
228
  if (!EC_KEY_set_public_key_affine_coordinates(
884
          ec.get(),
885
152
          x.ToBN().get(),
886
152
          y.ToBN().get())) {
887
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
888
    return std::shared_ptr<KeyObjectData>();
889
  }
890
891
76
  if (type == kKeyTypePrivate) {
892
74
    ByteSource d = ByteSource::FromEncodedString(env, d_value.As<String>());
893
37
    if (!EC_KEY_set_private_key(ec.get(), d.ToBN().get())) {
894
      THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
895
      return std::shared_ptr<KeyObjectData>();
896
    }
897
  }
898
899
152
  EVPKeyPointer pkey(EVP_PKEY_new());
900
76
  CHECK_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()), 1);
901
902
76
  return KeyObjectData::CreateAsymmetric(type, ManagedEVPPKey(std::move(pkey)));
903
}
904
905
196
Maybe<bool> GetEcKeyDetail(
906
    Environment* env,
907
    std::shared_ptr<KeyObjectData> key,
908
    Local<Object> target) {
909
392
  ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
910
392
  Mutex::ScopedLock lock(*m_pkey.mutex());
911
196
  CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
912
913
196
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
914
196
  CHECK_NOT_NULL(ec);
915
916
196
  const EC_GROUP* group = EC_KEY_get0_group(ec);
917
196
  int nid = EC_GROUP_get_curve_name(group);
918
919
  return target->Set(
920
      env->context(),
921
      env->named_curve_string(),
922
980
      OneByteString(env->isolate(), OBJ_nid2sn(nid)));
923
}
924
925
// WebCrypto requires a different format for ECDSA signatures than
926
// what OpenSSL produces, so we need to convert between them. The
927
// implementation here is a adapted from Chromium's impl here:
928
// https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc
929
930
size_t GroupOrderSize(const ManagedEVPPKey& key) {
931
  const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
932
  CHECK_NOT_NULL(ec);
933
  const EC_GROUP* group = EC_KEY_get0_group(ec);
934
  BignumPointer order(BN_new());
935
  CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
936
  return BN_num_bytes(order.get());
937
}
938
}  // namespace crypto
939

14316
}  // namespace node