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: 423 506 83.6 %
Date: 2021-02-11 04:11:15 Branches: 225 385 58.4 %

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

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

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

40
      if (EVP_PKEY_derive_init(ctx.get()) <= 0 ||
474
8
          EVP_PKEY_derive_set_peer(
475
              ctx.get(),
476

32
              params.public_->GetAsymmetricKey().get()) <= 0 ||
477
8
          EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) {
478
        return false;
479
      }
480
481
8
      data = MallocOpenSSL<char>(len);
482
483
8
      if (EVP_PKEY_derive(
484
              ctx.get(),
485
              reinterpret_cast<unsigned char*>(data),
486
              &len) <= 0) {
487
        return false;
488
      }
489
490
8
      break;
491
    }
492
    default: {
493
      const EC_KEY* private_key =
494
20
          EVP_PKEY_get0_EC_KEY(params.private_->GetAsymmetricKey().get());
495
      const EC_KEY* public_key =
496
20
          EVP_PKEY_get0_EC_KEY(params.public_->GetAsymmetricKey().get());
497
498
20
      const EC_GROUP* group = EC_KEY_get0_group(private_key);
499
20
      if (group == nullptr)
500
        return false;
501
502
20
      CHECK_EQ(EC_KEY_check_key(private_key), 1);
503
20
      CHECK_EQ(EC_KEY_check_key(public_key), 1);
504
20
      const EC_POINT* pub = EC_KEY_get0_public_key(public_key);
505
20
      int field_size = EC_GROUP_get_degree(group);
506
20
      len = (field_size + 7) / 8;
507
20
      data = MallocOpenSSL<char>(len);
508
20
      CHECK_NOT_NULL(data);
509
20
      CHECK_NOT_NULL(pub);
510
20
      CHECK_NOT_NULL(private_key);
511
20
      if (ECDH_compute_key(
512
              data,
513
              len,
514
              pub,
515
              private_key,
516
              nullptr) <= 0) {
517
        return false;
518
      }
519
    }
520
  }
521
56
  ByteSource buf = ByteSource::Allocated(data, len);
522
28
  *out = std::move(buf);
523
28
  return true;
524
}
525
526
62
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
527
124
  EVPKeyCtxPointer key_ctx;
528
62
  switch (params->params.curve_nid) {
529
    case EVP_PKEY_ED25519:
530
      // Fall through
531
    case EVP_PKEY_ED448:
532
      // Fall through
533
    case EVP_PKEY_X25519:
534
      // Fall through
535
    case EVP_PKEY_X448:
536
      key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr));
537
      break;
538
    default: {
539
124
      EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
540
62
      EVP_PKEY* raw_params = nullptr;
541

186
      if (!param_ctx ||
542
124
          EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
543
62
          EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
544
62
              param_ctx.get(), params->params.curve_nid) <= 0 ||
545
62
          EVP_PKEY_CTX_set_ec_param_enc(
546

124
              param_ctx.get(), params->params.param_encoding) <= 0 ||
547
62
          EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
548
        return EVPKeyCtxPointer();
549
      }
550
124
      EVPKeyPointer key_params(raw_params);
551
62
      key_ctx.reset(EVP_PKEY_CTX_new(key_params.get(), nullptr));
552
    }
553
  }
554
555

62
  if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
556
    key_ctx.reset();
557
558
62
  return key_ctx;
559
}
560
561
// EcKeyPairGenJob input arguments
562
//   1. CryptoJobMode
563
//   2. Curve Name
564
//   3. Param Encoding
565
//   4. Public Format
566
//   5. Public Type
567
//   6. Private Format
568
//   7. Private Type
569
//   8. Cipher
570
//   9. Passphrase
571
67
Maybe<bool> EcKeyGenTraits::AdditionalConfig(
572
    CryptoJobMode mode,
573
    const FunctionCallbackInfo<Value>& args,
574
    unsigned int* offset,
575
    EcKeyPairGenConfig* params) {
576
67
  Environment* env = Environment::GetCurrent(args);
577
268
  CHECK(args[*offset]->IsString());  // curve name
578
201
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
579
580
201
  Utf8Value curve_name(env->isolate(), args[*offset]);
581
67
  params->params.curve_nid = GetCurveFromName(*curve_name);
582
67
  if (params->params.curve_nid == NID_undef) {
583
5
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
584
5
    return Nothing<bool>();
585
  }
586
587
248
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
588

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

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

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

246
          degree_bytes).IsNothing() ||
725
164
      SetEncodedValue(
726
          env,
727
          target,
728
          env->jwk_y_string(),
729
82
          y.get(),
730
164
          degree_bytes).IsNothing()) {
731
    return Nothing<bool>();
732
  }
733
734
82
  if (key->GetKeyType() == kKeyTypePrivate) {
735
37
    const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
736
    return SetEncodedValue(
737
      env,
738
      target,
739
      env->jwk_d_string(),
740
      pvt,
741
37
      degree_bytes);
742
  }
743
744
45
  return Just(true);
745
}
746
747
30
Maybe<bool> ExportJWKEdKey(
748
    Environment* env,
749
    std::shared_ptr<KeyObjectData> key,
750
    Local<Object> target) {
751
60
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
752
753
30
  const char* curve = nullptr;
754

30
  switch (EVP_PKEY_id(pkey.get())) {
755
    case EVP_PKEY_ED25519:
756
11
      curve = "Ed25519";
757
11
      break;
758
    case EVP_PKEY_ED448:
759
9
      curve = "Ed448";
760
9
      break;
761
    case EVP_PKEY_X25519:
762
5
      curve = "X25519";
763
5
      break;
764
    case EVP_PKEY_X448:
765
5
      curve = "X448";
766
5
      break;
767
    default:
768
      UNREACHABLE();
769
  }
770
90
  if (target->Set(
771
          env->context(),
772
          env->jwk_crv_string(),
773
150
          OneByteString(env->isolate(), curve)).IsNothing()) {
774
    return Nothing<bool>();
775
  }
776
777
30
  size_t len = 0;
778
  Local<Value> encoded;
779
  Local<Value> error;
780
781
30
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len))
782
    return Nothing<bool>();
783
784
30
  unsigned char* data = MallocOpenSSL<unsigned char>(len);
785
60
  ByteSource out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
786
787
30
  if (key->GetKeyType() == kKeyTypePrivate) {
788

52
    if (!EVP_PKEY_get_raw_private_key(pkey.get(), data, &len) ||
789
39
        !StringBytes::Encode(
790
            env->isolate(),
791
            reinterpret_cast<const char*>(data),
792
            len,
793
            BASE64URL,
794

39
            &error).ToLocal(&encoded) ||
795
39
        !target->Set(
796
            env->context(),
797
            env->jwk_d_string(),
798
65
            encoded).IsJust()) {
799
      if (!error.IsEmpty())
800
        env->isolate()->ThrowException(error);
801
      return Nothing<bool>();
802
    }
803
  }
804
805

120
  if (!EVP_PKEY_get_raw_public_key(pkey.get(), data, &len) ||
806
90
      !StringBytes::Encode(
807
          env->isolate(),
808
          reinterpret_cast<const char*>(data),
809
          len,
810
          BASE64URL,
811

90
          &error).ToLocal(&encoded) ||
812
90
      !target->Set(
813
          env->context(),
814
          env->jwk_x_string(),
815
150
          encoded).IsJust()) {
816
    if (!error.IsEmpty())
817
      env->isolate()->ThrowException(error);
818
    return Nothing<bool>();
819
  }
820
821
90
  if (target->Set(
822
          env->context(),
823
          env->jwk_kty_string(),
824
150
          env->jwk_okp_string()).IsNothing()) {
825
    return Nothing<bool>();
826
  }
827
828
30
  return Just(true);
829
}
830
831
68
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
832
    Environment* env,
833
    Local<Object> jwk,
834
    const FunctionCallbackInfo<Value>& args,
835
    unsigned int offset) {
836
272
  CHECK(args[offset]->IsString());  // curve name
837
272
  Utf8Value curve(env->isolate(), args[offset].As<String>());
838
839
68
  int nid = GetCurveFromName(*curve);
840
68
  if (nid == NID_undef) {  // Unknown curve
841
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
842
    return std::shared_ptr<KeyObjectData>();
843
  }
844
845
  Local<Value> x_value;
846
  Local<Value> y_value;
847
  Local<Value> d_value;
848
849

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

408
      !jwk->Get(env->context(), env->jwk_y_string()).ToLocal(&y_value) ||
851
340
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
852
    return std::shared_ptr<KeyObjectData>();
853
  }
854
855

272
  if (!x_value->IsString() ||
856

272
      !y_value->IsString() ||
857
202
      (!d_value->IsUndefined() && !d_value->IsString())) {
858
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
859
    return std::shared_ptr<KeyObjectData>();
860
  }
861
862
136
  KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic;
863
864
136
  ECKeyPointer ec(EC_KEY_new_by_curve_name(nid));
865
68
  if (!ec) {
866
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
867
    return std::shared_ptr<KeyObjectData>();
868
  }
869
870
136
  ByteSource x = ByteSource::FromEncodedString(env, x_value.As<String>());
871
136
  ByteSource y = ByteSource::FromEncodedString(env, y_value.As<String>());
872
873
204
  if (!EC_KEY_set_public_key_affine_coordinates(
874
          ec.get(),
875
136
          x.ToBN().get(),
876
136
          y.ToBN().get())) {
877
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
878
    return std::shared_ptr<KeyObjectData>();
879
  }
880
881
68
  if (type == kKeyTypePrivate) {
882
66
    ByteSource d = ByteSource::FromEncodedString(env, d_value.As<String>());
883
33
    if (!EC_KEY_set_private_key(ec.get(), d.ToBN().get())) {
884
      THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK EC key");
885
      return std::shared_ptr<KeyObjectData>();
886
    }
887
  }
888
889
136
  EVPKeyPointer pkey(EVP_PKEY_new());
890
68
  CHECK_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()), 1);
891
892
68
  return KeyObjectData::CreateAsymmetric(type, ManagedEVPPKey(std::move(pkey)));
893
}
894
895
188
Maybe<bool> GetEcKeyDetail(
896
    Environment* env,
897
    std::shared_ptr<KeyObjectData> key,
898
    Local<Object> target) {
899
376
  ManagedEVPPKey m_pkey = key->GetAsymmetricKey();
900
376
  Mutex::ScopedLock lock(*m_pkey.mutex());
901
188
  CHECK_EQ(EVP_PKEY_id(m_pkey.get()), EVP_PKEY_EC);
902
903
188
  EC_KEY* ec = EVP_PKEY_get0_EC_KEY(m_pkey.get());
904
188
  CHECK_NOT_NULL(ec);
905
906
188
  const EC_GROUP* group = EC_KEY_get0_group(ec);
907
188
  int nid = EC_GROUP_get_curve_name(group);
908
909
  return target->Set(
910
      env->context(),
911
      env->named_curve_string(),
912
940
      OneByteString(env->isolate(), OBJ_nid2sn(nid)));
913
}
914
915
// WebCrypto requires a different format for ECDSA signatures than
916
// what OpenSSL produces, so we need to convert between them. The
917
// implementation here is a adapted from Chromium's impl here:
918
// https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc
919
920
82
size_t GroupOrderSize(ManagedEVPPKey key) {
921
82
  EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
922
82
  CHECK_NOT_NULL(ec);
923
82
  const EC_GROUP* group = EC_KEY_get0_group(ec);
924
164
  BignumPointer order(BN_new());
925
82
  CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
926
164
  return BN_num_bytes(order.get());
927
}
928
929
17
ByteSource ConvertToWebCryptoSignature(
930
    ManagedEVPPKey key,
931
    const ByteSource& signature) {
932
  const unsigned char* data =
933
17
      reinterpret_cast<const unsigned char*>(signature.get());
934
34
  EcdsaSigPointer ecsig(d2i_ECDSA_SIG(nullptr, &data, signature.size()));
935
936
17
  if (!ecsig)
937
    return ByteSource();
938
939
17
  size_t order_size_bytes = GroupOrderSize(key);
940
17
  char* outdata = MallocOpenSSL<char>(order_size_bytes * 2);
941
34
  ByteSource out = ByteSource::Allocated(outdata, order_size_bytes * 2);
942
17
  unsigned char* ptr = reinterpret_cast<unsigned char*>(outdata);
943
944
  const BIGNUM* pr;
945
  const BIGNUM* ps;
946
17
  ECDSA_SIG_get0(ecsig.get(), &pr, &ps);
947
948

34
  if (!BN_bn2binpad(pr, ptr, order_size_bytes) ||
949
17
      !BN_bn2binpad(ps, ptr + order_size_bytes, order_size_bytes)) {
950
    return ByteSource();
951
  }
952
17
  return out;
953
}
954
955
65
ByteSource ConvertFromWebCryptoSignature(
956
    ManagedEVPPKey key,
957
    const ByteSource& signature) {
958
65
  size_t order_size_bytes = GroupOrderSize(key);
959
960
  // If the size of the signature is incorrect, verification
961
  // will fail.
962
65
  if (signature.size() != 2 * order_size_bytes)
963
8
    return ByteSource();  // Empty!
964
965
114
  EcdsaSigPointer ecsig(ECDSA_SIG_new());
966
57
  if (!ecsig)
967
    return ByteSource();
968
969
114
  BignumPointer r(BN_new());
970
114
  BignumPointer s(BN_new());
971
972
57
  const unsigned char* sig = signature.data<unsigned char>();
973
974

171
  if (!BN_bin2bn(sig, order_size_bytes, r.get()) ||
975

114
      !BN_bin2bn(sig + order_size_bytes, order_size_bytes, s.get()) ||
976
57
      !ECDSA_SIG_set0(ecsig.get(), r.release(), s.release())) {
977
    return ByteSource();
978
  }
979
980
57
  int size = i2d_ECDSA_SIG(ecsig.get(), nullptr);
981
57
  char* data = MallocOpenSSL<char>(size);
982
57
  unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
983
57
  CHECK_EQ(i2d_ECDSA_SIG(ecsig.get(), &ptr), size);
984
57
  return ByteSource::Allocated(data, size);
985
}
986
987
}  // namespace crypto
988

14097
}  // namespace node