@@ -11,22 +11,29 @@ function readKey(name) {
1111 return fs . readFileSync ( `${ fixtures_keydir } /${ name } .pem` , 'utf8' ) ;
1212}
1313
14+ function readKeyPair ( publicKeyName , privateKeyName ) {
15+ return {
16+ publicKey : readKey ( publicKeyName ) ,
17+ privateKey : readKey ( privateKeyName ) ,
18+ } ;
19+ }
20+
1421const keyFixtures = { } ;
1522
1623if ( hasOpenSSL ( 3 , 5 ) ) {
17- keyFixtures [ 'ml-kem-512' ] = readKey ( 'ml_kem_512_private' ) ;
18- keyFixtures [ 'ml-kem-768' ] = readKey ( 'ml_kem_768_private' ) ;
19- keyFixtures [ 'ml-kem-1024' ] = readKey ( 'ml_kem_1024_private' ) ;
24+ keyFixtures [ 'ml-kem-512' ] = readKeyPair ( 'ml_kem_512_public' , 'ml_kem_512_private' ) ;
25+ keyFixtures [ 'ml-kem-768' ] = readKeyPair ( 'ml_kem_768_public' , 'ml_kem_768_private' ) ;
26+ keyFixtures [ 'ml-kem-1024' ] = readKeyPair ( 'ml_kem_1024_public' , 'ml_kem_1024_private' ) ;
2027}
2128if ( hasOpenSSL ( 3 , 2 ) ) {
22- keyFixtures [ 'p-256' ] = readKey ( 'ec_p256_private' ) ;
23- keyFixtures [ 'p-384' ] = readKey ( 'ec_p384_private' ) ;
24- keyFixtures [ 'p-521' ] = readKey ( 'ec_p521_private' ) ;
25- keyFixtures . x25519 = readKey ( 'x25519_private' ) ;
26- keyFixtures . x448 = readKey ( 'x448_private' ) ;
29+ keyFixtures [ 'p-256' ] = readKeyPair ( 'ec_p256_public' , 'ec_p256_private' ) ;
30+ keyFixtures [ 'p-384' ] = readKeyPair ( 'ec_p384_public' , 'ec_p384_private' ) ;
31+ keyFixtures [ 'p-521' ] = readKeyPair ( 'ec_p521_public' , 'ec_p521_private' ) ;
32+ keyFixtures . x25519 = readKeyPair ( 'x25519_public' , 'x25519_private' ) ;
33+ keyFixtures . x448 = readKeyPair ( 'x448_public' , 'x448_private' ) ;
2734}
2835if ( hasOpenSSL ( 3 , 0 ) ) {
29- keyFixtures . rsa = readKey ( 'rsa_private_2048' ) ;
36+ keyFixtures . rsa = readKeyPair ( 'rsa_public_2048' , 'rsa_private_2048' ) ;
3037}
3138
3239if ( Object . keys ( keyFixtures ) . length === 0 ) {
@@ -37,32 +44,46 @@ if (Object.keys(keyFixtures).length === 0) {
3744const bench = common . createBenchmark ( main , {
3845 keyType : Object . keys ( keyFixtures ) ,
3946 mode : [ 'sync' , 'async' , 'async-parallel' ] ,
40- keyFormat : [ 'keyObject' , 'keyObject.unique' ] ,
47+ keyFormat : [ 'keyObject' , 'keyObject.unique' , 'pem' , 'der' , 'jwk' ,
48+ 'raw-public' , 'raw-private' , 'raw-seed' ] ,
4149 op : [ 'encapsulate' , 'decapsulate' ] ,
4250 n : [ 1e3 ] ,
4351} , {
4452 combinationFilter ( p ) {
4553 // "keyObject.unique" allows to compare the result with "keyObject" to
4654 // assess whether mutexes over the key material impact the operation
47- return p . keyFormat !== 'keyObject.unique' ||
48- ( p . keyFormat === 'keyObject.unique' && p . mode === 'async-parallel' ) ;
55+ if ( p . keyFormat === 'keyObject.unique' )
56+ return p . mode === 'async-parallel' ;
57+ // JWK is not supported for ml-kem for now
58+ if ( p . keyFormat === 'jwk' )
59+ return ! p . keyType . startsWith ( 'ml-' ) ;
60+ // raw-public is only supported for encapsulate, not rsa
61+ if ( p . keyFormat === 'raw-public' )
62+ return p . keyType !== 'rsa' && p . op === 'encapsulate' ;
63+ // raw-private is not supported for rsa and ml-kem, only for decapsulate
64+ if ( p . keyFormat === 'raw-private' )
65+ return p . keyType !== 'rsa' && ! p . keyType . startsWith ( 'ml-' ) && p . op === 'decapsulate' ;
66+ // raw-seed is only supported for ml-kem
67+ if ( p . keyFormat === 'raw-seed' )
68+ return p . keyType . startsWith ( 'ml-' ) ;
69+ return true ;
4970 } ,
5071} ) ;
5172
52- function measureSync ( n , op , privateKey , keys , ciphertexts ) {
73+ function measureSync ( n , op , key , keys , ciphertexts ) {
5374 bench . start ( ) ;
5475 for ( let i = 0 ; i < n ; ++ i ) {
55- const key = privateKey || keys [ i ] ;
76+ const k = key || keys [ i ] ;
5677 if ( op === 'encapsulate' ) {
57- crypto . encapsulate ( key ) ;
78+ crypto . encapsulate ( k ) ;
5879 } else {
59- crypto . decapsulate ( key , ciphertexts [ i ] ) ;
80+ crypto . decapsulate ( k , ciphertexts [ i ] ) ;
6081 }
6182 }
6283 bench . end ( n ) ;
6384}
6485
65- function measureAsync ( n , op , privateKey , keys , ciphertexts ) {
86+ function measureAsync ( n , op , key , keys , ciphertexts ) {
6687 let remaining = n ;
6788 function done ( ) {
6889 if ( -- remaining === 0 )
@@ -72,44 +93,98 @@ function measureAsync(n, op, privateKey, keys, ciphertexts) {
7293 }
7394
7495 function one ( ) {
75- const key = privateKey || keys [ n - remaining ] ;
96+ const k = key || keys [ n - remaining ] ;
7697 if ( op === 'encapsulate' ) {
77- crypto . encapsulate ( key , done ) ;
98+ crypto . encapsulate ( k , done ) ;
7899 } else {
79- crypto . decapsulate ( key , ciphertexts [ n - remaining ] , done ) ;
100+ crypto . decapsulate ( k , ciphertexts [ n - remaining ] , done ) ;
80101 }
81102 }
82103 bench . start ( ) ;
83104 one ( ) ;
84105}
85106
86- function measureAsyncParallel ( n , op , privateKey , keys , ciphertexts ) {
107+ function measureAsyncParallel ( n , op , key , keys , ciphertexts ) {
87108 let remaining = n ;
88109 function done ( ) {
89110 if ( -- remaining === 0 )
90111 bench . end ( n ) ;
91112 }
92113 bench . start ( ) ;
93114 for ( let i = 0 ; i < n ; ++ i ) {
94- const key = privateKey || keys [ i ] ;
115+ const k = key || keys [ i ] ;
95116 if ( op === 'encapsulate' ) {
96- crypto . encapsulate ( key , done ) ;
117+ crypto . encapsulate ( k , done ) ;
97118 } else {
98- crypto . decapsulate ( key , ciphertexts [ i ] , done ) ;
119+ crypto . decapsulate ( k , ciphertexts [ i ] , done ) ;
99120 }
100121 }
101122}
102123
103124function main ( { n, mode, keyFormat, keyType, op } ) {
104- const pems = [ ...Buffer . alloc ( n ) ] . map ( ( ) => keyFixtures [ keyType ] ) ;
105- const keyObjects = pems . map ( crypto . createPrivateKey ) ;
125+ const isEncapsulate = op === 'encapsulate' ;
126+ const pemSource = isEncapsulate ?
127+ keyFixtures [ keyType ] . publicKey :
128+ keyFixtures [ keyType ] . privateKey ;
129+ const createKeyFn = isEncapsulate ? crypto . createPublicKey : crypto . createPrivateKey ;
130+ const pems = [ ...Buffer . alloc ( n ) ] . map ( ( ) => pemSource ) ;
131+ const keyObjects = pems . map ( createKeyFn ) ;
106132
107- let privateKey , keys , ciphertexts ;
133+ // Warm up OpenSSL's provider operation cache for each key object
134+ if ( isEncapsulate ) {
135+ for ( const keyObject of keyObjects ) {
136+ crypto . encapsulate ( keyObject ) ;
137+ }
138+ } else {
139+ const warmupCiphertext = crypto . encapsulate ( keyObjects [ 0 ] ) . ciphertext ;
140+ for ( const keyObject of keyObjects ) {
141+ crypto . decapsulate ( keyObject , warmupCiphertext ) ;
142+ }
143+ }
144+
145+ const asymmetricKeyType = keyObjects [ 0 ] . asymmetricKeyType ;
146+ let key , keys , ciphertexts ;
108147
109148 switch ( keyFormat ) {
110149 case 'keyObject' :
111- privateKey = keyObjects [ 0 ] ;
150+ key = keyObjects [ 0 ] ;
151+ break ;
152+ case 'pem' :
153+ key = pems [ 0 ] ;
112154 break ;
155+ case 'jwk' : {
156+ key = { key : keyObjects [ 0 ] . export ( { format : 'jwk' } ) , format : 'jwk' } ;
157+ break ;
158+ }
159+ case 'der' : {
160+ const type = isEncapsulate ? 'spki' : 'pkcs8' ;
161+ key = { key : keyObjects [ 0 ] . export ( { format : 'der' , type } ) , format : 'der' , type } ;
162+ break ;
163+ }
164+ case 'raw-public' : {
165+ const exportedKey = keyObjects [ 0 ] . export ( { format : 'raw-public' } ) ;
166+ const keyOpts = { key : exportedKey , format : 'raw-public' , asymmetricKeyType } ;
167+ if ( asymmetricKeyType === 'ec' ) keyOpts . namedCurve = keyObjects [ 0 ] . asymmetricKeyDetails . namedCurve ;
168+ key = keyOpts ;
169+ break ;
170+ }
171+ case 'raw-private' : {
172+ const exportedKey = keyObjects [ 0 ] . export ( { format : 'raw-private' } ) ;
173+ const keyOpts = { key : exportedKey , format : 'raw-private' , asymmetricKeyType } ;
174+ if ( asymmetricKeyType === 'ec' ) keyOpts . namedCurve = keyObjects [ 0 ] . asymmetricKeyDetails . namedCurve ;
175+ key = keyOpts ;
176+ break ;
177+ }
178+ case 'raw-seed' : {
179+ // raw-seed requires a private key to export from
180+ const privateKeyObject = crypto . createPrivateKey ( keyFixtures [ keyType ] . privateKey ) ;
181+ key = {
182+ key : privateKeyObject . export ( { format : 'raw-seed' } ) ,
183+ format : 'raw-seed' ,
184+ asymmetricKeyType,
185+ } ;
186+ break ;
187+ }
113188 case 'keyObject.unique' :
114189 keys = keyObjects ;
115190 break ;
@@ -118,23 +193,25 @@ function main({ n, mode, keyFormat, keyType, op }) {
118193 }
119194
120195 // Pre-generate ciphertexts for decapsulate operations
121- if ( op === 'decapsulate' ) {
122- if ( privateKey ) {
123- ciphertexts = [ ...Buffer . alloc ( n ) ] . map ( ( ) => crypto . encapsulate ( privateKey ) . ciphertext ) ;
196+ if ( ! isEncapsulate ) {
197+ const encapKey = crypto . createPublicKey (
198+ crypto . createPrivateKey ( keyFixtures [ keyType ] . privateKey ) ) ;
199+ if ( key ) {
200+ ciphertexts = [ ...Buffer . alloc ( n ) ] . map ( ( ) => crypto . encapsulate ( encapKey ) . ciphertext ) ;
124201 } else {
125- ciphertexts = keys . map ( ( key ) => crypto . encapsulate ( key ) . ciphertext ) ;
202+ ciphertexts = keys . map ( ( ) => crypto . encapsulate ( encapKey ) . ciphertext ) ;
126203 }
127204 }
128205
129206 switch ( mode ) {
130207 case 'sync' :
131- measureSync ( n , op , privateKey , keys , ciphertexts ) ;
208+ measureSync ( n , op , key , keys , ciphertexts ) ;
132209 break ;
133210 case 'async' :
134- measureAsync ( n , op , privateKey , keys , ciphertexts ) ;
211+ measureAsync ( n , op , key , keys , ciphertexts ) ;
135212 break ;
136213 case 'async-parallel' :
137- measureAsyncParallel ( n , op , privateKey , keys , ciphertexts ) ;
214+ measureAsyncParallel ( n , op , key , keys , ciphertexts ) ;
138215 break ;
139216 }
140217}
0 commit comments