GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_ec.cc Lines: 426 550 77.5 %
Date: 2022-06-16 04:15:44 Branches: 188 350 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
142
int GetOKPCurveFromName(const char* name) {
47
  int nid;
48
142
  if (strcmp(name, "Ed25519") == 0) {
49
11
    nid = EVP_PKEY_ED25519;
50
131
  } else if (strcmp(name, "Ed448") == 0) {
51
11
    nid = EVP_PKEY_ED448;
52
120
  } else if (strcmp(name, "X25519") == 0) {
53
23
    nid = EVP_PKEY_X25519;
54
97
  } else if (strcmp(name, "X448") == 0) {
55
23
    nid = EVP_PKEY_X448;
56
  } else {
57
74
    nid = NID_undef;
58
  }
59
142
  return nid;
60
}
61
62
853
void ECDH::Initialize(Environment* env, Local<Object> target) {
63
853
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
64
853
  t->Inherit(BaseObject::GetConstructorTemplate(env));
65
66
1706
  t->InstanceTemplate()->SetInternalFieldCount(ECDH::kInternalFieldCount);
67
68
853
  env->SetProtoMethod(t, "generateKeys", GenerateKeys);
69
853
  env->SetProtoMethod(t, "computeSecret", ComputeSecret);
70
853
  env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey);
71
853
  env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey);
72
853
  env->SetProtoMethod(t, "setPublicKey", SetPublicKey);
73
853
  env->SetProtoMethod(t, "setPrivateKey", SetPrivateKey);
74
75
853
  env->SetConstructorFunction(target, "ECDH", t);
76
77
853
  env->SetMethodNoSideEffect(target, "ECDHConvertKey", ECDH::ConvertKey);
78
853
  env->SetMethodNoSideEffect(target, "getCurves", ECDH::GetCurves);
79
80
853
  ECDHBitsJob::Initialize(env, target);
81
853
  ECKeyPairGenJob::Initialize(env, target);
82
853
  ECKeyExportJob::Initialize(env, target);
83
84
2559
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
85
1706
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
86
853
}
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
30
ECPointPointer ECDH::BufferToPoint(Environment* env,
172
                                   const EC_GROUP* group,
173
                                   Local<Value> buf) {
174
  int r;
175
176
60
  ECPointPointer pub(EC_POINT_new(group));
177
30
  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
60
  ArrayBufferOrViewContents<unsigned char> input(buf);
184
30
  if (UNLIKELY(!input.CheckSizeInt32())) {
185
    THROW_ERR_OUT_OF_RANGE(env, "buffer is too big");
186
    return ECPointPointer();
187
  }
188
60
  r = EC_POINT_oct2point(
189
      group,
190
      pub.get(),
191
30
      input.data(),
192
      input.size(),
193
      nullptr);
194
30
  if (!r)
195
4
    return ECPointPointer();
196
197
26
  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(Environment* env,
490
                                const ECDHBitsConfig& params,
491
                                ByteSource* out) {
492
98
  size_t len = 0;
493
196
  ManagedEVPPKey m_privkey = params.private_->GetAsymmetricKey();
494
196
  ManagedEVPPKey m_pubkey = params.public_->GetAsymmetricKey();
495
496
98
  switch (params.id_) {
497
24
    case EVP_PKEY_X25519:
498
      // Fall through
499
    case EVP_PKEY_X448: {
500
24
      EVPKeyCtxPointer ctx = nullptr;
501
      {
502
24
        ctx.reset(EVP_PKEY_CTX_new(m_privkey.get(), nullptr));
503
      }
504
24
      Mutex::ScopedLock pub_lock(*m_pubkey.mutex());
505
48
      if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
506
24
          EVP_PKEY_derive_set_peer(
507
              ctx.get(),
508

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

192
              param_ctx.get(), params->params.param_encoding) <= 0 ||
578
96
          EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
579
        return EVPKeyCtxPointer();
580
      }
581
192
      EVPKeyPointer key_params(raw_params);
582
192
      key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
583
    }
584
  }
585
586

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

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

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

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

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

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

48
             .ToLocal(&encoded) ||
855

64
        !target->Set(env->context(), env->jwk_d_string(), encoded).IsJust()) {
856
      if (!error.IsEmpty())
857
        env->isolate()->ThrowException(error);
858
      return Nothing<bool>();
859
    }
860
  }
861
862
72
  if (!EVP_PKEY_get_raw_public_key(
863
36
          pkey.get(), out.data<unsigned char>(), &len) ||
864
72
      !StringBytes::Encode(
865
36
           env->isolate(), out.data<const char>(), len, BASE64URL, &error)
866

108
           .ToLocal(&encoded) ||
867

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

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

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

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

624
      !y_value->IsString() ||
909

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