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_ecdh.cc Lines: 344 418 82.3 %
Date: 2020-11-20 19:51:53 Branches: 180 312 57.7 %

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

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

40
  if (private_key->Data()->GetKeyType() != kKeyTypePrivate ||
425
20
      public_key->Data()->GetKeyType() != kKeyTypePublic) {
426
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
427
    return Nothing<bool>();
428
  }
429
430
40
  params->private_key = ECKeyPointer(
431
      EC_KEY_dup(
432
60
          EVP_PKEY_get1_EC_KEY(private_key->Data()->GetAsymmetricKey().get())));
433
20
  if (!params->private_key) {
434
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
435
    return Nothing<bool>();
436
  }
437
438
40
  params->public_key = ECKeyPointer(
439
      EC_KEY_dup(
440
60
          EVP_PKEY_get1_EC_KEY(public_key->Data()->GetAsymmetricKey().get())));
441
20
  if (!params->public_key) {
442
    THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
443
    return Nothing<bool>();
444
  }
445
446
20
  params->group = EC_KEY_get0_group(params->private_key.get());
447
448
20
  return Just(true);
449
}
450
451
20
bool ECDHBitsTraits::DeriveBits(
452
    Environment* env,
453
    const ECDHBitsConfig& params,
454
    ByteSource* out) {
455
20
  if (params.group == nullptr)
456
    return false;
457
20
  CHECK_EQ(EC_KEY_check_key(params.private_key.get()), 1);
458
20
  CHECK_EQ(EC_KEY_check_key(params.public_key.get()), 1);
459
20
  const EC_POINT* pub = EC_KEY_get0_public_key(params.public_key.get());
460
20
  int field_size = EC_GROUP_get_degree(params.group);
461
20
  size_t len = (field_size + 7) / 8;
462
20
  char* data = MallocOpenSSL<char>(len);
463
40
  ByteSource buf = ByteSource::Allocated(data, len);
464
20
  if (ECDH_compute_key(
465
          data,
466
          len,
467
          pub,
468
20
          params.private_key.get(),
469
          nullptr) <= 0) {
470
    return false;
471
  }
472
20
  *out = std::move(buf);
473
20
  return true;
474
}
475
476
62
EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) {
477
124
  EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
478
62
  EVP_PKEY* raw_params = nullptr;
479

186
  if (!param_ctx ||
480
124
      EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 ||
481
62
      EVP_PKEY_CTX_set_ec_paramgen_curve_nid(
482
62
          param_ctx.get(), params->params.curve_nid) <= 0 ||
483
62
      EVP_PKEY_CTX_set_ec_param_enc(
484

124
          param_ctx.get(), params->params.param_encoding) <= 0 ||
485
62
      EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) {
486
    return EVPKeyCtxPointer();
487
  }
488
124
  EVPKeyPointer key_params(raw_params);
489
124
  EVPKeyCtxPointer key_ctx(EVP_PKEY_CTX_new(key_params.get(), nullptr));
490
491

62
  if (!key_ctx || EVP_PKEY_keygen_init(key_ctx.get()) <= 0)
492
    return EVPKeyCtxPointer();
493
494
62
  return key_ctx;
495
}
496
497
// EcKeyPairGenJob input arguments
498
//   1. CryptoJobMode
499
//   2. Curve Name
500
//   3. Param Encoding
501
//   4. Public Format
502
//   5. Public Type
503
//   6. Private Format
504
//   7. Private Type
505
//   8. Cipher
506
//   9. Passphrase
507
63
Maybe<bool> EcKeyGenTraits::AdditionalConfig(
508
    CryptoJobMode mode,
509
    const FunctionCallbackInfo<Value>& args,
510
    unsigned int* offset,
511
    EcKeyPairGenConfig* params) {
512
63
  Environment* env = Environment::GetCurrent(args);
513
252
  CHECK(args[*offset]->IsString());  // curve name
514
189
  CHECK(args[*offset + 1]->IsInt32());  // param encoding
515
516
189
  Utf8Value curve_name(env->isolate(), args[*offset]);
517
63
  params->params.curve_nid = GetCurveFromName(*curve_name);
518
63
  if (params->params.curve_nid == NID_undef) {
519
1
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
520
1
    return Nothing<bool>();
521
  }
522
523
248
  params->params.param_encoding = args[*offset + 1].As<Int32>()->Value();
524

65
  if (params->params.param_encoding != OPENSSL_EC_NAMED_CURVE &&
525
3
      params->params.param_encoding != OPENSSL_EC_EXPLICIT_CURVE) {
526
    THROW_ERR_OUT_OF_RANGE(env, "Invalid param_encoding specified");
527
    return Nothing<bool>();
528
  }
529
530
62
  *offset += 2;
531
532
62
  return Just(true);
533
}
534
535
namespace {
536
WebCryptoKeyExportStatus EC_Raw_Export(
537
    KeyObjectData* key_data,
538
    const ECKeyExportConfig& params,
539
    ByteSource* out) {
540
  CHECK(key_data->GetAsymmetricKey());
541
542
  EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(key_data->GetAsymmetricKey().get());
543
  CHECK_NOT_NULL(ec_key);
544
545
  const EC_GROUP* group = EC_KEY_get0_group(ec_key);
546
  const EC_POINT* point = EC_KEY_get0_public_key(ec_key);
547
  point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
548
549
  // Get the allocated data size...
550
  size_t len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr);
551
  if (len == 0)
552
    return WebCryptoKeyExportStatus::FAILED;
553
554
  unsigned char* data = MallocOpenSSL<unsigned char>(len);
555
  size_t check_len = EC_POINT_point2oct(group, point, form, data, len, nullptr);
556
  if (check_len == 0)
557
    return WebCryptoKeyExportStatus::FAILED;
558
559
  CHECK_EQ(len, check_len);
560
561
  *out = ByteSource::Allocated(reinterpret_cast<char*>(data), len);
562
563
  return WebCryptoKeyExportStatus::OK;
564
}
565
}  // namespace
566
567
70
Maybe<bool> ECKeyExportTraits::AdditionalConfig(
568
    const FunctionCallbackInfo<Value>& args,
569
    unsigned int offset,
570
    ECKeyExportConfig* params) {
571
70
  return Just(true);
572
}
573
574
70
WebCryptoKeyExportStatus ECKeyExportTraits::DoExport(
575
    std::shared_ptr<KeyObjectData> key_data,
576
    WebCryptoKeyFormat format,
577
    const ECKeyExportConfig& params,
578
    ByteSource* out) {
579
70
  CHECK_NE(key_data->GetKeyType(), kKeyTypeSecret);
580
581

70
  switch (format) {
582
    case kWebCryptoKeyFormatRaw:
583
      if (key_data->GetKeyType() != kKeyTypePublic)
584
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
585
      return EC_Raw_Export(key_data.get(), params, out);
586
    case kWebCryptoKeyFormatPKCS8:
587
33
      if (key_data->GetKeyType() != kKeyTypePrivate)
588
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
589
33
      return PKEY_PKCS8_Export(key_data.get(), out);
590
    case kWebCryptoKeyFormatSPKI:
591
37
      if (key_data->GetKeyType() != kKeyTypePublic)
592
        return WebCryptoKeyExportStatus::INVALID_KEY_TYPE;
593
37
      return PKEY_SPKI_Export(key_data.get(), out);
594
    default:
595
      UNREACHABLE();
596
  }
597
}
598
599
70
Maybe<bool> ExportJWKEcKey(
600
    Environment* env,
601
    std::shared_ptr<KeyObjectData> key,
602
    Local<Object> target) {
603
140
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
604
70
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_EC);
605
606
70
  EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey.get());
607
70
  CHECK_NOT_NULL(ec);
608
609
70
  const EC_POINT* pub = EC_KEY_get0_public_key(ec);
610
70
  const EC_GROUP* group = EC_KEY_get0_group(ec);
611
612
70
  int degree_bits = EC_GROUP_get_degree(group);
613
  int degree_bytes =
614
70
    (degree_bits / CHAR_BIT) + (7 + (degree_bits % CHAR_BIT)) / 8;
615
616
140
  BignumPointer x(BN_new());
617
140
  BignumPointer y(BN_new());
618
619
70
  EC_POINT_get_affine_coordinates(group, pub, x.get(), y.get(), nullptr);
620
621
210
  if (target->Set(
622
          env->context(),
623
          env->jwk_kty_string(),
624
350
          env->jwk_ec_string()).IsNothing()) {
625
    return Nothing<bool>();
626
  }
627
628
210
  if (SetEncodedValue(
629
          env,
630
          target,
631
          env->jwk_x_string(),
632
70
          x.get(),
633

210
          degree_bytes).IsNothing() ||
634
140
      SetEncodedValue(
635
          env,
636
          target,
637
          env->jwk_y_string(),
638
70
          y.get(),
639
140
          degree_bytes).IsNothing()) {
640
    return Nothing<bool>();
641
  }
642
643
70
  if (key->GetKeyType() == kKeyTypePrivate) {
644
33
    const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
645
    return SetEncodedValue(
646
      env,
647
      target,
648
      env->jwk_d_string(),
649
      pvt,
650
33
      degree_bytes);
651
  }
652
653
37
  return Just(true);
654
}
655
656
68
std::shared_ptr<KeyObjectData> ImportJWKEcKey(
657
    Environment* env,
658
    Local<Object> jwk,
659
    const FunctionCallbackInfo<Value>& args,
660
    unsigned int offset) {
661
272
  CHECK(args[offset]->IsString());  // curve name
662
272
  Utf8Value curve(env->isolate(), args[offset].As<String>());
663
664
68
  int nid = GetCurveFromName(*curve);
665
68
  if (nid == NID_undef) {  // Unknown curve
666
    THROW_ERR_CRYPTO_INVALID_CURVE(env);
667
    return std::shared_ptr<KeyObjectData>();
668
  }
669
670
  Local<Value> x_value;
671
  Local<Value> y_value;
672
  Local<Value> d_value;
673
674

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

408
      !jwk->Get(env->context(), env->jwk_y_string()).ToLocal(&y_value) ||
676
340
      !jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value)) {
677
    return std::shared_ptr<KeyObjectData>();
678
  }
679
680

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

272
      !y_value->IsString() ||
682
202
      (!d_value->IsUndefined() && !d_value->IsString())) {
683
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JSK EC key");
684
    return std::shared_ptr<KeyObjectData>();
685
  }
686
687
136
  KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic;
688
689
136
  ECKeyPointer ec(EC_KEY_new_by_curve_name(nid));
690
68
  if (!ec) {
691
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JSK EC key");
692
    return std::shared_ptr<KeyObjectData>();
693
  }
694
695
136
  ByteSource x = ByteSource::FromEncodedString(env, x_value.As<String>());
696
136
  ByteSource y = ByteSource::FromEncodedString(env, y_value.As<String>());
697
698
204
  if (!EC_KEY_set_public_key_affine_coordinates(
699
          ec.get(),
700
136
          x.ToBN().get(),
701
136
          y.ToBN().get())) {
702
    THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JSK EC key");
703
    return std::shared_ptr<KeyObjectData>();
704
  }
705
706
68
  if (type == kKeyTypePrivate) {
707
66
    ByteSource d = ByteSource::FromEncodedString(env, d_value.As<String>());
708
33
    if (!EC_KEY_set_private_key(ec.get(), d.ToBN().get())) {
709
      THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JSK EC key");
710
      return std::shared_ptr<KeyObjectData>();
711
    }
712
  }
713
714
136
  EVPKeyPointer pkey(EVP_PKEY_new());
715
68
  CHECK_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()), 1);
716
717
68
  return KeyObjectData::CreateAsymmetric(type, ManagedEVPPKey(std::move(pkey)));
718
}
719
720
170
Maybe<bool> GetEcKeyDetail(
721
    Environment* env,
722
    std::shared_ptr<KeyObjectData> key,
723
    Local<Object> target) {
724
340
  ManagedEVPPKey pkey = key->GetAsymmetricKey();
725
170
  CHECK_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_EC);
726
727
170
  EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey.get());
728
170
  CHECK_NOT_NULL(ec);
729
730
170
  const EC_GROUP* group = EC_KEY_get0_group(ec);
731
170
  int nid = EC_GROUP_get_curve_name(group);
732
733
  return target->Set(
734
      env->context(),
735
      env->named_curve_string(),
736
850
      OneByteString(env->isolate(), OBJ_nid2sn(nid)));
737
}
738
739
// WebCrypto requires a different format for ECDSA signatures than
740
// what OpenSSL produces, so we need to convert between them. The
741
// implementation here is a adapted from Chromium's impl here:
742
// https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/ecdsa.cc
743
744
82
size_t GroupOrderSize(ManagedEVPPKey key) {
745
82
  EC_KEY* ec = EVP_PKEY_get0_EC_KEY(key.get());
746
82
  CHECK_NOT_NULL(ec);
747
82
  const EC_GROUP* group = EC_KEY_get0_group(ec);
748
164
  BignumPointer order(BN_new());
749
82
  CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
750
164
  return BN_num_bytes(order.get());
751
}
752
753
17
ByteSource ConvertToWebCryptoSignature(
754
    ManagedEVPPKey key,
755
    const ByteSource& signature) {
756
  const unsigned char* data =
757
17
      reinterpret_cast<const unsigned char*>(signature.get());
758
34
  EcdsaSigPointer ecsig(d2i_ECDSA_SIG(nullptr, &data, signature.size()));
759
760
17
  if (!ecsig)
761
    return ByteSource();
762
763
17
  size_t order_size_bytes = GroupOrderSize(key);
764
17
  char* outdata = MallocOpenSSL<char>(order_size_bytes * 2);
765
34
  ByteSource out = ByteSource::Allocated(outdata, order_size_bytes * 2);
766
17
  unsigned char* ptr = reinterpret_cast<unsigned char*>(outdata);
767
768
  const BIGNUM* pr;
769
  const BIGNUM* ps;
770
17
  ECDSA_SIG_get0(ecsig.get(), &pr, &ps);
771
772

34
  if (!BN_bn2binpad(pr, ptr, order_size_bytes) ||
773
17
      !BN_bn2binpad(ps, ptr + order_size_bytes, order_size_bytes)) {
774
    return ByteSource();
775
  }
776
17
  return out;
777
}
778
779
65
ByteSource ConvertFromWebCryptoSignature(
780
    ManagedEVPPKey key,
781
    const ByteSource& signature) {
782
65
  size_t order_size_bytes = GroupOrderSize(key);
783
784
  // If the size of the signature is incorrect, verification
785
  // will fail.
786
65
  if (signature.size() != 2 * order_size_bytes)
787
8
    return ByteSource();  // Empty!
788
789
114
  EcdsaSigPointer ecsig(ECDSA_SIG_new());
790
57
  if (!ecsig)
791
    return ByteSource();
792
793
114
  BignumPointer r(BN_new());
794
114
  BignumPointer s(BN_new());
795
796
57
  const unsigned char* sig = signature.data<unsigned char>();
797
798

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

114
      !BN_bin2bn(sig + order_size_bytes, order_size_bytes, s.get()) ||
800
57
      !ECDSA_SIG_set0(ecsig.get(), r.release(), s.release())) {
801
    return ByteSource();
802
  }
803
804
57
  int size = i2d_ECDSA_SIG(ecsig.get(), nullptr);
805
57
  char* data = MallocOpenSSL<char>(size);
806
57
  unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
807
57
  CHECK_EQ(i2d_ECDSA_SIG(ecsig.get(), &ptr), size);
808
57
  return ByteSource::Allocated(data, size);
809
}
810
811
}  // namespace crypto
812

14034
}  // namespace node