diff --git a/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsAnalyzedProperty.java b/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsAnalyzedProperty.java index 72c739fea..58df5a980 100644 --- a/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsAnalyzedProperty.java +++ b/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsAnalyzedProperty.java @@ -143,6 +143,9 @@ public enum TlsAnalyzedProperty implements AnalyzedProperty { SUPPORTS_DSS(TlsAnalyzedPropertyCategory.CIPHER_SUITES), SUPPORTS_GOST(TlsAnalyzedPropertyCategory.CIPHER_SUITES), SUPPORTS_SRP(TlsAnalyzedPropertyCategory.CIPHER_SUITES), + SUPPORTS_SRP_EXTENSION(TlsAnalyzedPropertyCategory.EXTENSIONS), + SRP_CIPHERSUITES(TlsAnalyzedPropertyCategory.CIPHER_SUITES), + MISSING_SRP_EXTENSION_BUG(TlsAnalyzedPropertyCategory.ATTACKS), SUPPORTS_KERBEROS(TlsAnalyzedPropertyCategory.CIPHER_SUITES), SUPPORTS_PSK_PLAIN(TlsAnalyzedPropertyCategory.CIPHER_SUITES), SUPPORTS_PSK_RSA(TlsAnalyzedPropertyCategory.CIPHER_SUITES), diff --git a/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsProbeType.java b/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsProbeType.java index 02b38c849..05e466f53 100644 --- a/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsProbeType.java +++ b/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/constants/TlsProbeType.java @@ -69,6 +69,7 @@ public enum TlsProbeType implements ProbeType { RANDOMNESS("Randomness"), TLS_FALLBACK_SCSV("TLS Fallback SCSV"), CONNECTION_CLOSING_DELTA("Connection Closing Delta"), + SRP("SRP"), // CLIENT SPECIFIC PROBES FORCED_COMPRESSION("Forced Compression"), FREAK("Freak"), diff --git a/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/report/DefaultPrintingScheme.java b/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/report/DefaultPrintingScheme.java index fd4e3f135..aba20d9d4 100644 --- a/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/report/DefaultPrintingScheme.java +++ b/TLS-Scanner-Core/src/main/java/de/rub/nds/tlsscanner/core/report/DefaultPrintingScheme.java @@ -215,6 +215,15 @@ public static PrintingScheme getDefaultPrintingScheme() { colorMap.put( TlsAnalyzedProperty.SUPPORTS_SRP, getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR)); + colorMap.put( + TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, + getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR)); + colorMap.put( + TlsAnalyzedProperty.SRP_CIPHERSUITES, + getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR)); + colorMap.put( + TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, + getDefaultColorEncoding(AnsiColor.RED, AnsiColor.GREEN)); colorMap.put( TlsAnalyzedProperty.SUPPORTS_KERBEROS, getDefaultColorEncoding(AnsiColor.DEFAULT_COLOR, AnsiColor.DEFAULT_COLOR)); diff --git a/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/execution/TlsServerScanner.java b/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/execution/TlsServerScanner.java index 04a09bbbb..80ade50bd 100644 --- a/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/execution/TlsServerScanner.java +++ b/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/execution/TlsServerScanner.java @@ -92,6 +92,7 @@ import de.rub.nds.tlsscanner.serverscanner.probe.SignatureAndHashAlgorithmProbe; import de.rub.nds.tlsscanner.serverscanner.probe.SignatureHashAlgorithmOrderProbe; import de.rub.nds.tlsscanner.serverscanner.probe.SniProbe; +import de.rub.nds.tlsscanner.serverscanner.probe.SrpProbe; import de.rub.nds.tlsscanner.serverscanner.probe.TlsFallbackScsvProbe; import de.rub.nds.tlsscanner.serverscanner.probe.TlsServerProbe; import de.rub.nds.tlsscanner.serverscanner.probe.TokenbindingProbe; @@ -216,6 +217,7 @@ protected void fillProbeLists() { registerProbeForExecution(new CipherSuiteProbe(configSelector, parallelExecutor)); registerProbeForExecution(new DirectRaccoonProbe(configSelector, parallelExecutor)); registerProbeForExecution(new CipherSuiteOrderProbe(configSelector, parallelExecutor)); + registerProbeForExecution(new SrpProbe(configSelector, parallelExecutor)); registerProbeForExecution(new ExtensionProbe(configSelector, parallelExecutor)); registerProbeForExecution(new ECPointFormatProbe(configSelector, parallelExecutor)); registerProbeForExecution(new ResumptionProbe(configSelector, parallelExecutor)); diff --git a/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbe.java b/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbe.java new file mode 100644 index 000000000..e48b763d0 --- /dev/null +++ b/TLS-Server-Scanner/src/main/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbe.java @@ -0,0 +1,242 @@ +/* + * TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker + * + * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +package de.rub.nds.tlsscanner.serverscanner.probe; + +import de.rub.nds.modifiablevariable.util.Modifiable; +import de.rub.nds.scanner.core.probe.requirements.Requirement; +import de.rub.nds.tlsattacker.core.config.Config; +import de.rub.nds.tlsattacker.core.constants.AlertDescription; +import de.rub.nds.tlsattacker.core.constants.AlertLevel; +import de.rub.nds.tlsattacker.core.constants.CipherSuite; +import de.rub.nds.tlsattacker.core.constants.HandshakeMessageType; +import de.rub.nds.tlsattacker.core.constants.ProtocolMessageType; +import de.rub.nds.tlsattacker.core.constants.ProtocolVersion; +import de.rub.nds.tlsattacker.core.protocol.message.AlertMessage; +import de.rub.nds.tlsattacker.core.protocol.message.ServerHelloMessage; +import de.rub.nds.tlsattacker.core.protocol.message.extension.SRPExtensionMessage; +import de.rub.nds.tlsattacker.core.state.State; +import de.rub.nds.tlsattacker.core.workflow.ParallelExecutor; +import de.rub.nds.tlsattacker.core.workflow.WorkflowTraceResultUtil; +import de.rub.nds.tlsattacker.core.workflow.factory.WorkflowTraceType; +import de.rub.nds.tlsscanner.core.constants.ProtocolType; +import de.rub.nds.tlsscanner.core.constants.TlsAnalyzedProperty; +import de.rub.nds.tlsscanner.core.constants.TlsProbeType; +import de.rub.nds.tlsscanner.core.probe.requirements.ProtocolTypeTrueRequirement; +import de.rub.nds.tlsscanner.serverscanner.probe.requirements.WorkingConfigRequirement; +import de.rub.nds.tlsscanner.serverscanner.report.ServerReport; +import de.rub.nds.tlsscanner.serverscanner.selector.ConfigSelector; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class SrpProbe extends TlsServerProbe { + + private static final Logger LOGGER = LogManager.getLogger(); + + // SRP cipher suites from RFC 5054 + private static final List SRP_CIPHER_SUITES = + Arrays.asList( + CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, + CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, + CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, + CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA); + + // Test SRP identity + private static final byte[] TEST_SRP_IDENTITY = "testuser".getBytes(); + + private Boolean supportsSrpExtension = null; + private Boolean missingSrpExtensionBug = null; + private List supportedSrpCipherSuites = null; + + public SrpProbe(ConfigSelector configSelector, ParallelExecutor parallelExecutor) { + super(parallelExecutor, TlsProbeType.SRP, configSelector); + register( + TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, + TlsAnalyzedProperty.SRP_CIPHERSUITES, + TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG); + } + + @Override + public void executeTest() { + // Test basic SRP support with extension + testSrpWithExtension(); + + // Test for missing SRP extension bug + testMissingSrpExtensionBug(); + } + + private void testSrpWithExtension() { + Config config = configSelector.getBaseConfig(); + config.setWorkflowTraceType(WorkflowTraceType.DYNAMIC_HELLO); + config.setDefaultClientSupportedCipherSuites(SRP_CIPHER_SUITES); + config.setHighestProtocolVersion(ProtocolVersion.TLS12); + config.setEnforceSettings(true); + config.setStopReceivingAfterFatal(true); + config.setStopActionsAfterFatal(true); + config.setAddServerNameIndicationExtension(true); + + // Add SRP extension + SRPExtensionMessage srpExtension = new SRPExtensionMessage(); + srpExtension.setSrpIdentifier(Modifiable.explicit(TEST_SRP_IDENTITY)); + srpExtension.setSrpIdentifierLength(Modifiable.explicit(TEST_SRP_IDENTITY.length)); + + config.setAddSRPExtension(true); + config.setSecureRemotePasswordExtensionIdentifier(TEST_SRP_IDENTITY); + + State state = new State(config); + executeState(state); + + ServerHelloMessage serverHello = + (ServerHelloMessage) + WorkflowTraceResultUtil.getFirstReceivedMessage( + state.getWorkflowTrace(), HandshakeMessageType.SERVER_HELLO); + + if (serverHello != null && serverHello.getSelectedCipherSuite() != null) { + CipherSuite selectedSuite = + CipherSuite.getCipherSuite(serverHello.getSelectedCipherSuite().getValue()); + if (SRP_CIPHER_SUITES.contains(selectedSuite)) { + supportsSrpExtension = true; + supportedSrpCipherSuites = new ArrayList<>(); + supportedSrpCipherSuites.add(selectedSuite); + + // Test other SRP cipher suites + for (CipherSuite suite : SRP_CIPHER_SUITES) { + if (!suite.equals(selectedSuite) && testSingleSrpCipherSuite(suite)) { + supportedSrpCipherSuites.add(suite); + } + } + } else { + supportsSrpExtension = false; + supportedSrpCipherSuites = new ArrayList<>(); + } + } else { + supportsSrpExtension = false; + supportedSrpCipherSuites = new ArrayList<>(); + } + } + + private boolean testSingleSrpCipherSuite(CipherSuite cipherSuite) { + Config config = configSelector.getBaseConfig(); + config.setWorkflowTraceType(WorkflowTraceType.DYNAMIC_HELLO); + config.setDefaultClientSupportedCipherSuites(Arrays.asList(cipherSuite)); + config.setHighestProtocolVersion(ProtocolVersion.TLS12); + config.setEnforceSettings(true); + config.setStopReceivingAfterFatal(true); + config.setStopActionsAfterFatal(true); + config.setAddSRPExtension(true); + config.setSecureRemotePasswordExtensionIdentifier(TEST_SRP_IDENTITY); + + State state = new State(config); + executeState(state); + + ServerHelloMessage serverHello = + (ServerHelloMessage) + WorkflowTraceResultUtil.getFirstReceivedMessage( + state.getWorkflowTrace(), HandshakeMessageType.SERVER_HELLO); + + if (serverHello != null && serverHello.getSelectedCipherSuite() != null) { + CipherSuite selectedSuite = + CipherSuite.getCipherSuite(serverHello.getSelectedCipherSuite().getValue()); + return selectedSuite.equals(cipherSuite); + } + return false; + } + + private void testMissingSrpExtensionBug() { + // Test if server properly rejects SRP cipher suites without SRP extension + Config config = configSelector.getBaseConfig(); + config.setWorkflowTraceType(WorkflowTraceType.DYNAMIC_HELLO); + config.setDefaultClientSupportedCipherSuites(SRP_CIPHER_SUITES); + config.setHighestProtocolVersion(ProtocolVersion.TLS12); + config.setEnforceSettings(true); + config.setStopReceivingAfterFatal(true); + config.setStopActionsAfterFatal(true); + config.setAddSRPExtension(false); // Explicitly don't add SRP extension + + State state = new State(config); + executeState(state); + + // Check if we received an alert + AlertMessage alert = null; + if (WorkflowTraceResultUtil.didReceiveMessage( + state.getWorkflowTrace(), ProtocolMessageType.ALERT)) { + alert = + (AlertMessage) + WorkflowTraceResultUtil.getFirstReceivedMessage( + state.getWorkflowTrace(), ProtocolMessageType.ALERT); + } + + ServerHelloMessage serverHello = + (ServerHelloMessage) + WorkflowTraceResultUtil.getFirstReceivedMessage( + state.getWorkflowTrace(), HandshakeMessageType.SERVER_HELLO); + + if (serverHello != null && serverHello.getSelectedCipherSuite() != null) { + CipherSuite selectedSuite = + CipherSuite.getCipherSuite(serverHello.getSelectedCipherSuite().getValue()); + if (SRP_CIPHER_SUITES.contains(selectedSuite)) { + // Server selected SRP cipher suite without SRP extension - this is a bug + missingSrpExtensionBug = true; + } else { + missingSrpExtensionBug = false; + } + } else if (alert != null && alert.getLevel() != null && alert.getDescription() != null) { + // Check if we got the expected alert + if (alert.getLevel().getValue() == AlertLevel.FATAL.getValue() + && alert.getDescription().getValue() + == AlertDescription.UNKNOWN_PSK_IDENTITY.getValue()) { + // Correct behavior - server sent unknown_psk_identity alert + missingSrpExtensionBug = false; + } else { + // Server sent an alert but not the expected one + missingSrpExtensionBug = false; + } + } else { + // No clear response + missingSrpExtensionBug = false; + } + } + + @Override + public Requirement getRequirements() { + return new ProtocolTypeTrueRequirement(ProtocolType.TLS) + .and(new WorkingConfigRequirement(configSelector)); + } + + @Override + public void adjustConfig(ServerReport report) {} + + @Override + protected void mergeData(ServerReport report) { + if (supportsSrpExtension != null) { + report.putResult(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, supportsSrpExtension); + } else { + report.putResult(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, false); + } + + if (supportedSrpCipherSuites != null && !supportedSrpCipherSuites.isEmpty()) { + report.putResult(TlsAnalyzedProperty.SRP_CIPHERSUITES, supportedSrpCipherSuites); + } else { + report.putResult(TlsAnalyzedProperty.SRP_CIPHERSUITES, new ArrayList()); + } + + if (missingSrpExtensionBug != null) { + report.putResult(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, missingSrpExtensionBug); + } else { + report.putResult(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, false); + } + } +} diff --git a/TLS-Server-Scanner/src/test/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbeIT.java b/TLS-Server-Scanner/src/test/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbeIT.java new file mode 100644 index 000000000..85b3eeb48 --- /dev/null +++ b/TLS-Server-Scanner/src/test/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbeIT.java @@ -0,0 +1,38 @@ +/* + * TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker + * + * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +package de.rub.nds.tlsscanner.serverscanner.probe; + +import de.rub.nds.scanner.core.probe.result.TestResults; +import de.rub.nds.tls.subject.TlsImplementationType; +import de.rub.nds.tlsattacker.util.tests.TestCategories; +import de.rub.nds.tlsscanner.core.constants.TlsAnalyzedProperty; +import org.junit.jupiter.api.Tag; + +@Tag(TestCategories.INTEGRATION_TEST) +public class SrpProbeIT extends AbstractProbeIT { + + public SrpProbeIT() { + super(TlsImplementationType.OPENSSL, "1.1.1f", ""); + } + + @Override + protected TlsServerProbe getProbe() { + return new SrpProbe(configSelector, parallelExecutor); + } + + @Override + protected void prepareReport() {} + + @Override + protected boolean executedAsPlanned() { + // Most servers don't support SRP, so we expect false + return verifyProperty(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION, TestResults.FALSE) + && verifyProperty(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG, TestResults.FALSE); + } +} diff --git a/TLS-Server-Scanner/src/test/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbeTest.java b/TLS-Server-Scanner/src/test/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbeTest.java new file mode 100644 index 000000000..471a11de7 --- /dev/null +++ b/TLS-Server-Scanner/src/test/java/de/rub/nds/tlsscanner/serverscanner/probe/SrpProbeTest.java @@ -0,0 +1,65 @@ +/* + * TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker + * + * Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +package de.rub.nds.tlsscanner.serverscanner.probe; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.rub.nds.scanner.core.constants.TestResults; +import de.rub.nds.tlsscanner.core.constants.TlsAnalyzedProperty; +import de.rub.nds.tlsscanner.core.constants.TlsProbeType; +import de.rub.nds.tlsscanner.serverscanner.report.ServerReport; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SrpProbeTest { + + private ServerReport report; + + @BeforeEach + public void setUp() { + report = new ServerReport(); + } + + @Test + public void testSrpProbeRegistersCorrectProperties() { + SrpProbe probe = new SrpProbe(null, null); + + assertEquals(TlsProbeType.SRP, probe.getType()); + assertTrue( + probe.getAnnouncedResults().contains(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION)); + assertTrue(probe.getAnnouncedResults().contains(TlsAnalyzedProperty.SRP_CIPHERSUITES)); + assertTrue( + probe.getAnnouncedResults() + .contains(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG)); + } + + @Test + public void testMergeDataWithNoSrpSupport() { + // Create a test probe instance with test data + SrpProbe probe = + new SrpProbe(null, null) { + @Override + protected void mergeData(ServerReport report) { + super.mergeData(report); + } + }; + + // Merge data with no SRP support + probe.mergeData(report); + + // Verify the results + assertEquals( + TestResults.FALSE, report.getResult(TlsAnalyzedProperty.SUPPORTS_SRP_EXTENSION)); + assertNotNull(report.getResult(TlsAnalyzedProperty.SRP_CIPHERSUITES)); + assertEquals( + TestResults.FALSE, report.getResult(TlsAnalyzedProperty.MISSING_SRP_EXTENSION_BUG)); + } +}