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 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); } }