@@ -22,12 +22,34 @@ const { once } = require('node:events');
2222const { Worker, MessageChannel } = require ( 'node:worker_threads' ) ;
2323const { subtle } = globalThis . crypto ;
2424
25+ function assertNoOwnReflection ( key ) {
26+ assert . deepStrictEqual ( Object . getOwnPropertySymbols ( key ) , [ ] ) ;
27+ assert . deepStrictEqual ( Object . getOwnPropertyNames ( key ) , [ ] ) ;
28+ assert . deepStrictEqual ( Reflect . ownKeys ( key ) , [ ] ) ;
29+ }
30+
31+ function describeKey ( key ) {
32+ const algorithm = { ...key . algorithm } ;
33+ if ( algorithm . hash !== undefined )
34+ algorithm . hash = { ...algorithm . hash } ;
35+ if ( algorithm . publicExponent !== undefined )
36+ algorithm . publicExponent = Array . from ( algorithm . publicExponent ) ;
37+ return {
38+ type : key . type ,
39+ extractable : key . extractable ,
40+ algorithm,
41+ usages : [ ...key . usages ] . sort ( ) ,
42+ } ;
43+ }
44+
2545function assertSameCryptoKey ( a , b ) {
2646 assert . notStrictEqual ( a , b ) ;
2747 assert . strictEqual ( a . type , b . type ) ;
2848 assert . strictEqual ( a . extractable , b . extractable ) ;
2949 assert . deepStrictEqual ( a . algorithm , b . algorithm ) ;
3050 assert . deepStrictEqual ( [ ...a . usages ] . sort ( ) , [ ...b . usages ] . sort ( ) ) ;
51+ assertNoOwnReflection ( a ) ;
52+ assertNoOwnReflection ( b ) ;
3153 // util.inspect reads native internal slots directly, so a clone's
3254 // rendered form must match the original's.
3355 assert . strictEqual ( inspect ( a , { depth : 4 } ) , inspect ( b , { depth : 4 } ) ) ;
@@ -191,6 +213,83 @@ async function checkTransferToWorker(key) {
191213 true ) ;
192214}
193215
216+ async function checkRsaPssTransferToWorker ( { publicKey, privateKey } ) {
217+ const data = Buffer . from ( 'rsa-pss worker payload' ) ;
218+ const algorithm = { name : 'RSA-PSS' , saltLength : 32 } ;
219+ const parentSignature = Buffer . from (
220+ await subtle . sign ( algorithm , privateKey , data ) ) ;
221+
222+ const worker = new Worker ( `
223+ 'use strict';
224+ const assert = require('node:assert');
225+ const { parentPort } = require('node:worker_threads');
226+ const { subtle } = globalThis.crypto;
227+
228+ function describeKey(key) {
229+ const algorithm = { ...key.algorithm };
230+ if (algorithm.hash !== undefined)
231+ algorithm.hash = { ...algorithm.hash };
232+ if (algorithm.publicExponent !== undefined)
233+ algorithm.publicExponent = Array.from(algorithm.publicExponent);
234+ return {
235+ type: key.type,
236+ extractable: key.extractable,
237+ algorithm,
238+ usages: [...key.usages].sort(),
239+ };
240+ }
241+
242+ parentPort.once('message', async (message) => {
243+ try {
244+ const {
245+ publicKey,
246+ privateKey,
247+ expectedPublic,
248+ expectedPrivate,
249+ signature,
250+ data,
251+ } = message;
252+ assert.deepStrictEqual(describeKey(publicKey), expectedPublic);
253+ assert.deepStrictEqual(describeKey(privateKey), expectedPrivate);
254+
255+ const algorithm = { name: 'RSA-PSS', saltLength: 32 };
256+ const verified = await subtle.verify(
257+ algorithm, publicKey, signature, data);
258+ const workerSignature = Buffer.from(
259+ await subtle.sign(algorithm, privateKey, data));
260+
261+ parentPort.postMessage({
262+ publicKey,
263+ privateKey,
264+ verified,
265+ signature: workerSignature,
266+ });
267+ } catch (err) {
268+ parentPort.postMessage({ error: err.stack || err.message });
269+ }
270+ });
271+ ` , { eval : true } ) ;
272+
273+ worker . postMessage ( {
274+ publicKey,
275+ privateKey,
276+ expectedPublic : describeKey ( publicKey ) ,
277+ expectedPrivate : describeKey ( privateKey ) ,
278+ signature : parentSignature ,
279+ data,
280+ } ) ;
281+ const [ msg ] = await once ( worker , 'message' ) ;
282+ await worker . terminate ( ) ;
283+
284+ assert . strictEqual ( msg . error , undefined , msg . error ) ;
285+ assert . strictEqual ( msg . verified , true ) ;
286+ assertSameCryptoKey ( publicKey , msg . publicKey ) ;
287+ assertSameCryptoKey ( privateKey , msg . privateKey ) ;
288+ assert . strictEqual (
289+ await subtle . verify ( algorithm , publicKey , msg . signature , data ) ,
290+ true ) ;
291+ }
292+
194293( async ( ) => {
195294 // Extractable HMAC (secret)
196295 const hmacExtractable = await subtle . importKey (
@@ -248,4 +347,17 @@ async function checkTransferToWorker(key) {
248347 true ,
249348 [ 'sign' , 'verify' ] ) ;
250349 await checkAsymmetricKeyPair ( ecKeypairExtractable ) ;
350+
351+ // RSA-PSS keypair through a Worker (covers public/private native key
352+ // handles and cloning of the publicExponent algorithm member).
353+ const rsaPssKeypair = await subtle . generateKey (
354+ {
355+ name : 'RSA-PSS' ,
356+ modulusLength : 2048 ,
357+ publicExponent : new Uint8Array ( [ 1 , 0 , 1 ] ) ,
358+ hash : 'SHA-256' ,
359+ } ,
360+ false ,
361+ [ 'sign' , 'verify' ] ) ;
362+ await checkRsaPssTransferToWorker ( rsaPssKeypair ) ;
251363} ) ( ) . then ( common . mustCall ( ) ) ;
0 commit comments