From 299cbb6cbb28d57e0d27a512066baa51115d86cd Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Wed, 6 May 2026 09:15:30 -0400 Subject: [PATCH 1/2] fix: default RSA-PSS saltLength to RSA_PSS_SALTLEN_MAX_SIGN in sign/verify Match Node.js behavior: when padding === RSA_PKCS1_PSS_PADDING and saltLength is undefined, default to RSA_PSS_SALTLEN_MAX_SIGN before forwarding to native. Closes #1006. --- .../src/keys/signVerify.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/react-native-quick-crypto/src/keys/signVerify.ts b/packages/react-native-quick-crypto/src/keys/signVerify.ts index 28f8e595..f25996b3 100644 --- a/packages/react-native-quick-crypto/src/keys/signVerify.ts +++ b/packages/react-native-quick-crypto/src/keys/signVerify.ts @@ -13,6 +13,7 @@ import { KFormatType, KeyEncoding, } from '../utils'; +import { constants } from '../constants'; type KeyInput = BinaryLike | KeyObject | CryptoKey | KeyInputObject; @@ -144,6 +145,16 @@ function dsaEncodingToNumber( return undefined; } +function getSaltLength(options?: SignOptions): number | undefined { + if ( + options?.padding === constants.RSA_PKCS1_PSS_PADDING && + options?.saltLength === undefined + ) { + return constants.RSA_PSS_SALTLEN_MAX_SIGN; + } + return options?.saltLength; +} + export class Sign { private handle: SignHandleSpec; @@ -169,7 +180,7 @@ export class Sign { const signature = this.handle.sign( keyObject.handle, options?.padding, - options?.saltLength, + getSaltLength(options), dsaEncodingToNumber(options?.dsaEncoding), ); @@ -219,7 +230,7 @@ export class Verify { keyObject.handle, sigBuffer, options?.padding, - options?.saltLength, + getSaltLength(options), dsaEncodingToNumber(options?.dsaEncoding), ); } From 0555240e84c3d1c598ed19ae710e747da7a1aa8f Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Wed, 6 May 2026 09:17:19 -0400 Subject: [PATCH 2/2] test: cover RSA-PSS saltLength default in sign/verify tests Verify that omitting saltLength with RSA_PKCS1_PSS_PADDING produces a signature that round-trips both with no explicit saltLength and with the explicit RSA_PSS_SALTLEN_MAX_SIGN constant. --- example/src/tests/keys/sign_verify_oneshot.ts | 32 +++++++++++++++++ .../src/tests/keys/sign_verify_streaming.ts | 34 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/example/src/tests/keys/sign_verify_oneshot.ts b/example/src/tests/keys/sign_verify_oneshot.ts index f7a4dff4..9eb1e580 100644 --- a/example/src/tests/keys/sign_verify_oneshot.ts +++ b/example/src/tests/keys/sign_verify_oneshot.ts @@ -113,6 +113,38 @@ test(SUITE, 'RSA-PSS with padding and salt length options', () => { expect(isValid).to.equal(true); }); +test(SUITE, 'RSA-PSS defaults saltLength to MAX_SIGN when undefined', () => { + const signature = sign('SHA256', testData, { + key: rsaPrivateKeyPem, + padding: constants.RSA_PKCS1_PSS_PADDING, + }); + + const isValid = verify( + 'SHA256', + testData, + { + key: rsaPublicKeyPem, + padding: constants.RSA_PKCS1_PSS_PADDING, + }, + signature, + ); + + expect(isValid).to.equal(true); + + const isValidExplicit = verify( + 'SHA256', + testData, + { + key: rsaPublicKeyPem, + padding: constants.RSA_PKCS1_PSS_PADDING, + saltLength: constants.RSA_PSS_SALTLEN_MAX_SIGN, + }, + signature, + ); + + expect(isValidExplicit).to.equal(true); +}); + // --- ECDSA Tests --- test(SUITE, 'ECDSA P-256 with DER encoding', async () => { diff --git a/example/src/tests/keys/sign_verify_streaming.ts b/example/src/tests/keys/sign_verify_streaming.ts index 906ec8f5..77ef7d27 100644 --- a/example/src/tests/keys/sign_verify_streaming.ts +++ b/example/src/tests/keys/sign_verify_streaming.ts @@ -219,6 +219,40 @@ test(SUITE, 'RSA-PSS with SHA256 and auto salt length', () => { expect(isValid).to.equal(true); }); +test(SUITE, 'RSA-PSS defaults saltLength to MAX_SIGN when undefined', () => { + const signer = createSign('SHA256'); + signer.update(testData); + const signature = signer.sign({ + key: rsaPrivateKeyPem, + padding: constants.RSA_PKCS1_PSS_PADDING, + }); + + const verifier = createVerify('SHA256'); + verifier.update(testData); + const isValid = verifier.verify( + { + key: rsaPublicKeyPem, + padding: constants.RSA_PKCS1_PSS_PADDING, + }, + signature, + ); + + expect(isValid).to.equal(true); + + const verifierExplicit = createVerify('SHA256'); + verifierExplicit.update(testData); + const isValidExplicit = verifierExplicit.verify( + { + key: rsaPublicKeyPem, + padding: constants.RSA_PKCS1_PSS_PADDING, + saltLength: constants.RSA_PSS_SALTLEN_MAX_SIGN, + }, + signature, + ); + + expect(isValidExplicit).to.equal(true); +}); + // --- KeyObject Tests --- test(SUITE, 'Sign/Verify with KeyObject', () => {