@@ -27,6 +27,15 @@ const {
2727 kKeyEncodingSEC1,
2828} = internalBinding ( 'crypto' ) ;
2929
30+ const hasPqcSupport = KeyObjectHandle . prototype . initPqcRaw !== undefined ;
31+
32+ const {
33+ crypto : {
34+ POINT_CONVERSION_COMPRESSED ,
35+ POINT_CONVERSION_UNCOMPRESSED ,
36+ } ,
37+ } = internalBinding ( 'constants' ) ;
38+
3039const {
3140 validateObject,
3241 validateOneOf,
@@ -82,6 +91,7 @@ const kKeyUsages = Symbol('kKeyUsages');
8291const kCachedAlgorithm = Symbol ( 'kCachedAlgorithm' ) ;
8392const kCachedKeyUsages = Symbol ( 'kCachedKeyUsages' ) ;
8493
94+
8595// Key input contexts.
8696const kConsumePublic = 0 ;
8797const kConsumePrivate = 1 ;
@@ -340,14 +350,27 @@ const {
340350 }
341351
342352 export ( options ) {
343- if ( options && options . format === 'jwk' ) {
344- return this [ kHandle ] . exportJwk ( { } , false ) ;
353+ switch ( options ?. format ) {
354+ case 'jwk' :
355+ return this [ kHandle ] . exportJwk ( { } , false ) ;
356+ case 'raw-public' : {
357+ if ( this . asymmetricKeyType === 'ec' ) {
358+ const { type = 'compressed' } = options ;
359+ validateOneOf ( type , 'options.type' , [ 'compressed' , 'uncompressed' ] ) ;
360+ const form = type === 'compressed' ?
361+ POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED ;
362+ return this [ kHandle ] . exportECPublicRaw ( form ) ;
363+ }
364+ return this [ kHandle ] . rawPublicKey ( ) ;
365+ }
366+ default : {
367+ const {
368+ format,
369+ type,
370+ } = parsePublicKeyEncoding ( options , this . asymmetricKeyType ) ;
371+ return this [ kHandle ] . export ( format , type ) ;
372+ }
345373 }
346- const {
347- format,
348- type,
349- } = parsePublicKeyEncoding ( options , this . asymmetricKeyType ) ;
350- return this [ kHandle ] . export ( format , type ) ;
351374 }
352375 }
353376
@@ -357,20 +380,32 @@ const {
357380 }
358381
359382 export ( options ) {
360- if ( options && options . format === 'jwk' ) {
361- if ( options . passphrase !== undefined ) {
362- throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (
363- 'jwk' , 'does not support encryption' ) ;
383+ if ( options ?. passphrase !== undefined &&
384+ options . format !== 'pem' && options . format !== 'der' ) {
385+ throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (
386+ options . format , 'does not support encryption' ) ;
387+ }
388+ switch ( options ?. format ) {
389+ case 'jwk' :
390+ return this [ kHandle ] . exportJwk ( { } , false ) ;
391+ case 'raw-private' : {
392+ if ( this . asymmetricKeyType === 'ec' ) {
393+ return this [ kHandle ] . exportECPrivateRaw ( ) ;
394+ }
395+ return this [ kHandle ] . rawPrivateKey ( ) ;
396+ }
397+ case 'raw-seed' :
398+ return this [ kHandle ] . rawSeed ( ) ;
399+ default : {
400+ const {
401+ format,
402+ type,
403+ cipher,
404+ passphrase,
405+ } = parsePrivateKeyEncoding ( options , this . asymmetricKeyType ) ;
406+ return this [ kHandle ] . export ( format , type , cipher , passphrase ) ;
364407 }
365- return this [ kHandle ] . exportJwk ( { } , false ) ;
366408 }
367- const {
368- format,
369- type,
370- cipher,
371- passphrase,
372- } = parsePrivateKeyEncoding ( options , this . asymmetricKeyType ) ;
373- return this [ kHandle ] . export ( format , type , cipher , passphrase ) ;
374409 }
375410 }
376411
@@ -549,7 +584,7 @@ function mlDsaPubLen(alg) {
549584
550585function getKeyObjectHandleFromJwk ( key , ctx ) {
551586 validateObject ( key , 'key' ) ;
552- if ( KeyObjectHandle . prototype . initPqcRaw ) {
587+ if ( hasPqcSupport ) {
553588 validateOneOf (
554589 key . kty , 'key.kty' , [ 'RSA' , 'EC' , 'OKP' , 'AKP' ] ) ;
555590 } else {
@@ -691,6 +726,82 @@ function getKeyObjectHandleFromJwk(key, ctx) {
691726 return handle ;
692727}
693728
729+
730+ function getKeyObjectHandleFromRaw ( options , data , format ) {
731+ if ( ! isStringOrBuffer ( data ) ) {
732+ throw new ERR_INVALID_ARG_TYPE (
733+ 'key.key' ,
734+ [ 'ArrayBuffer' , 'Buffer' , 'TypedArray' , 'DataView' ] ,
735+ data ) ;
736+ }
737+
738+ const keyData = getArrayBufferOrView ( data , 'key.key' ) ;
739+
740+ validateString ( options . asymmetricKeyType , 'key.asymmetricKeyType' ) ;
741+ const asymmetricKeyType = options . asymmetricKeyType ;
742+
743+ const handle = new KeyObjectHandle ( ) ;
744+
745+ switch ( asymmetricKeyType ) {
746+ case 'ec' : {
747+ validateString ( options . namedCurve , 'key.namedCurve' ) ;
748+ if ( format === 'raw-public' ) {
749+ if ( ! handle . initECRaw ( options . namedCurve , keyData ) ) {
750+ throw new ERR_INVALID_ARG_VALUE ( 'key.key' , keyData ) ;
751+ }
752+ } else if ( ! handle . initECPrivateRaw ( options . namedCurve , keyData ) ) {
753+ throw new ERR_INVALID_ARG_VALUE ( 'key.key' , keyData ) ;
754+ }
755+ return handle ;
756+ }
757+ case 'ed25519' :
758+ case 'ed448' :
759+ case 'x25519' :
760+ case 'x448' : {
761+ const keyType = format === 'raw-public' ? kKeyTypePublic : kKeyTypePrivate ;
762+ if ( ! handle . initEDRaw ( asymmetricKeyType , keyData , keyType ) ) {
763+ throw new ERR_INVALID_ARG_VALUE ( 'key.key' , keyData ) ;
764+ }
765+ return handle ;
766+ }
767+ case 'rsa' :
768+ case 'rsa-pss' :
769+ case 'dsa' :
770+ case 'dh' :
771+ throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS (
772+ format , `is not supported for ${ asymmetricKeyType } keys` ) ;
773+ case 'ml-dsa-44' :
774+ case 'ml-dsa-65' :
775+ case 'ml-dsa-87' :
776+ case 'ml-kem-512' :
777+ case 'ml-kem-768' :
778+ case 'ml-kem-1024' :
779+ case 'slh-dsa-sha2-128f' :
780+ case 'slh-dsa-sha2-128s' :
781+ case 'slh-dsa-sha2-192f' :
782+ case 'slh-dsa-sha2-192s' :
783+ case 'slh-dsa-sha2-256f' :
784+ case 'slh-dsa-sha2-256s' :
785+ case 'slh-dsa-shake-128f' :
786+ case 'slh-dsa-shake-128s' :
787+ case 'slh-dsa-shake-192f' :
788+ case 'slh-dsa-shake-192s' :
789+ case 'slh-dsa-shake-256f' :
790+ case 'slh-dsa-shake-256s' : {
791+ if ( ! hasPqcSupport ) {
792+ throw new ERR_INVALID_ARG_VALUE ( 'asymmetricKeyType' , asymmetricKeyType ) ;
793+ }
794+ const keyType = format === 'raw-public' ? kKeyTypePublic : kKeyTypePrivate ;
795+ if ( ! handle . initPqcRaw ( asymmetricKeyType , keyData , keyType ) ) {
796+ throw new ERR_INVALID_ARG_VALUE ( 'key.key' , keyData ) ;
797+ }
798+ return handle ;
799+ }
800+ default :
801+ throw new ERR_INVALID_ARG_VALUE ( 'asymmetricKeyType' , asymmetricKeyType ) ;
802+ }
803+ }
804+
694805function prepareAsymmetricKey ( key , ctx ) {
695806 if ( isKeyObject ( key ) ) {
696807 // Best case: A key object, as simple as that.
@@ -712,6 +823,12 @@ function prepareAsymmetricKey(key, ctx) {
712823 else if ( format === 'jwk' ) {
713824 validateObject ( data , 'key.key' ) ;
714825 return { data : getKeyObjectHandleFromJwk ( data , ctx ) , format : 'jwk' } ;
826+ } else if ( format === 'raw-public' || format === 'raw-private' ||
827+ format === 'raw-seed' ) {
828+ return {
829+ data : getKeyObjectHandleFromRaw ( key , data , format ) ,
830+ format,
831+ } ;
715832 }
716833
717834 // Either PEM or DER using PKCS#1 or SPKI.
@@ -777,7 +894,7 @@ function createPublicKey(key) {
777894 const { format, type, data, passphrase } =
778895 prepareAsymmetricKey ( key , kCreatePublic ) ;
779896 let handle ;
780- if ( format === 'jwk' ) {
897+ if ( format === 'jwk' || format === 'raw-public' ) {
781898 handle = data ;
782899 } else {
783900 handle = new KeyObjectHandle ( ) ;
@@ -790,7 +907,7 @@ function createPrivateKey(key) {
790907 const { format, type, data, passphrase } =
791908 prepareAsymmetricKey ( key , kCreatePrivate ) ;
792909 let handle ;
793- if ( format === 'jwk' ) {
910+ if ( format === 'jwk' || format === 'raw-private' || format === 'raw-seed' ) {
794911 handle = data ;
795912 } else {
796913 handle = new KeyObjectHandle ( ) ;
0 commit comments