From 812da07fe40b756d998e2ff9a80a33827f01b052 Mon Sep 17 00:00:00 2001 From: TLS-Attacker Fix Bot Date: Thu, 26 Jun 2025 15:35:02 +0000 Subject: [PATCH] Fix RSA PSS signature verification with self-signed certificates This fixes issue #191 where TLS-Attacker client couldn't send self-signed certificates to OpenSSL servers due to incorrect PSS salt handling. Changes: - Generate proper random salt when default salt length doesn't match hash length - Fix Config setter/getter for PSS salt to properly handle different salt sizes - Add test to verify correct PSS salt generation for different hash algorithms The issue was that the salt was being padded with zeros instead of using proper random bytes, causing OpenSSL to fail with "last octet invalid" error. --- .../nds/tlsattacker/core/config/Config.java | 4 +- .../core/crypto/TlsSignatureUtil.java | 20 +++++---- .../core/crypto/TlsSignatureUtilTest.java | 44 +++++++++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/config/Config.java b/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/config/Config.java index 63e3e9ec21..aa5383f49f 100644 --- a/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/config/Config.java +++ b/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/config/Config.java @@ -1453,11 +1453,11 @@ public Config() { } public void setDefaultRsaSsaPssSalt(byte[] salt) { - System.arraycopy(defaultRsaSsaPssSalt, 0, salt, 0, defaultRsaSsaPssSalt.length); + this.defaultRsaSsaPssSalt = salt.clone(); } public byte[] getDefaultRsaSsaPssSalt() { - return defaultRsaSsaPssSalt; + return defaultRsaSsaPssSalt.clone(); } public Point getDefaultClientEphemeralEcPublicKey() { diff --git a/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtil.java b/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtil.java index 2cf6074ff2..256bdd8212 100644 --- a/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtil.java +++ b/TLS-Core/src/main/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtil.java @@ -26,7 +26,7 @@ import de.rub.nds.tlsattacker.core.workflow.chooser.Chooser; import de.rub.nds.x509attacker.chooser.X509Chooser; import java.math.BigInteger; -import java.util.Arrays; +import java.security.SecureRandom; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -239,14 +239,16 @@ private void computeRsaPssSignature( .getChooser() .getSubjectRsaPrivateKey(); byte[] salt = chooser.getConfig().getDefaultRsaSsaPssSalt(); - if (salt.length > algorithm.getBitLength() / 8) { - LOGGER.debug("Default PSS salt is too long, truncating"); - salt = Arrays.copyOfRange(salt, 0, algorithm.getBitLength() / 8); - } else if (salt.length < algorithm.getBitLength() / 8) { - LOGGER.debug("Default PSS salt is too short, padding"); - byte[] newSalt = new byte[algorithm.getBitLength() / 8]; - System.arraycopy(salt, 0, newSalt, 0, salt.length); - salt = newSalt; + int expectedSaltLength = algorithm.getBitLength() / 8; + + if (salt.length != expectedSaltLength) { + LOGGER.debug( + "PSS salt length does not match hash length. Generating new random salt of length: " + + expectedSaltLength); + // Generate a new random salt with the correct length + salt = new byte[expectedSaltLength]; + SecureRandom random = new SecureRandom(); + random.nextBytes(salt); } calculator.computeRsaPssSignature( computations, diff --git a/TLS-Core/src/test/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtilTest.java b/TLS-Core/src/test/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtilTest.java index a91fd4eea0..4cc72f06ba 100644 --- a/TLS-Core/src/test/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtilTest.java +++ b/TLS-Core/src/test/java/de/rub/nds/tlsattacker/core/crypto/TlsSignatureUtilTest.java @@ -9,7 +9,10 @@ package de.rub.nds.tlsattacker.core.crypto; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import de.rub.nds.protocol.crypto.signature.RsaSsaPssSignatureComputations; import de.rub.nds.protocol.crypto.signature.SignatureCalculator; import de.rub.nds.protocol.crypto.signature.SignatureComputations; import de.rub.nds.tlsattacker.core.config.Config; @@ -17,9 +20,11 @@ import de.rub.nds.tlsattacker.core.state.State; import de.rub.nds.tlsattacker.core.workflow.chooser.Chooser; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -64,4 +69,43 @@ void testComputeSignature(SignatureAndHashAlgorithm algorithm) { chooser, algorithm, value, computations)); } } + + @Test + void testRsaPssSaltHandling() { + // Test with different salt lengths to ensure proper random salt generation + SignatureAndHashAlgorithm[] pssAlgorithms = { + SignatureAndHashAlgorithm.RSA_PSS_RSAE_SHA256, + SignatureAndHashAlgorithm.RSA_PSS_RSAE_SHA384, + SignatureAndHashAlgorithm.RSA_PSS_RSAE_SHA512 + }; + + for (SignatureAndHashAlgorithm algorithm : pssAlgorithms) { + // Test with incorrect salt length (should generate new random salt) + Config config = chooser.getConfig(); + config.setDefaultRsaSsaPssSalt(new byte[10]); // Incorrect length + + RsaSsaPssSignatureComputations computations = new RsaSsaPssSignatureComputations(); + byte[] testData = new byte[] {0x01, 0x02, 0x03, 0x04}; + + assertDoesNotThrow( + () -> + tlsSignatureUtil.computeSignature( + chooser, algorithm, testData, computations)); + + // Verify that salt was generated with correct length + byte[] salt = computations.getSalt().getValue(); + int expectedSaltLength = algorithm.getHashAlgorithm().getBitLength() / 8; + assertEquals( + expectedSaltLength, + salt.length, + "Salt length should match hash algorithm output length for " + algorithm); + + // Verify that salt is not all zeros (was properly randomized) + byte[] allZeros = new byte[expectedSaltLength]; + assertNotEquals( + Arrays.toString(allZeros), + Arrays.toString(salt), + "Salt should be randomized, not all zeros"); + } + } }