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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

13995
}  // namespace node