Skip to content

Commit c3042c6

Browse 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

4 files changed

Lines changed: 488 additions & 1 deletion

File tree

‎deps/ncrypto/ncrypto.h‎

Lines 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
enumclassPKParseError { 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,

‎lib/internal/crypto/keys.js‎

Lines 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
returnkKeyFormatDER;
434437
elseif(formatStr==='jwk')
435438
returnkKeyFormatJWK;
439+
elseif(formatStr==='raw-public')
440+
returnkKeyFormatRawPublic;
441+
elseif(formatStr==='raw-private')
442+
returnkKeyFormatRawPrivate;
443+
elseif(formatStr==='raw-seed')
444+
returnkKeyFormatRawSeed;
436445
thrownewERR_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+
thrownewERR_INVALID_ARG_VALUE(option('format',objName),'raw-public');
488+
}
489+
lettype;
490+
if(typeStr===undefined||typeStr==='uncompressed'){
491+
type=POINT_CONVERSION_UNCOMPRESSED;
492+
}elseif(typeStr==='compressed'){
493+
type=POINT_CONVERSION_COMPRESSED;
494+
}else{
495+
thrownewERR_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+
thrownewERR_INVALID_ARG_VALUE(
503+
option('format',objName),
504+
format===kKeyFormatRawPrivate ? 'raw-private' : 'raw-seed');
505+
}
506+
if(typeStr!==undefined){
507+
thrownewERR_INVALID_ARG_VALUE(option('type',objName),typeStr);
508+
}
509+
return{ format };
510+
}
511+
476512
constisRequired=(!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+
thrownewERR_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(typeofcipher!=='string')

‎src/crypto/crypto_keys.cc‎

Lines 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+
} elseif (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
returnExportJWKInner(
331340
env, addRefWithType(KeyType::kKeyTypePublic), *out, false);
341+
} elseif (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) {
342+
Mutex::ScopedLock lock(mutex());
343+
constauto& pkey = GetAsymmetricKey();
344+
if (pkey.id() == EVP_PKEY_EC) {
345+
constEC_KEY* ec_key = pkey;
346+
CHECK_NOT_NULL(ec_key);
347+
auto form = static_cast<point_conversion_form_t>(config.ec_point_form);
348+
constauto group = ECKeyPointer::GetGroup(ec_key);
349+
constauto point = ECKeyPointer::GetPublicKey(ec_key);
350+
returnECPointToBuffer(env, group, point, form).ToLocal(out);
351+
}
352+
switch (pkey.id()) {
353+
caseEVP_PKEY_ED25519:
354+
caseEVP_PKEY_ED448:
355+
caseEVP_PKEY_X25519:
356+
caseEVP_PKEY_X448:
357+
#if OPENSSL_WITH_PQC
358+
caseEVP_PKEY_ML_DSA_44:
359+
caseEVP_PKEY_ML_DSA_65:
360+
caseEVP_PKEY_ML_DSA_87:
361+
caseEVP_PKEY_ML_KEM_512:
362+
caseEVP_PKEY_ML_KEM_768:
363+
caseEVP_PKEY_ML_KEM_1024:
364+
caseEVP_PKEY_SLH_DSA_SHA2_128F:
365+
caseEVP_PKEY_SLH_DSA_SHA2_128S:
366+
caseEVP_PKEY_SLH_DSA_SHA2_192F:
367+
caseEVP_PKEY_SLH_DSA_SHA2_192S:
368+
caseEVP_PKEY_SLH_DSA_SHA2_256F:
369+
caseEVP_PKEY_SLH_DSA_SHA2_256S:
370+
caseEVP_PKEY_SLH_DSA_SHAKE_128F:
371+
caseEVP_PKEY_SLH_DSA_SHAKE_128S:
372+
caseEVP_PKEY_SLH_DSA_SHAKE_192F:
373+
caseEVP_PKEY_SLH_DSA_SHAKE_192S:
374+
caseEVP_PKEY_SLH_DSA_SHAKE_256F:
375+
caseEVP_PKEY_SLH_DSA_SHAKE_256S:
376+
#endif
377+
break;
378+
default:
379+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
380+
returnfalse;
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+
returnfalse;
386+
}
387+
returnBuffer::Copy(env, raw_data.get<constchar>(), raw_data.size())
388+
.ToLocal(out);
332389
}
333390

334391
returnWritePublicKey(env, GetAsymmetricKey(), config).ToLocal(out);
@@ -347,6 +404,86 @@ bool KeyObjectData::ToEncodedPrivateKey(
347404
*out = Object::New(env->isolate());
348405
returnExportJWKInner(
349406
env, addRefWithType(KeyType::kKeyTypePrivate), *out, false);
407+
} elseif (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE) {
408+
Mutex::ScopedLock lock(mutex());
409+
constauto& pkey = GetAsymmetricKey();
410+
if (pkey.id() == EVP_PKEY_EC) {
411+
constEC_KEY* ec_key = pkey;
412+
CHECK_NOT_NULL(ec_key);
413+
constBIGNUM* private_key = ECKeyPointer::GetPrivateKey(ec_key);
414+
CHECK_NOT_NULL(private_key);
415+
constauto 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+
returnfalse;
424+
}
425+
returnBuffer::Copy(env, buf.get<constchar>(), buf.size()).ToLocal(out);
426+
}
427+
switch (pkey.id()) {
428+
caseEVP_PKEY_ED25519:
429+
caseEVP_PKEY_ED448:
430+
caseEVP_PKEY_X25519:
431+
caseEVP_PKEY_X448:
432+
#if OPENSSL_WITH_PQC
433+
caseEVP_PKEY_SLH_DSA_SHA2_128F:
434+
caseEVP_PKEY_SLH_DSA_SHA2_128S:
435+
caseEVP_PKEY_SLH_DSA_SHA2_192F:
436+
caseEVP_PKEY_SLH_DSA_SHA2_192S:
437+
caseEVP_PKEY_SLH_DSA_SHA2_256F:
438+
caseEVP_PKEY_SLH_DSA_SHA2_256S:
439+
caseEVP_PKEY_SLH_DSA_SHAKE_128F:
440+
caseEVP_PKEY_SLH_DSA_SHAKE_128S:
441+
caseEVP_PKEY_SLH_DSA_SHAKE_192F:
442+
caseEVP_PKEY_SLH_DSA_SHAKE_192S:
443+
caseEVP_PKEY_SLH_DSA_SHAKE_256F:
444+
caseEVP_PKEY_SLH_DSA_SHAKE_256S:
445+
#endif
446+
break;
447+
default:
448+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
449+
returnfalse;
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+
returnfalse;
455+
}
456+
returnBuffer::Copy(env, raw_data.get<constchar>(), raw_data.size())
457+
.ToLocal(out);
458+
} elseif (config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
459+
Mutex::ScopedLock lock(mutex());
460+
constauto& pkey = GetAsymmetricKey();
461+
switch (pkey.id()) {
462+
#if OPENSSL_WITH_PQC
463+
caseEVP_PKEY_ML_DSA_44:
464+
caseEVP_PKEY_ML_DSA_65:
465+
caseEVP_PKEY_ML_DSA_87:
466+
caseEVP_PKEY_ML_KEM_512:
467+
caseEVP_PKEY_ML_KEM_768:
468+
caseEVP_PKEY_ML_KEM_1024:
469+
break;
470+
#endif
471+
default:
472+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
473+
returnfalse;
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+
returnfalse;
480+
}
481+
returnBuffer::Copy(env, raw_data.get<constchar>(), raw_data.size())
482+
.ToLocal(out);
483+
#else
484+
THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(env);
485+
returnfalse;
486+
#endif
350487
}
351488

352489
returnWritePrivateKey(env, GetAsymmetricKey(), config).ToLocal(out);
@@ -367,6 +504,14 @@ KeyObjectData::GetPrivateKeyEncodingFromJs(
367504
if (config.output_key_object) {
368505
if (context != kKeyContextInput)
369506
(*offset)++;
507+
} elseif (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
constexprintkKeyFormatJWK =
15591704
static_cast<int>(EVPKeyPointer::PKFormatType::JWK);
1705+
constexprintkKeyFormatRawPublic =
1706+
static_cast<int>(EVPKeyPointer::PKFormatType::RAW_PUBLIC);
1707+
constexprintkKeyFormatRawPrivate =
1708+
static_cast<int>(EVPKeyPointer::PKFormatType::RAW_PRIVATE);
1709+
constexprintkKeyFormatRawSeed =
1710+
static_cast<int>(EVPKeyPointer::PKFormatType::RAW_SEED);
15601711

15611712
constexprautokSigEncDER = DSASigEnc::DER;
15621713
constexprautokSigEncP1363 = 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)