GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 429 529 81.1 %
Date: 2021-09-20 04:12:42 Branches: 192 344 55.8 %

Line Branch Exec Source
1
#include "crypto/crypto_ec.h"
2
#include "crypto/crypto_common.h"
3
#include "crypto/crypto_util.h"
4
#include "allocated_buffer-inl.h"
5
#include "async_wrap-inl.h"
6
#include "base_object-inl.h"
7
#include "env-inl.h"
8
#include "memory_tracker-inl.h"
9
#include "node_buffer.h"
10
#include "string_bytes.h"
11
#include "threadpoolwork-inl.h"
12
#include "v8.h"
13
14
#include <openssl/bn.h>
15
#include <openssl/ec.h>
16
#include <openssl/ecdh.h>
17
18
#include <algorithm>
19
20
namespace node {
21
22
using v8::Array;
23
using v8::FunctionCallbackInfo;
24
using v8::FunctionTemplate;
25
using v8::Int32;
26
using v8::Just;
27
using v8::Local;
28
using v8::Maybe;
29
using v8::Nothing;
30
using v8::Object;
31
using v8::String;
32
using v8::Uint32;
33
using v8::Value;
34
35
namespace crypto {
36
37
261
int GetCurveFromName(const char* name) {
38
261
  int nid = EC_curve_nist2nid(name);
39
261
  if (nid == NID_undef)
40
23
    nid = OBJ_sn2nid(name);
41
261
  return nid;
42
}
43
44
108
int GetOKPCurveFromName(const char* name) {
45
  int nid;
46
108
  if (strcmp(name, "NODE-ED25519") == 0) {
47
10
    nid = EVP_PKEY_ED25519;
48
98
  } else if (strcmp(name, "NODE-ED448") == 0) {
49
8
    nid = EVP_PKEY_ED448;
50
90
  } else if (strcmp(name, "NODE-X25519") == 0) {
51
12
    nid = EVP_PKEY_X25519;
52
78
  } else if (strcmp(name, "NODE-X448") == 0) {
53
10
    nid = EVP_PKEY_X448;
54
  } else {
55
68
    nid = NID_undef;
56
  }
57
108
  return nid;
58
}
59
60
4299
void ECDH::Initialize(Environment* env, Local<Object> target) {
61
4299
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
62
4299
  t->Inherit(BaseObject::GetConstructorTemplate(env));
63
64
8598
  t->InstanceTemplate()->SetInternalFieldCount(ECDH::kInternalFieldCount);
65
66
4299
  env->SetProtoMethod(t, "generateKeys", GenerateKeys);
67
4299
  env->SetProtoMethod(t, "computeSecret", ComputeSecret);
68
4299
  env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey);
69
4299
  env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey);
70
4299
  env->SetProtoMethod(t, "setPublicKey", SetPublicKey);
71
4299
  env->SetProtoMethod(t, "setPrivateKey", SetPrivateKey);
72
73
4299
  env->SetConstructorFunction(target, "ECDH", t);
74
75
4299
  env->SetMethodNoSideEffect(target, "ECDHConvertKey", ECDH::ConvertKey);
76
4299
  env->SetMethodNoSideEffect(target, "getCurves", ECDH::GetCurves);
77
78
4299
  ECDHBitsJob::Initialize(env, target);
79
4299
  ECKeyPairGenJob::Initialize(env, target);
80
4299
  ECKeyExportJob::Initialize(env, target);
81
82
12897
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
83
8598
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
84
4299
}
85
86
6
void ECDH::GetCurves(const FunctionCallbackInfo<Value>& args) {
87
6
  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
6
      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
6
      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
10
    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
40
ECDH::~ECDH() {}
121
122
10
void ECDH::New(const FunctionCallbackInfo<Value>& args) {
123
10
  Environment* env = Environment::GetCurrent(args);
124
125
10
  MarkPopErrorOnReturn mark_pop_error_on_return;
126
127
  // TODO(indutny): Support raw curves?
128
20
  CHECK(args[0]->IsString());
129
10
  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
10
  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
      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
7
  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
6
                          args[0]));
199
6
  if (!pub) {
200
2
    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
4
  AllocatedBuffer out = AllocatedBuffer::AllocateManaged(env, out_len);
210
211
4
  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
1
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
232
1
        "Failed to get ECDH public key");
233
234
20
  CHECK(args[0]->IsUint32());
235
40
  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
1
    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
6
  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
7
  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
7
      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
3
    return THROW_ERR_CRYPTO_INVALID_KEYTYPE(env,
284
3
        "Private key is not valid for specified curve.");
285
  }
286
287
4
  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
4
  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
4
  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
  ecdh->key_ = std::move(new_key);
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
5
  MarkPopErrorOnReturn mark_pop_error_on_return;
330
331
  ECPointPointer pub(
332
      ECDH::BufferToPoint(env,
333
                          ecdh->group_,
334
5
                          args[0]));
335
5
  if (!pub) {
336
1
    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
6
  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
7
  MarkPopErrorOnReturn mark_pop_error_on_return;
363
7
  USE(&mark_pop_error_on_return);
364
7
  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
8
  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
8
  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
8
  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
7
      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
7
                          args[0]));
396
397
7
  if (pub == nullptr) {
398
1
    return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
399
1
        "Failed to convert Buffer to EC_POINT");
400
  }
401
402
6
  CHECK(args[2]->IsUint32());
403
12
  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
76
Maybe<bool> ECDHBitsTraits::EncodeOutput(
419
    Environment* env,
420
    const ECDHBitsConfig& params,
421
    ByteSource* out,
422
    v8::Local<v8::Value>* result) {
423
152
  *result = out->ToArrayBuffer(env);
424
76
  return Just(!result->IsEmpty());
425
}
426
427
76
Maybe<bool> ECDHBitsTraits::AdditionalConfig(
428
    CryptoJobMode mode,
429
    const FunctionCallbackInfo<Value>& args,
430
    unsigned int offset,
431
    ECDHBitsConfig* params) {
432
76
  Environment* env = Environment::GetCurrent(args);
433
434

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

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

152
  CHECK(args[offset + 2]->IsObject());  // private key
437
438
  KeyObjectHandle* private_key;
439
  KeyObjectHandle* public_key;
440
441
228
  Utf8Value name(env->isolate(), args[offset]);
442
443

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

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

152
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
447
76
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
448
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
449
    return Nothing<bool>();
450
  }
451
452
76
  params->id_ = GetOKPCurveFromName(*name);
453
76
  params->private_ = private_key->Data();
454
76
  params->public_ = public_key->Data();
455
456
76
  return Just(true);
457
}
458
459
76
bool ECDHBitsTraits::DeriveBits(
460
    Environment* env,
461
    const ECDHBitsConfig& params,
462
    ByteSource* out) {
463
464
76
  char* data = nullptr;
465
76
  size_t len = 0;
466
152
  ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
467
152
  ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
468
469
76
  switch (params.id_) {
470
8
    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
16
      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
8
              &len) <= 0) {
492
        return false;
493
      }
494
495
8
      break;
496
    }
497
68
    default: {
498
      const EC_KEY* private_key;
499
      {
500
68
        Mutex::ScopedLock priv_lock(*m_privkey.mutex());
501
68
        private_key = EVP_PKEY_get0_EC_KEY(m_privkey.get());
502
      }
503
504
68
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
505
68
      const EC_KEY* public_key = EVP_PKEY_get0_EC_KEY(m_pubkey.get());
506
507
68
      const EC_GROUP* group = EC_KEY_get0_group(private_key);
508
68
      if (group == nullptr)
509
        return false;
510
511
68
      CHECK_EQ(EC_KEY_check_key(private_key), 1);
512
68
      CHECK_EQ(EC_KEY_check_key(public_key), 1);
513
68
      const EC_POINT* pub = EC_KEY_get0_public_key(public_key);
514
68
      int field_size = EC_GROUP_get_degree(group);
515
68
      len = (field_size + 7) / 8;
516
68
      data = MallocOpenSSL<char>(len);
517
68
      CHECK_NOT_NULL(data);
518
68
      CHECK_NOT_NULL(pub);
519
68
      CHECK_NOT_NULL(private_key);
520
68
      if (ECDH_compute_key(
521
              data,
522
              len,
523
              pub,
524
              private_key,
525
68
              nullptr) <= 0) {
526
        return false;
527
68
      }
528
    }
529
  }
530
76
  ByteSource buf = ByteSource::Allocated(data, len);
531
76
  *out = std::move(buf);
532
76
  return true;
533
}
534
535
100
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
536
100
  EVPKeyCtxPointer key_ctx;
537
100
  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
100
    default: {
548
100
      EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
549
100
      EVP_PKEY* raw_params = nullptr;
550
200
      if (!param_ctx ||
551
200
          EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
552
100
          EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
553
100
              param_ctx.get(), params->params.curve_nid) <= 0 ||
554
100
          EVP_PKEY_CTX_set_ec_param_enc(
555

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

100
  if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
565
    key_ctx.reset();
566
567
100
  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
105
Maybe<bool> EcKeyGenTraits::AdditionalConfig(
581
    CryptoJobMode mode,
582
    const FunctionCallbackInfo<Value>& args,
583
    unsigned int* offset,
584
    EcKeyPairGenConfig* params) {
585
105
  Environment* env = Environment::GetCurrent(args);
586

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

210
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
588
589
315
  Utf8Value curve_name(env->isolate(), args[*offset]);
590
105
  params->params.curve_nid = GetCurveFromName(*curve_name);
591
105
  if (params->params.curve_nid == NID_undef) {
592
5
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
593
5
    return Nothing<bool>();
594
  }
595
596
300
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
597
100
  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
100
  *offset += 2;
604
605
100
  return Just(true);
606
}
607
608
namespace {
609
7
WebCryptoKeyExportStatus EC_Raw_Export(
610
    KeyObjectData* key_data,
611
    const ECKeyExportConfig& params,
612
    ByteSource* out) {
613
14
  ManagedEVPPKey m_pkey = key_data->GetAsymmetricKey();
614
7
  CHECK(m_pkey);
615
14
  Mutex::ScopedLock lock(*m_pkey.mutex());
616
617
7
  const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(m_pkey.get());
618
619
  unsigned char* data;
620
7
  size_t len = 0;
621
622
7
  if (ec_key == nullptr) {
623
    typedef int (*export_fn)(const EVP_PKEY*, unsigned char*, size_t* len);
624
7
    export_fn fn = nullptr;
625

7
    switch (key_data->GetKeyType()) {
626
      case kKeyTypePrivate:
627
        fn = EVP_PKEY_get_raw_private_key;
628
        break;
629
7
      case kKeyTypePublic:
630
7
        fn = EVP_PKEY_get_raw_public_key;
631
7
        break;
632
      case kKeyTypeSecret:
633
        UNREACHABLE();
634
    }
635
7
    CHECK_NOT_NULL(fn);
636
    // Get the size of the raw key data
637
7
    if (fn(m_pkey.get(), nullptr, &len) == 0)
638
      return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
639
7
    data = MallocOpenSSL<unsigned char>(len);
640
7
    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
7
  *out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
663
664
7
  return WebCryptoKeyExportStatus::OK;
665
}
666
}  // namespace
667
668
155
Maybe<bool> ECKeyExportTraits::AdditionalConfig(
669
    const FunctionCallbackInfo<Value>& args,
670
    unsigned int offset,
671
    ECKeyExportConfig* params) {
672
155
  return Just(true);
673
}
674
675
155
WebCryptoKeyExportStatus ECKeyExportTraits::DoExport(
676
    std::shared_ptr<KeyObjectData> key_data,
677
    WebCryptoKeyFormat format,
678
    const ECKeyExportConfig& params,
679
    ByteSource* out) {
680
155
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
681
682

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

199
  switch (nid) {
746
97
    case NID_X9_62_prime256v1:
747
97
      crv_name = OneByteString(env->isolate(), "P-256");
748
97
      break;
749
7
    case NID_secp256k1:
750
7
      crv_name = OneByteString(env->isolate(), "secp256k1");
751
7
      break;
752
75
    case NID_secp384r1:
753
75
      crv_name = OneByteString(env->isolate(), "P-384");
754
75
      break;
755
17
    case NID_secp521r1:
756
17
      crv_name = OneByteString(env->isolate(), "P-521");
757
17
      break;
758
3
    default: {
759
3
      THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
760
3
          env, "Unsupported JWK EC curve: %s.", OBJ_nid2sn(nid));
761
3
      return Nothing<bool>();
762
    }
763
  }
764
392
  if (target->Set(
765
      env->context(),
766
      env->jwk_crv_string(),
767
588
      crv_name).IsNothing()) {
768
    return Nothing<bool>();
769
  }
770
771
196
  if (key->GetKeyType() == kKeyTypePrivate) {
772
111
    const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
773
    return SetEncodedValue(
774
      env,
775
      target,
776
      env->jwk_d_string(),
777
      pvt,
778
111
      degree_bytes);
779
  }
780
781
85
  return Just(true);
782
}
783
784
46
Maybe<bool> ExportJWKEdKey(
785
    Environment* env,
786
    std::shared_ptr<KeyObjectData> key,
787
    Local<Object> target) {
788
92
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
789
92
  Mutex::ScopedLock lock(*pkey.mutex());
790
791
46
  const char* curve = nullptr;
792

46
  switch (EVP_PKEY_id(pkey.get())) {
793
15
    case EVP_PKEY_ED25519:
794
15
      curve = "Ed25519";
795
15
      break;
796
13
    case EVP_PKEY_ED448:
797
13
      curve = "Ed448";
798
13
      break;
799
9
    case EVP_PKEY_X25519:
800
9
      curve = "X25519";
801
9
      break;
802
9
    case EVP_PKEY_X448:
803
9
      curve = "X448";
804
9
      break;
805
    default:
806
      UNREACHABLE();
807
  }
808
92
  if (target->Set(
809
          env->context(),
810
          env->jwk_crv_string(),
811
184
          OneByteString(env->isolate(), curve)).IsNothing()) {
812
    return Nothing<bool>();
813
  }
814
815
46
  size_t len = 0;
816
  Local<Value> encoded;
817
  Local<Value> error;
818
819
46
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
820
    return Nothing<bool>();
821
822
46
  unsigned char* data = MallocOpenSSL<unsigned char>(len);
823
92
  ByteSource out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
824
825
46
  if (key->GetKeyType() == kKeyTypePrivate) {
826
21
    if (!EVP_PKEY_get_raw_private_key(pkey.get(), data, &len) ||
827
42
        !StringBytes::Encode(
828
            env->isolate(),
829
            reinterpret_cast<const char*>(data),
830
            len,
831
            BASE64URL,
832

63
            &error).ToLocal(&encoded) ||
833
42
        !target->Set(
834
            env->context(),
835
            env->jwk_d_string(),
836

84
            encoded).IsJust()) {
837
      if (!error.IsEmpty())
838
        env->isolate()->ThrowException(error);
839
      return Nothing<bool>();
840
    }
841
  }
842
843
46
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), data, &len) ||
844
92
      !StringBytes::Encode(
845
          env->isolate(),
846
          reinterpret_cast<const char*>(data),
847
          len,
848
          BASE64URL,
849

138
          &error).ToLocal(&encoded) ||
850
92
      !target->Set(
851
          env->context(),
852
          env->jwk_x_string(),
853

184
          encoded).IsJust()) {
854
    if (!error.IsEmpty())
855
      env->isolate()->ThrowException(error);
856
    return Nothing<bool>();
857
  }
858
859
92
  if (target->Set(
860
          env->context(),
861
          env->jwk_kty_string(),
862
184
          env->jwk_okp_string()).IsNothing()) {
863
    return Nothing<bool>();
864
  }
865
866
46
  return Just(true);
867
}
868
869
156
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
870
    Environment* env,
871
    Local<Object> jwk,
872
    const FunctionCallbackInfo<Value>& args,
873
    unsigned int offset) {
874

468
  CHECK(args[offset]->IsString());  // curve name
875
624
  Utf8Value curve(env->isolate(), args[offset].As<String>());
876
877
156
  int nid = GetCurveFromName(*curve);
878
156
  if (nid == NID_undef) {  // Unknown curve
879
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
880
    return std::shared_ptr<KeyObjectData>();
881
  }
882
883
  Local<Value> x_value;
884
  Local<Value> y_value;
885
  Local<Value> d_value;
886
887
312
  if (!jwk->Get(env->context(), env->jwk_x_string()).ToLocal(&x_value) ||
888

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

624
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
890
    return std::shared_ptr<KeyObjectData>();
891
  }
892
893
156
  if (!x_value->IsString() ||
894

624
      !y_value->IsString() ||
895

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