Skip to content

Commit e7e3a80

Browse files
committed
[fix] PSS auto salt-length verify failed due leading zeros
RSABlindedEngine.processBlock() converts the raw modular-exponentiation result back through BigInteger, which strips leading zero bytes. When the encoded message (EM) happened to start with 0x00 — roughly a 1-in-256 chance per signature — the returned array was shorter than the expected emLen = ceil((modBits-1)/8). The hand-rolled PSS parser in pssAutoSaltLength() used the array length as emLen, which shifted every subsequent offset (maskedDB, H, the 0x01 separator), causing the salt-length recovery to fail and verify_pss to return false. Add a deterministic regression test with a hardcoded signature whose EM is known to have a leading 0x00 byte for the rsa2048 test fixture key.
1 parent 63d0b43 commit e7e3a80

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

src/main/java/org/jruby/ext/openssl/PKeyRSA.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,10 +1095,22 @@ private static int pssAutoSaltLength(RSAPublicKey pubKey, byte[] sigBytes, Strin
10951095
RSAKeyParameters bcPubKey = new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent());
10961096
RSABlindedEngine rsa = new RSABlindedEngine();
10971097
rsa.init(false, bcPubKey);
1098-
byte[] em = rsa.processBlock(sigBytes, 0, sigBytes.length);
1098+
byte[] raw = rsa.processBlock(sigBytes, 0, sigBytes.length);
1099+
1100+
// emLen = ceil((modBits - 1) / 8) per RFC 8017 §9.1.1
1101+
int emLen = (pubKey.getModulus().bitLength() - 1 + 7) / 8;
1102+
1103+
// RSA raw output may be shorter than emLen if leading bytes are zero;
1104+
// left-pad with zeros to the expected length.
1105+
byte[] em;
1106+
if (raw.length < emLen) {
1107+
em = new byte[emLen];
1108+
System.arraycopy(raw, 0, em, emLen - raw.length, raw.length);
1109+
} else {
1110+
em = raw;
1111+
}
10991112

11001113
int hLen = getDigestLength(digestAlg);
1101-
int emLen = em.length;
11021114
if (emLen < hLen + 2 || em[emLen - 1] != (byte) 0xBC) return -1;
11031115

11041116
int dbLen = emLen - hLen - 1;

src/test/ruby/rsa/test_rsa.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,29 @@ def test_sign_verify_pss
288288
}
289289
end
290290

291+
# Regression test: verify_pss with salt_length: :auto must handle RSA raw
292+
# output shorter than emLen (leading zero bytes stripped by BigInteger).
293+
# The hardcoded signature below was produced by sign_pss with the rsa2048
294+
# fixture key and salt_length: :digest; its RSA public-key recovery yields
295+
# an encoded message (EM) with a leading 0x00 byte (255 bytes instead of
296+
# the expected emLen=256), which triggers the leading-zero edge case.
297+
def test_sign_verify_pss_auto_salt_leading_zero
298+
key = Fixtures.pkey("rsa2048")
299+
data = "test-data-6"
300+
signature = Base64.decode64(
301+
"X0U4VKxjVFN6sXKJaZdpGKOwSPo9L1VNbKyTqJ5P7nNDOz8flimVFJwe6H969zYM" \
302+
"xdbP2hLVLHtgzLeQd6N85DiYLDqObUIAhm94kMhGNh2bc3mutYTU96huKbL8YfhG/" \
303+
"+1SxAJUaMBQqRQ7UIaPJHuShD5rQ9SHRRASNbWpzRGtMzRgMorykF2+nAnMlbQxxNW" \
304+
"s7Ia1rsR9OGUS/Q9rQqUuB5i9IR513Xf/O39AxgiaoEehdYAdmbh1W/hQqGGnb8Xt" \
305+
"fTdQlsbiyuMvTnbNqIlwVNh4FU2AZr9u4DHd3zQt6csYI2UPcE00vv3e0RWY4C4EA" \
306+
"TGVk92gZ59VEVeJEQ=="
307+
)
308+
assert_equal true,
309+
key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA256")
310+
assert_equal true,
311+
key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA256")
312+
end
313+
291314
def test_rsa_param_accessors
292315
key_file = File.join(File.dirname(__FILE__), 'private_key.pem')
293316
key = OpenSSL::PKey::RSA.new(File.read(key_file))

0 commit comments

Comments
 (0)