Skip to content

Commit 618f793

Browse files
panvanodejs-github-bot
authored andcommitted
crypto: report actual RSA modulus lengths
Use the generated key's modulus size for both CryptoKey algorithm objects. Preserve successful backend generation when it rounds the requested size, keeping metadata consistent across export and cloning. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: #66237 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent cf36e30 commit 618f793

2 files changed

Lines changed: 56 additions & 2 deletions

File tree

‎lib/internal/crypto/rsa.js‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const {
3232
bigIntArrayToUnsignedInt,
3333
getUsagesMask,
3434
jobPromise,
35+
jobPromiseThen,
3536
normalizeHashName,
3637
validateAlgorithm,
3738
validateMaxBufferLength,
@@ -132,15 +133,21 @@ function rsaKeyGenerate(
132133
const keyUsages = getKeyPairUsages(usagesSet, allowedUsages);
133134
validateUsagesNotEmpty(keyUsages.private);
134135

135-
return jobPromise(() => new RsaKeyPairGenJob(
136+
return jobPromiseThen(jobPromise(() => new RsaKeyPairGenJob(
136137
kCryptoJobWebCrypto,
137138
kKeyVariantRSA_SSA_PKCS1_v1_5,
138139
modulusLength,
139140
publicExponentConverted,
140141
keyAlgorithm,
141142
getUsagesMask(keyUsages.public),
142143
getUsagesMask(keyUsages.private),
143-
extractable));
144+
extractable)), (result) => {
145+
const { modulusLength: actualModulusLength } =
146+
getCryptoKeyHandle(result.publicKey).keyDetail({ __proto__: null });
147+
getCryptoKeyAlgorithm(result.publicKey).modulusLength = actualModulusLength;
148+
getCryptoKeyAlgorithm(result.privateKey).modulusLength = actualModulusLength;
149+
return result;
150+
});
144151
}
145152

146153
function rsaExportKey(key, format) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const { generateKeyPairSync, KeyObject } = require('crypto');
9+
const { subtle } = globalThis.crypto;
10+
11+
(async () => {
12+
for (const name of ['RSA-PSS', 'RSASSA-PKCS1-v1_5', 'RSA-OAEP']) {
13+
const usages = name === 'RSA-OAEP' ? ['encrypt', 'decrypt'] : ['sign', 'verify'];
14+
for (const modulusLength of [1025, 2048, 2049]) {
15+
let pair;
16+
try {
17+
pair = await subtle.generateKey({
18+
name, modulusLength, hash: 'SHA-256', publicExponent: new Uint8Array([1, 0, 1]),
19+
}, true, usages);
20+
} catch (err) {
21+
if (modulusLength === 2048 || err.name !== 'OperationError')
22+
throw err;
23+
// Only reject sizes the backend also rejects. Some backends round
24+
// the requested size, which must still produce a usable CryptoKey.
25+
assert.throws(() => generateKeyPairSync('rsa', { modulusLength }),
26+
{ name: 'Error' });
27+
continue;
28+
}
29+
const actualModulusLength =
30+
KeyObject.from(pair.publicKey).asymmetricKeyDetails.modulusLength;
31+
for (const key of [pair.publicKey, pair.privateKey]) {
32+
assert.strictEqual(key.algorithm.modulusLength, actualModulusLength);
33+
assert.strictEqual(
34+
KeyObject.from(key).asymmetricKeyDetails.modulusLength, actualModulusLength);
35+
assert.deepStrictEqual(structuredClone(key).algorithm, key.algorithm);
36+
37+
const format = key.type === 'public' ? 'spki' : 'pkcs8';
38+
const imported = await subtle.importKey(
39+
format, await subtle.exportKey(format, key),
40+
{ name, hash: 'SHA-256' }, true, key.usages);
41+
assert.deepStrictEqual(imported.algorithm, key.algorithm);
42+
}
43+
const publicKey = await subtle.getPublicKey(pair.privateKey, pair.publicKey.usages);
44+
assert.deepStrictEqual(publicKey.algorithm, pair.publicKey.algorithm);
45+
}
46+
}
47+
})().then(common.mustCall());

0 commit comments

Comments
 (0)