@@ -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