@@ -67,7 +67,16 @@ Maybe<EVPKeyPointer::AsymmetricKeyEncodingConfig> GetKeyFormatAndTypeFromJs(
6767 config.format = static_cast <EVPKeyPointer::PKFormatType>(
6868 args[*offset].As <Int32>()->Value ());
6969
70- if (args[*offset + 1 ]->IsInt32 ()) {
70+ if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC ||
71+ config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE ||
72+ config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
73+ // Raw formats use the type slot for ec_point_form (int) or null.
74+ if (args[*offset + 1 ]->IsInt32 ()) {
75+ config.ec_point_form = args[*offset + 1 ].As <Int32>()->Value ();
76+ } else {
77+ CHECK (args[*offset + 1 ]->IsNullOrUndefined ());
78+ }
79+ } else if (args[*offset + 1 ]->IsInt32 ()) {
7180 config.type = static_cast <EVPKeyPointer::PKEncodingType>(
7281 args[*offset + 1 ].As <Int32>()->Value ());
7382 } else {
@@ -325,6 +334,54 @@ bool KeyObjectData::ToEncodedPublicKey(
325334 *out = Object::New (env->isolate ());
326335 return ExportJWKInner (
327336 env, addRefWithType (KeyType::kKeyTypePublic ), *out, false );
337+ } else if (config.format == EVPKeyPointer::PKFormatType::RAW_PUBLIC) {
338+ Mutex::ScopedLock lock (mutex ());
339+ const auto & pkey = GetAsymmetricKey ();
340+ if (pkey.id () == EVP_PKEY_EC) {
341+ const EC_KEY* ec_key = pkey;
342+ CHECK_NOT_NULL (ec_key);
343+ auto form = static_cast <point_conversion_form_t >(config.ec_point_form );
344+ const auto group = ECKeyPointer::GetGroup (ec_key);
345+ const auto point = ECKeyPointer::GetPublicKey (ec_key);
346+ return ECPointToBuffer (env, group, point, form).ToLocal (out);
347+ }
348+ switch (pkey.id ()) {
349+ case EVP_PKEY_ED25519:
350+ case EVP_PKEY_ED448:
351+ case EVP_PKEY_X25519:
352+ case EVP_PKEY_X448:
353+ #if OPENSSL_WITH_PQC
354+ case EVP_PKEY_ML_DSA_44:
355+ case EVP_PKEY_ML_DSA_65:
356+ case EVP_PKEY_ML_DSA_87:
357+ case EVP_PKEY_ML_KEM_512:
358+ case EVP_PKEY_ML_KEM_768:
359+ case EVP_PKEY_ML_KEM_1024:
360+ case EVP_PKEY_SLH_DSA_SHA2_128F:
361+ case EVP_PKEY_SLH_DSA_SHA2_128S:
362+ case EVP_PKEY_SLH_DSA_SHA2_192F:
363+ case EVP_PKEY_SLH_DSA_SHA2_192S:
364+ case EVP_PKEY_SLH_DSA_SHA2_256F:
365+ case EVP_PKEY_SLH_DSA_SHA2_256S:
366+ case EVP_PKEY_SLH_DSA_SHAKE_128F:
367+ case EVP_PKEY_SLH_DSA_SHAKE_128S:
368+ case EVP_PKEY_SLH_DSA_SHAKE_192F:
369+ case EVP_PKEY_SLH_DSA_SHAKE_192S:
370+ case EVP_PKEY_SLH_DSA_SHAKE_256F:
371+ case EVP_PKEY_SLH_DSA_SHAKE_256S:
372+ #endif
373+ break ;
374+ default :
375+ THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (env);
376+ return false ;
377+ }
378+ auto raw_data = pkey.rawPublicKey ();
379+ if (!raw_data) {
380+ THROW_ERR_CRYPTO_OPERATION_FAILED (env, " Failed to get raw public key" );
381+ return false ;
382+ }
383+ return Buffer::Copy (env, raw_data.get <const char >(), raw_data.size ())
384+ .ToLocal (out);
328385 }
329386
330387 return WritePublicKey (env, GetAsymmetricKey (), config).ToLocal (out);
@@ -343,6 +400,86 @@ bool KeyObjectData::ToEncodedPrivateKey(
343400 *out = Object::New (env->isolate ());
344401 return ExportJWKInner (
345402 env, addRefWithType (KeyType::kKeyTypePrivate ), *out, false );
403+ } else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE) {
404+ Mutex::ScopedLock lock (mutex ());
405+ const auto & pkey = GetAsymmetricKey ();
406+ if (pkey.id () == EVP_PKEY_EC) {
407+ const EC_KEY* ec_key = pkey;
408+ CHECK_NOT_NULL (ec_key);
409+ const BIGNUM* private_key = ECKeyPointer::GetPrivateKey (ec_key);
410+ CHECK_NOT_NULL (private_key);
411+ const auto group = ECKeyPointer::GetGroup (ec_key);
412+ auto order = BignumPointer::New ();
413+ CHECK (order);
414+ CHECK (EC_GROUP_get_order (group, order.get (), nullptr ));
415+ auto buf = BignumPointer::EncodePadded (private_key, order.byteLength ());
416+ if (!buf) {
417+ THROW_ERR_CRYPTO_OPERATION_FAILED (env,
418+ " Failed to export EC private key" );
419+ return false ;
420+ }
421+ return Buffer::Copy (env, buf.get <const char >(), buf.size ()).ToLocal (out);
422+ }
423+ switch (pkey.id ()) {
424+ case EVP_PKEY_ED25519:
425+ case EVP_PKEY_ED448:
426+ case EVP_PKEY_X25519:
427+ case EVP_PKEY_X448:
428+ #if OPENSSL_WITH_PQC
429+ case EVP_PKEY_SLH_DSA_SHA2_128F:
430+ case EVP_PKEY_SLH_DSA_SHA2_128S:
431+ case EVP_PKEY_SLH_DSA_SHA2_192F:
432+ case EVP_PKEY_SLH_DSA_SHA2_192S:
433+ case EVP_PKEY_SLH_DSA_SHA2_256F:
434+ case EVP_PKEY_SLH_DSA_SHA2_256S:
435+ case EVP_PKEY_SLH_DSA_SHAKE_128F:
436+ case EVP_PKEY_SLH_DSA_SHAKE_128S:
437+ case EVP_PKEY_SLH_DSA_SHAKE_192F:
438+ case EVP_PKEY_SLH_DSA_SHAKE_192S:
439+ case EVP_PKEY_SLH_DSA_SHAKE_256F:
440+ case EVP_PKEY_SLH_DSA_SHAKE_256S:
441+ #endif
442+ break ;
443+ default :
444+ THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (env);
445+ return false ;
446+ }
447+ auto raw_data = pkey.rawPrivateKey ();
448+ if (!raw_data) {
449+ THROW_ERR_CRYPTO_OPERATION_FAILED (env, " Failed to get raw private key" );
450+ return false ;
451+ }
452+ return Buffer::Copy (env, raw_data.get <const char >(), raw_data.size ())
453+ .ToLocal (out);
454+ } else if (config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
455+ Mutex::ScopedLock lock (mutex ());
456+ const auto & pkey = GetAsymmetricKey ();
457+ switch (pkey.id ()) {
458+ #if OPENSSL_WITH_PQC
459+ case EVP_PKEY_ML_DSA_44:
460+ case EVP_PKEY_ML_DSA_65:
461+ case EVP_PKEY_ML_DSA_87:
462+ case EVP_PKEY_ML_KEM_512:
463+ case EVP_PKEY_ML_KEM_768:
464+ case EVP_PKEY_ML_KEM_1024:
465+ break ;
466+ #endif
467+ default :
468+ THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (env);
469+ return false ;
470+ }
471+ #if OPENSSL_WITH_PQC
472+ auto raw_data = pkey.rawSeed ();
473+ if (!raw_data) {
474+ THROW_ERR_CRYPTO_OPERATION_FAILED (env, " Failed to get raw seed" );
475+ return false ;
476+ }
477+ return Buffer::Copy (env, raw_data.get <const char >(), raw_data.size ())
478+ .ToLocal (out);
479+ #else
480+ THROW_ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (env);
481+ return false ;
482+ #endif
346483 }
347484
348485 return WritePrivateKey (env, GetAsymmetricKey (), config).ToLocal (out);
@@ -363,6 +500,14 @@ KeyObjectData::GetPrivateKeyEncodingFromJs(
363500 if (config.output_key_object ) {
364501 if (context != kKeyContextInput )
365502 (*offset)++;
503+ } else if (config.format == EVPKeyPointer::PKFormatType::RAW_PRIVATE ||
504+ config.format == EVPKeyPointer::PKFormatType::RAW_SEED) {
505+ // Raw formats don't support encryption. Still consume the arg offsets.
506+ if (context != kKeyContextInput ) {
507+ CHECK (args[*offset]->IsNullOrUndefined ());
508+ (*offset)++;
509+ }
510+ CHECK (args[*offset]->IsNullOrUndefined ());
366511 } else {
367512 bool needs_passphrase = false ;
368513 if (context != kKeyContextInput ) {
@@ -1581,6 +1726,12 @@ void Initialize(Environment* env, Local<Object> target) {
15811726 static_cast <int >(EVPKeyPointer::PKFormatType::PEM);
15821727 constexpr int kKeyFormatJWK =
15831728 static_cast <int >(EVPKeyPointer::PKFormatType::JWK);
1729+ constexpr int kKeyFormatRawPublic =
1730+ static_cast <int >(EVPKeyPointer::PKFormatType::RAW_PUBLIC);
1731+ constexpr int kKeyFormatRawPrivate =
1732+ static_cast <int >(EVPKeyPointer::PKFormatType::RAW_PRIVATE);
1733+ constexpr int kKeyFormatRawSeed =
1734+ static_cast <int >(EVPKeyPointer::PKFormatType::RAW_SEED);
15841735
15851736 constexpr auto kSigEncDER = DSASigEnc::DER;
15861737 constexpr auto kSigEncP1363 = DSASigEnc::P1363;
@@ -1620,6 +1771,9 @@ void Initialize(Environment* env, Local<Object> target) {
16201771 NODE_DEFINE_CONSTANT (target, kKeyFormatDER );
16211772 NODE_DEFINE_CONSTANT (target, kKeyFormatPEM );
16221773 NODE_DEFINE_CONSTANT (target, kKeyFormatJWK );
1774+ NODE_DEFINE_CONSTANT (target, kKeyFormatRawPublic );
1775+ NODE_DEFINE_CONSTANT (target, kKeyFormatRawPrivate );
1776+ NODE_DEFINE_CONSTANT (target, kKeyFormatRawSeed );
16231777 NODE_DEFINE_CONSTANT (target, kKeyTypeSecret );
16241778 NODE_DEFINE_CONSTANT (target, kKeyTypePublic );
16251779 NODE_DEFINE_CONSTANT (target, kKeyTypePrivate );
0 commit comments