From c13227583f8e19f24941465729ff0ab0f1a1e175 Mon Sep 17 00:00:00 2001 From: Daniel Hodder Date: Tue, 21 Jan 2025 15:34:36 +1300 Subject: [PATCH 1/2] Update project to Java 8 so it will build on modern systems Issue: --- .classpath | 85 ++++++++++++++++-------- .settings/org.eclipse.jdt.apt.core.prefs | 2 + .settings/org.eclipse.jdt.core.prefs | 10 ++- pom.xml | 6 +- 4 files changed, 70 insertions(+), 33 deletions(-) create mode 100644 .settings/org.eclipse.jdt.apt.core.prefs diff --git a/.classpath b/.classpath index 6a61f8b..6f9c79d 100644 --- a/.classpath +++ b/.classpath @@ -1,27 +1,58 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 0000000..ec0c557 --- /dev/null +++ b/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=false diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 69c31cd..fa50df0 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,8 +1,12 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/pom.xml b/pom.xml index 62c97a8..665a819 100644 --- a/pom.xml +++ b/pom.xml @@ -28,8 +28,8 @@ maven-compiler-plugin 3.0 - 1.6 - 1.6 + 1.8 + 1.8 @@ -45,7 +45,7 @@ javax.net.ssl;version="0.0.0",javax.net;version="0.0.0",javax.crypto;version="0.0.0" ${project.artifactId} ${project.artifactId} - JavaSE-1.6 + JavaSE-1.8 <_removeheaders>Bnd-LastModified,Private-Package From 8b2a18f169b0e4d108985e20d63a1d7a105c9ba7 Mon Sep 17 00:00:00 2001 From: Daniel Hodder Date: Tue, 21 Jan 2025 15:37:38 +1300 Subject: [PATCH 2/2] Add TLS 1.3 support to protocol and cipher filters This is a lot more of a change than you'd expect since TLS 1.3 Ciphers do not specify a Key Exchange. The Key Exchange has been moved to a message later in the protocol, so there is a new Key Exchange type which defined these ciphers as a special kind of key exchange which can be targeted with the keyExchange, and authentication, filters. The format of the TLS 1.3 cipher specs is also different which required special handling in the Cipher Spec Parser. The _WITH_ has been dropped since the key exchange no longer appears in the spec. Since there are so few the library list lists all the possible ciphers which are defined in the RFC directly now. I have also added the magic markers for the TLS 1.3 handshake to the fips, and forwardSecrecy, matchers since I believe they should be included in those sets. --- .../groktls/cipher/CipherSuiteFilters.java | 6 +- .../impl/cipher/CipherSuiteParserImpl.java | 58 +++++++++++++++++++ .../groktls/impl/cipher/KeyExchangeImpl.java | 8 +++ .../groktls/cipher/CipherSuiteFilterTest.java | 30 +++++++++- .../groktls/cipher/TlsCipherParserTest.java | 9 +++ .../protocol/ProtocolVariantFilterTest.java | 23 ++++---- .../protocol/ProtocolVariantParserTest.java | 1 + .../groktls/protocol/ProtocolVariantTest.java | 12 ++-- 8 files changed, 128 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/archie/groktls/cipher/CipherSuiteFilters.java b/src/main/java/org/archie/groktls/cipher/CipherSuiteFilters.java index a64a5ee..03ae35f 100644 --- a/src/main/java/org/archie/groktls/cipher/CipherSuiteFilters.java +++ b/src/main/java/org/archie/groktls/cipher/CipherSuiteFilters.java @@ -275,7 +275,7 @@ public boolean matches(final CipherSuite cipher, final Set defaults * Filter spec usage: FS. */ public static CipherFilter forwardSecrecy() { - return or(keyExchange("DHE"), keyExchange("ECDHE")); + return or(keyExchange("DHE"), keyExchange("ECDHE"), keyExchange("TLSv1.3_PROTOCOL_DECIDED")); } /** @@ -501,8 +501,8 @@ public boolean matches(final CipherSuite cipher, final Set defaults public static CipherFilter fips() { // http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf // http://csrc.nist.gov/publications/nistpubs/800-52/SP800-52.pdf - return and(or(keyExchange("DH"), keyExchange("DHE"), keyExchange("RSA"), keyExchange("ECDH"), keyExchange("ECDHE")), - or(authentication("DSS"), authentication("RSA"), authentication("ECDSA")), + return and(or(keyExchange("DH"), keyExchange("DHE"), keyExchange("RSA"), keyExchange("ECDH"), keyExchange("ECDHE"), keyExchange("TLSv1.3_PROTOCOL_DECIDED")), + or(authentication("DSS"), authentication("RSA"), authentication("ECDSA"), authentication("TLSv1.3_PROTOCOL_DECIDED")), or(encryption("AES"), encryption("3DES")), or(encryptionMode("CBC"), encryptionMode("GCM"), encryptionMode("CCM")), or(mac("SHA"), mac("SHA256"), mac("SHA384"), mac("SHA512"))); diff --git a/src/main/java/org/archie/groktls/impl/cipher/CipherSuiteParserImpl.java b/src/main/java/org/archie/groktls/impl/cipher/CipherSuiteParserImpl.java index 816f3d4..bf96485 100644 --- a/src/main/java/org/archie/groktls/impl/cipher/CipherSuiteParserImpl.java +++ b/src/main/java/org/archie/groktls/impl/cipher/CipherSuiteParserImpl.java @@ -16,6 +16,8 @@ package org.archie.groktls.impl.cipher; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Matcher; @@ -35,6 +37,18 @@ public class CipherSuiteParserImpl implements ItemParser { private static final Pattern PATTERN_DUAL_KEY_EXCHANGE = Pattern.compile("(RSA_FIPS|SRP_SHA|[^_]+)_([^_]+)(_EXPORT.*)?"); private static final Pattern PATTERN_SINGLE_KEY_EXCHANGE = Pattern.compile("(RSA_FIPS|SRP_SHA|[^_]+)(_EXPORT.*)?"); + private static final Set TLS_1_3_CIPHERS; + + static { + final Set tls13Ciphers = new HashSet<>(); + tls13Ciphers.add("TLS_AES_128_GCM_SHA256"); + tls13Ciphers.add("TLS_AES_256_GCM_SHA384"); + tls13Ciphers.add("TLS_CHACHA20_POLY1305_SHA256"); + tls13Ciphers.add("TLS_AES_128_CCM_SHA256"); + tls13Ciphers.add("TLS_AES_128_CCM_8_SHA256"); + TLS_1_3_CIPHERS = Collections.unmodifiableSet(tls13Ciphers); + } + private static final String PREFIX_SSLV2 = "SSL_CK_"; private static final String PREFIX_SSL = "SSL_"; private static final String PREFIX_TLS = "TLS_"; @@ -50,6 +64,11 @@ public CipherSuite parse(final String cipherSuite) { if (cipherSuite.endsWith(SCSV_SUFFIX)) { return new CipherSuiteImpl(cipherSuite); } + + if (TLS_1_3_CIPHERS.contains(cipherSuite)) { + return parseTls13(cipherSuite); + } + final String cs = patchSSLv2(cipherSuite); final int withSplit = cs.indexOf(WITH); if (withSplit == -1) { @@ -85,6 +104,45 @@ public CipherSuite parse(final String cipherSuite) { return new CipherSuiteImpl(cipherSuite, keyExchange, cipher, hash); } + /** + * TLS 1.3 redefines how the Cipher Spec is written. The key differences are: + *

+ *

    + *
  • There is no + * + *
    +     * _WITH_
    +     * 
    + * + * in the cipher string anymore separating the Key Exchange from the Encryption Algorithm. + *
  • There is no Key Exchange defined in the suite at all. The Key Exchange has been moved to a separate TLS extension in the + * protocol. + * + * Because of these changes the list of ciphers is now very, very, small and so they are listed explicitly for comparison - this might + * not work if this list expands, or if TLS 1.4 comes along and changes more things. For compatibility all TLS 1.3 ciphers are reported + * as using DHE key exchange so that previous matching works. + */ + private CipherSuite parseTls13(final String cipherSuite) { + final String cs = removeTlsPrefix(cipherSuite); + final int lastSep = cs.lastIndexOf(SEPARATOR); + if (lastSep == -1) { + return null; + } + final String cipherSpec = cs.substring(0, lastSep); + final String hashSpec = cs.substring(lastSep + 1); + + final CipherImpl cipher = parseCipher(cipherSpec); + if (cipher == null) { + return null; + } + + final MacImpl hash = parseHash(hashSpec); + if (hash == null) { + return null; + } + return new CipherSuiteImpl(cipherSuite, KeyExchangeImpl.TLSv13_KEY_EXCHANGE, cipher, hash); + } + @Override public Set parse(final Collection ciphers) { return parse(ciphers, null); diff --git a/src/main/java/org/archie/groktls/impl/cipher/KeyExchangeImpl.java b/src/main/java/org/archie/groktls/impl/cipher/KeyExchangeImpl.java index 0d0a340..5eea350 100644 --- a/src/main/java/org/archie/groktls/impl/cipher/KeyExchangeImpl.java +++ b/src/main/java/org/archie/groktls/impl/cipher/KeyExchangeImpl.java @@ -24,6 +24,14 @@ public class KeyExchangeImpl implements KeyExchange { private static final Map ALIASES = new HashMap(); + /** + * TLSv1.3 does not have a static key exchange and instead uses a key exchange message elsewhere in the protocol. Returning this should + * make other filters work correctly with this while marking that no key exchange is part of the specification. From the Protocol + * definition we do know there will be a key exchange so using the {@code NULL} key exchange would not be appropriate. + */ + public static final KeyExchangeImpl TLSv13_KEY_EXCHANGE = new KeyExchangeImpl("TLSv1.3_PROTOCOL_DECIDED", "TLSv1.3_PROTOCOL_DECIDED", + "TLSv1.3_PROTOCOL_DECIDED", false, null); + static { // GOST key exchange from http://tools.ietf.org/html/draft-chudov-cryptopro-cptls-04 ALIASES.put("GOSTR341094", "GOST94"); diff --git a/src/test/java/org/archie/groktls/cipher/CipherSuiteFilterTest.java b/src/test/java/org/archie/groktls/cipher/CipherSuiteFilterTest.java index 85d3d38..f5ba49c 100644 --- a/src/test/java/org/archie/groktls/cipher/CipherSuiteFilterTest.java +++ b/src/test/java/org/archie/groktls/cipher/CipherSuiteFilterTest.java @@ -598,7 +598,35 @@ public void testSpacesInFilterSpec() { "TLS_DH_anon_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_NULL_SHA"); final List expectedCiphers = Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DH_RSA_WITH_AES_128_CBC_SHA"); - ItemFilter filter = new CipherSuiteFilterBuilderImpl().add(cipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA")).add(cipherSuite("TLS_DH_RSA_WITH_AES_128_CBC_SHA")).build(); + final ItemFilter filter = new CipherSuiteFilterBuilderImpl().add(cipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA")).add(cipherSuite("TLS_DH_RSA_WITH_AES_128_CBC_SHA")).build(); checkResult("TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DH_RSA_WITH_AES_128_CBC_SHA", filter, supported, expectedCiphers); } + + @Test + public void testFipsIncludes13() { + final List tls13Ciphers = Arrays.asList("TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "TLS_AES_128_CCM_SHA256", + "TLS_AES_128_CCM_8_SHA256"); + + final List expectedFipsCiphers = Arrays.asList("TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_CCM_SHA256"); + + final ItemFilter filter = new CipherSuiteFilterBuilderImpl().add(fips()).build(); + checkResult("FIPS", filter, tls13Ciphers, expectedFipsCiphers); + } + + @Test + public void testForwardSecrecyIncludes13() { + final List tls13Ciphers = Arrays.asList("TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "TLS_AES_128_CCM_SHA256", + "TLS_AES_128_CCM_8_SHA256"); + + final ItemFilter filter = new CipherSuiteFilterBuilderImpl().add(forwardSecrecy()).build(); + checkResult("FS", filter, tls13Ciphers, tls13Ciphers); + } } diff --git a/src/test/java/org/archie/groktls/cipher/TlsCipherParserTest.java b/src/test/java/org/archie/groktls/cipher/TlsCipherParserTest.java index 088fc8d..a2ff4ca 100644 --- a/src/test/java/org/archie/groktls/cipher/TlsCipherParserTest.java +++ b/src/test/java/org/archie/groktls/cipher/TlsCipherParserTest.java @@ -447,6 +447,15 @@ public void testChaCha_Poly1305_draft_agl_CipherSuites() { check("TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "DHE", "RSA", false, null, "CHACHA20_POLY1305", null, CipherType.AEAD, 256, "SHA256", 256); } + @Test + public void testTls13_RFC8446_CipherSuites() { + check("TLS_AES_128_GCM_SHA256", "TLSv1.3_PROTOCOL_DECIDED", "TLSv1.3_PROTOCOL_DECIDED", false, null, "AES", "GCM", CipherType.AEAD, 128, "SHA256", 256); + check("TLS_AES_256_GCM_SHA384", "TLSv1.3_PROTOCOL_DECIDED", "TLSv1.3_PROTOCOL_DECIDED", false, null, "AES", "GCM", CipherType.AEAD, 256, "SHA384", 384); + check("TLS_CHACHA20_POLY1305_SHA256", "TLSv1.3_PROTOCOL_DECIDED", "TLSv1.3_PROTOCOL_DECIDED", false, null, "CHACHA20_POLY1305", null, CipherType.AEAD, 256, "SHA256", 256); + check("TLS_AES_128_CCM_SHA256", "TLSv1.3_PROTOCOL_DECIDED", "TLSv1.3_PROTOCOL_DECIDED", false, null, "AES", "CCM", CipherType.AEAD, 128, "SHA256", 256); + check("TLS_AES_128_CCM_8_SHA256", "TLSv1.3_PROTOCOL_DECIDED", "TLSv1.3_PROTOCOL_DECIDED", false, null, "AES", "CCM_8", CipherType.AEAD, 128, "SHA256", 256); + } + private void check(final String cipherSuite, final String keyExchange, final String authentication, diff --git a/src/test/java/org/archie/groktls/protocol/ProtocolVariantFilterTest.java b/src/test/java/org/archie/groktls/protocol/ProtocolVariantFilterTest.java index 1cb88fb..0bfb5a6 100644 --- a/src/test/java/org/archie/groktls/protocol/ProtocolVariantFilterTest.java +++ b/src/test/java/org/archie/groktls/protocol/ProtocolVariantFilterTest.java @@ -35,8 +35,9 @@ public class ProtocolVariantFilterTest extends AbstractItemFilterTest ALL_PROTOCOLS = Arrays.asList(TLSv1, TLSv11, TLSv12, SSLv2HELLO, SSLv2, SSLv3); + private static final List ALL_PROTOCOLS = Arrays.asList(TLSv1, TLSv11, TLSv12, TLSv13, SSLv2HELLO, SSLv2, SSLv3); @Override protected ItemFilterSpecParser createSpecParser() { @@ -46,8 +47,8 @@ protected ItemFilterSpecParser createSpecParser() { @Test public void testAll() { final List supported = ALL_PROTOCOLS; - final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12, SSLv3); - ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(all()).build(); + final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12, TLSv13, SSLv3); + final ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(all()).build(); checkResult("ALL", filter, supported, expected); } @@ -73,16 +74,16 @@ public void testDefault() { public void testComplementOfDefault() { final List supported = ALL_PROTOCOLS; final List defaults = Arrays.asList(TLSv1, TLSv11, SSLv3); - final List expected = Arrays.asList(TLSv12); - ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(complementOfDefaults()).build(); + final List expected = Arrays.asList(TLSv12, TLSv13); + final ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(complementOfDefaults()).build(); checkResult("COMPLEMENTOFDEFAULT", filter, supported, expected, defaults); } @Test public void testFamily() { final List supported = ALL_PROTOCOLS; - final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12); - ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(family("TLS")).build(); + final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12, TLSv13); + final ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(family("TLS")).build(); checkResult("fTLS", filter, supported, expected); } @@ -114,16 +115,16 @@ public void testUnsafeAndPseudoProtocols() { @Test public void testMinVersion() { final List supported = ALL_PROTOCOLS; - final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12); - ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(minimumVersion(3, 1)).build(); + final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12, TLSv13); + final ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(minimumVersion(3, 1)).build(); checkResult(">=TLSv1", filter, supported, expected); } @Test public void testMinVersionString() { final List supported = ALL_PROTOCOLS; - final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12); - ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(minimumVersion(TLSv1)).build(); + final List expected = Arrays.asList(TLSv1, TLSv11, TLSv12, TLSv13); + final ItemFilter filter = new ProtocolVariantFilterBuilderImpl().add(minimumVersion(TLSv1)).build(); checkResult(">=TLSv1", filter, supported, expected); } diff --git a/src/test/java/org/archie/groktls/protocol/ProtocolVariantParserTest.java b/src/test/java/org/archie/groktls/protocol/ProtocolVariantParserTest.java index af307e6..55fa8b0 100644 --- a/src/test/java/org/archie/groktls/protocol/ProtocolVariantParserTest.java +++ b/src/test/java/org/archie/groktls/protocol/ProtocolVariantParserTest.java @@ -32,6 +32,7 @@ public void testVariants() { check("TLSv1.0", "TLS", 3, 1, null); check("TLSv1.1", "TLS", 3, 2, null); check("TLSv1.2", "TLS", 3, 3, null); + check("TLSv1.3", "TLS", 3, 4, null); } private void check(final String name, final String family, final int major, final int minor, final String pseudo) { diff --git a/src/test/java/org/archie/groktls/protocol/ProtocolVariantTest.java b/src/test/java/org/archie/groktls/protocol/ProtocolVariantTest.java index 6a82a29..88304ea 100644 --- a/src/test/java/org/archie/groktls/protocol/ProtocolVariantTest.java +++ b/src/test/java/org/archie/groktls/protocol/ProtocolVariantTest.java @@ -24,10 +24,11 @@ public class ProtocolVariantTest { @Test public void testOrdering() { - ProtocolVariant p10 = new ProtocolVariantImpl("TLSv1", "TLS", 3, 1, null); - ProtocolVariant p10b = new ProtocolVariantImpl("SSLv3.1", "SSL", 3, 1, null); - ProtocolVariant p11 = new ProtocolVariantImpl("TLSv1.1", "TLS", 3, 2, null); - ProtocolVariant p12 = new ProtocolVariantImpl("TLSv1.2", "TLS", 3, 3, null); + final ProtocolVariant p10 = new ProtocolVariantImpl("TLSv1", "TLS", 3, 1, null); + final ProtocolVariant p10b = new ProtocolVariantImpl("SSLv3.1", "SSL", 3, 1, null); + final ProtocolVariant p11 = new ProtocolVariantImpl("TLSv1.1", "TLS", 3, 2, null); + final ProtocolVariant p12 = new ProtocolVariantImpl("TLSv1.2", "TLS", 3, 3, null); + final ProtocolVariant p13 = new ProtocolVariantImpl("TLSv1.3", "TLS", 3, 4, null); assertTrue(p10.compareTo(p10b) == 0); assertTrue(p10b.compareTo(p10) == 0); @@ -40,6 +41,9 @@ public void testOrdering() { assertTrue(p11.compareTo(p12) < 0); assertTrue(p12.compareTo(p11) > 0); + + assertTrue(p12.compareTo(p13) < 0); + assertTrue(p13.compareTo(p12) > 0); } }