Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c3042c6

Browse filesBrowse files
committed
crypto: recognize raw formats in keygen
Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #62480 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent fbf8276 commit c3042c6
Copy full SHA for c3042c6

4 files changed

+488-1Lines changed: 488 additions & 1 deletion

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎deps/ncrypto/ncrypto.h‎

Copy file name to clipboardExpand all lines: deps/ncrypto/ncrypto.h
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,9 @@ class EVPKeyPointer final {
858858
DER,
859859
PEM,
860860
JWK,
861+
RAW_PUBLIC,
862+
RAW_PRIVATE,
863+
RAW_SEED,
861864
};
862865

863866
enum class PKParseError { NOT_RECOGNIZED, NEED_PASSPHRASE, FAILED };
@@ -867,6 +870,7 @@ class EVPKeyPointer final {
867870
bool output_key_object = false;
868871
PKFormatType format = PKFormatType::DER;
869872
PKEncodingType type = PKEncodingType::PKCS8;
873+
int ec_point_form = POINT_CONVERSION_UNCOMPRESSED;
870874
AsymmetricKeyEncodingConfig() = default;
871875
AsymmetricKeyEncodingConfig(bool output_key_object,
872876
PKFormatType format,
Collapse file

‎lib/internal/crypto/keys.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/keys.js
+44Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ const {
2121
kKeyFormatPEM,
2222
kKeyFormatDER,
2323
kKeyFormatJWK,
24+
kKeyFormatRawPublic,
25+
kKeyFormatRawPrivate,
26+
kKeyFormatRawSeed,
2427
kKeyEncodingPKCS1,
2528
kKeyEncodingPKCS8,
2629
kKeyEncodingSPKI,
@@ -433,6 +436,12 @@ function parseKeyFormat(formatStr, defaultFormat, optionName) {
433436
return kKeyFormatDER;
434437
else if (formatStr === 'jwk')
435438
return kKeyFormatJWK;
439+
else if (formatStr === 'raw-public')
440+
return kKeyFormatRawPublic;
441+
else if (formatStr === 'raw-private')
442+
return kKeyFormatRawPrivate;
443+
else if (formatStr === 'raw-seed')
444+
return kKeyFormatRawSeed;
436445
throw new ERR_INVALID_ARG_VALUE(optionName, formatStr);
437446
}
438447

@@ -473,6 +482,33 @@ function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
473482
isInput ? kKeyFormatPEM : undefined,
474483
option('format', objName));
475484

485+
if (format === kKeyFormatRawPublic) {
486+
if (isPublic === false) {
487+
throw new ERR_INVALID_ARG_VALUE(option('format', objName), 'raw-public');
488+
}
489+
let type;
490+
if (typeStr === undefined || typeStr === 'uncompressed') {
491+
type = POINT_CONVERSION_UNCOMPRESSED;
492+
} else if (typeStr === 'compressed') {
493+
type = POINT_CONVERSION_COMPRESSED;
494+
} else {
495+
throw new ERR_INVALID_ARG_VALUE(option('type', objName), typeStr);
496+
}
497+
return { format, type };
498+
}
499+
500+
if (format === kKeyFormatRawPrivate || format === kKeyFormatRawSeed) {
501+
if (isPublic === true) {
502+
throw new ERR_INVALID_ARG_VALUE(
503+
option('format', objName),
504+
format === kKeyFormatRawPrivate ? 'raw-private' : 'raw-seed');
505+
}
506+
if (typeStr !== undefined) {
507+
throw new ERR_INVALID_ARG_VALUE(option('type', objName), typeStr);
508+
}
509+
return { format };
510+
}
511+
476512
const isRequired = (!isInput ||
477513
format === kKeyFormatDER) &&
478514
format !== kKeyFormatJWK;
@@ -504,6 +540,14 @@ function parseKeyEncoding(enc, keyType, isPublic, objName) {
504540
if (isPublic !== true) {
505541
({ cipher, passphrase, encoding } = enc);
506542

543+
if (format === kKeyFormatRawPrivate || format === kKeyFormatRawSeed) {
544+
if (cipher != null || passphrase !== undefined) {
545+
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
546+
'raw format', 'does not support encryption');
547+
}
548+
return { format, type };
549+
}
550+
507551
if (!isInput) {
508552
if (cipher != null) {
509553
if (typeof cipher !== 'string')
Collapse file

‎src/crypto/crypto_keys.cc‎

Copy file name to clipboardExpand all lines: src/crypto/crypto_keys.cc
+155-1Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,16 @@ Maybe<EVPKeyPointer::AsymmetricKeyEncodingConfig> GetKeyFormatAndTypeFromJs(
6666
config.format = static_cast<EVPKeyPointer::PKFormatType>(
6767
args[*offset].As<Int32>()->Value());
6868

69-
if (args[*offset + 1]->IsInt32()) {
69+
if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC ||
70+
config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE ||
71+
config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
72+
// Raw formats use the type slot for ec_point_form (int) or null.
73+
if (args[*offset + 1]->IsInt32()) {
74+
config.ec_point_form = args[*offset + 1].As<Int32>()->Value();
75+
} else {
76+
CHECK(args[*offset + 1]->IsNullOrUndefined());
77+
}
78+
} else if (args[*offset + 1]->IsInt32()) {
7079
config.type = static_cast<EVPKeyPointer::PKEncodingType>(
7180
args[*offset + 1].As<Int32>()->Value());
7281
} else {
@@ -329,6 +338,54 @@ bool KeyObjectData::ToEncodedPublicKey(
329338
*out = Object::New(env->isolate());
330339
return ExportJWKInner(
331340
env, addRefWithType(KeyType::kKeyTypePublic), *out, false);
341+
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) {
342+
Mutex::ScopedLock lock(mutex());
343+
const auto& pkey = GetAsymmetricKey();
344+
if (pkey.id() == EVP_PKEY_EC) {
345+
const EC_KEY* ec_key = pkey;
346+
CHECK_NOT_NULL(ec_key);
347+
auto form = static_cast<point_conversion_form_t>(config.ec_point_form);
348+
const auto group = ECKeyPointer::GetGroup(ec_key);
349+
const auto point = ECKeyPointer::GetPublicKey(ec_key);
350+
return ECPointToBuffer(env, group, point, form).ToLocal(out);
351+
}
352+
switch (pkey.id()) {
353+
case EVP_PKEY_ED25519:
354+
case EVP_PKEY_ED448:
355+
case EVP_PKEY_X25519:
356+
case EVP_PKEY_X448:
357+
#if OPENSSL_WITH_PQC
358+
case EVP_PKEY_ML_DSA_44:
359+
case EVP_PKEY_ML_DSA_65:
360+
case EVP_PKEY_ML_DSA_87:
361+
case EVP_PKEY_ML_KEM_512:
362+
case EVP_PKEY_ML_KEM_768:
363+
case EVP_PKEY_ML_KEM_1024:
364+
case EVP_PKEY_SLH_DSA_SHA2_128F:
365+
case EVP_PKEY_SLH_DSA_SHA2_128S:
366+
case EVP_PKEY_SLH_DSA_SHA2_192F:
367+
case EVP_PKEY_SLH_DSA_SHA2_192S:
368+
case EVP_PKEY_SLH_DSA_SHA2_256F:
369+
case EVP_PKEY_SLH_DSA_SHA2_256S:
370+
case EVP_PKEY_SLH_DSA_SHAKE_128F:
371+
case EVP_PKEY_SLH_DSA_SHAKE_128S:
372+
case EVP_PKEY_SLH_DSA_SHAKE_192F:
373+
case EVP_PKEY_SLH_DSA_SHAKE_192S:
374+
case EVP_PKEY_SLH_DSA_SHAKE_256F:
375+
case EVP_PKEY_SLH_DSA_SHAKE_256S:
376+
#endif
377+
break;
378+
default:
379+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
380+
return false;
381+
}
382+
auto raw_data = pkey.rawPublicKey();
383+
if (!raw_data) {
384+
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw public key");
385+
return false;
386+
}
387+
return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size())
388+
.ToLocal(out);
332389
}
333390

334391
return WritePublicKey(env, GetAsymmetricKey(), config).ToLocal(out);
@@ -347,6 +404,86 @@ bool KeyObjectData::ToEncodedPrivateKey(
347404
*out = Object::New(env->isolate());
348405
return ExportJWKInner(
349406
env, addRefWithType(KeyType::kKeyTypePrivate), *out, false);
407+
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE) {
408+
Mutex::ScopedLock lock(mutex());
409+
const auto& pkey = GetAsymmetricKey();
410+
if (pkey.id() == EVP_PKEY_EC) {
411+
const EC_KEY* ec_key = pkey;
412+
CHECK_NOT_NULL(ec_key);
413+
const BIGNUM* private_key = ECKeyPointer::GetPrivateKey(ec_key);
414+
CHECK_NOT_NULL(private_key);
415+
const auto group = ECKeyPointer::GetGroup(ec_key);
416+
auto order = BignumPointer::New();
417+
CHECK(order);
418+
CHECK(EC_GROUP_get_order(group, order.get(), nullptr));
419+
auto buf = BignumPointer::EncodePadded(private_key, order.byteLength());
420+
if (!buf) {
421+
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
422+
"Failed to export EC private key");
423+
return false;
424+
}
425+
return Buffer::Copy(env, buf.get<const char>(), buf.size()).ToLocal(out);
426+
}
427+
switch (pkey.id()) {
428+
case EVP_PKEY_ED25519:
429+
case EVP_PKEY_ED448:
430+
case EVP_PKEY_X25519:
431+
case EVP_PKEY_X448:
432+
#if OPENSSL_WITH_PQC
433+
case EVP_PKEY_SLH_DSA_SHA2_128F:
434+
case EVP_PKEY_SLH_DSA_SHA2_128S:
435+
case EVP_PKEY_SLH_DSA_SHA2_192F:
436+
case EVP_PKEY_SLH_DSA_SHA2_192S:
437+
case EVP_PKEY_SLH_DSA_SHA2_256F:
438+
case EVP_PKEY_SLH_DSA_SHA2_256S:
439+
case EVP_PKEY_SLH_DSA_SHAKE_128F:
440+
case EVP_PKEY_SLH_DSA_SHAKE_128S:
441+
case EVP_PKEY_SLH_DSA_SHAKE_192F:
442+
case EVP_PKEY_SLH_DSA_SHAKE_192S:
443+
case EVP_PKEY_SLH_DSA_SHAKE_256F:
444+
case EVP_PKEY_SLH_DSA_SHAKE_256S:
445+
#endif
446+
break;
447+
default:
448+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
449+
return false;
450+
}
451+
auto raw_data = pkey.rawPrivateKey();
452+
if (!raw_data) {
453+
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw private key");
454+
return false;
455+
}
456+
return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size())
457+
.ToLocal(out);
458+
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
459+
Mutex::ScopedLock lock(mutex());
460+
const auto& pkey = GetAsymmetricKey();
461+
switch (pkey.id()) {
462+
#if OPENSSL_WITH_PQC
463+
case EVP_PKEY_ML_DSA_44:
464+
case EVP_PKEY_ML_DSA_65:
465+
case EVP_PKEY_ML_DSA_87:
466+
case EVP_PKEY_ML_KEM_512:
467+
case EVP_PKEY_ML_KEM_768:
468+
case EVP_PKEY_ML_KEM_1024:
469+
break;
470+
#endif
471+
default:
472+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
473+
return false;
474+
}
475+
#if OPENSSL_WITH_PQC
476+
auto raw_data = pkey.rawSeed();
477+
if (!raw_data) {
478+
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get raw seed");
479+
return false;
480+
}
481+
return Buffer::Copy(env, raw_data.get<const char>(), raw_data.size())
482+
.ToLocal(out);
483+
#else
484+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
485+
return false;
486+
#endif
350487
}
351488

352489
return WritePrivateKey(env, GetAsymmetricKey(), config).ToLocal(out);
@@ -367,6 +504,14 @@ KeyObjectData::GetPrivateKeyEncodingFromJs(
367504
if (config.output_key_object) {
368505
if (context != kKeyContextInput)
369506
(*offset)++;
507+
} else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE ||
508+
config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
509+
// Raw formats don't support encryption. Still consume the arg offsets.
510+
if (context != kKeyContextInput) {
511+
CHECK(args[*offset]->IsNullOrUndefined());
512+
(*offset)++;
513+
}
514+
CHECK(args[*offset]->IsNullOrUndefined());
370515
} else {
371516
bool needs_passphrase = false;
372517
if (context != kKeyContextInput) {
@@ -1557,6 +1702,12 @@ void Initialize(Environment* env, Local<Object> target) {
15571702
static_cast<int>(EVPKeyPointer::PKFormatType::PEM);
15581703
constexpr int kKeyFormatJWK =
15591704
static_cast<int>(EVPKeyPointer::PKFormatType::JWK);
1705+
constexpr int kKeyFormatRawPublic =
1706+
static_cast<int>(EVPKeyPointer::PKFormatType::RAW_PUBLIC);
1707+
constexpr int kKeyFormatRawPrivate =
1708+
static_cast<int>(EVPKeyPointer::PKFormatType::RAW_PRIVATE);
1709+
constexpr int kKeyFormatRawSeed =
1710+
static_cast<int>(EVPKeyPointer::PKFormatType::RAW_SEED);
15601711

15611712
constexpr auto kSigEncDER = DSASigEnc::DER;
15621713
constexpr auto kSigEncP1363 = DSASigEnc::P1363;
@@ -1596,6 +1747,9 @@ void Initialize(Environment* env, Local<Object> target) {
15961747
NODE_DEFINE_CONSTANT(target, kKeyFormatDER);
15971748
NODE_DEFINE_CONSTANT(target, kKeyFormatPEM);
15981749
NODE_DEFINE_CONSTANT(target, kKeyFormatJWK);
1750+
NODE_DEFINE_CONSTANT(target, kKeyFormatRawPublic);
1751+
NODE_DEFINE_CONSTANT(target, kKeyFormatRawPrivate);
1752+
NODE_DEFINE_CONSTANT(target, kKeyFormatRawSeed);
15991753
NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
16001754
NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
16011755
NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.