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: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
This commit is contained in:
Filip Skokan authored and Node.js GitHub Bot committed 2026-09-25 13:23:19 +00:00
1 parent cf36e304e3
commit 618f793464
2 files changed
+56 -2

No files matched your search

+9 -2
View File
@@ -32,6 +32,7 @@ const {
bigIntArrayToUnsignedInt,
getUsagesMask,
jobPromise,
jobPromiseThen,
normalizeHashName,
validateAlgorithm,
validateMaxBufferLength,
@@ -132,7 +133,7 @@ function rsaKeyGenerate(
const keyUsages = getKeyPairUsages(usagesSet, allowedUsages);
validateUsagesNotEmpty(keyUsages.private);
return jobPromise(() => new RsaKeyPairGenJob(
return jobPromiseThen(jobPromise(() => new RsaKeyPairGenJob(
kCryptoJobWebCrypto,
kKeyVariantRSA_SSA_PKCS1_v1_5,
modulusLength,
@@ -140,7 +141,13 @@ function rsaKeyGenerate(
keyAlgorithm,
getUsagesMask(keyUsages.public),
getUsagesMask(keyUsages.private),
extractable));
extractable)), (result) => {
const { modulusLength: actualModulusLength } =
getCryptoKeyHandle(result.publicKey).keyDetail({ __proto__: null });
getCryptoKeyAlgorithm(result.publicKey).modulusLength = actualModulusLength;
getCryptoKeyAlgorithm(result.privateKey).modulusLength = actualModulusLength;
return result;
});
}
function rsaExportKey(key, format) {
@@ -0,0 +1,47 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const { generateKeyPairSync, KeyObject } = require('crypto');
const { subtle } = globalThis.crypto;
(async () => {
for (const name of ['RSA-PSS', 'RSASSA-PKCS1-v1_5', 'RSA-OAEP']) {
const usages = name === 'RSA-OAEP' ? ['encrypt', 'decrypt'] : ['sign', 'verify'];
for (const modulusLength of [1025, 2048, 2049]) {
let pair;
try {
pair = await subtle.generateKey({
name, modulusLength, hash: 'SHA-256', publicExponent: new Uint8Array([1, 0, 1]),
}, true, usages);
} catch (err) {
if (modulusLength === 2048 || err.name !== 'OperationError')
throw err;
// Only reject sizes the backend also rejects. Some backends round
// the requested size, which must still produce a usable CryptoKey.
assert.throws(() => generateKeyPairSync('rsa', { modulusLength }),
{ name: 'Error' });
continue;
}
const actualModulusLength =
KeyObject.from(pair.publicKey).asymmetricKeyDetails.modulusLength;
for (const key of [pair.publicKey, pair.privateKey]) {
assert.strictEqual(key.algorithm.modulusLength, actualModulusLength);
assert.strictEqual(
KeyObject.from(key).asymmetricKeyDetails.modulusLength, actualModulusLength);
assert.deepStrictEqual(structuredClone(key).algorithm, key.algorithm);
const format = key.type === 'public' ? 'spki' : 'pkcs8';
const imported = await subtle.importKey(
format, await subtle.exportKey(format, key),
{ name, hash: 'SHA-256' }, true, key.usages);
assert.deepStrictEqual(imported.algorithm, key.algorithm);
}
const publicKey = await subtle.getPublicKey(pair.privateKey, pair.publicKey.usages);
assert.deepStrictEqual(publicKey.algorithm, pair.publicKey.algorithm);
}
}
})().then(common.mustCall());