From 8816adb42aceb7b54b4fa900d214583298e420b4 Mon Sep 17 00:00:00 2001 From: Miguel Date: Tue, 3 Dec 2024 17:11:31 +0000 Subject: [PATCH 1/6] Re-add stats-log class for conscrypt metrics This class is created by codegen in the gmscore version of conscrypt and so we need the logging path to go through it in order for it to be replaced in gmscore --- .../conscrypt/metrics/ConscryptStatsLog.java | 76 +++++++++++++++++++ .../org/conscrypt/metrics/StatsLogImpl.java | 19 ++--- 2 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java diff --git a/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java new file mode 100644 index 000000000..b4b9b906b --- /dev/null +++ b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.conscrypt.metrics; + +import org.conscrypt.Internal; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.lang.Thread.UncaughtExceptionHandler; + +/** + * Reimplement with reflection calls the logging class, + * generated by frameworks/statsd. + *

+ * In case atom is changed, generate new wrapper with stats-log-api-gen + * tool as shown below and add corresponding methods to ReflexiveStatsEvent's + * newEvent() method. + *

+ * $ stats-log-api-gen \ + * --java "common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java" \ + * --module conscrypt \ + * --javaPackage org.conscrypt.metrics \ + * --javaClass ConscryptStatsLog + **/ +@Internal +public final class ConscryptStatsLog { + public static final int TLS_HANDSHAKE_REPORTED = 317; + + private ConscryptStatsLog() {} + + public static void write(int atomId, boolean success, int protocol, int cipherSuite, + int duration, Source source, int[] uids) { + ReflexiveStatsEvent event = ReflexiveStatsEvent.buildEvent( + atomId, success, protocol, cipherSuite, duration, source.ordinal(), uids); + + ReflexiveStatsLog.write(event); + } + + public static void write( + int atomId, boolean success, int protocol, int cipherSuite, int duration, Source source, + int uids[]) { + ReflexiveStatsEvent event = ReflexiveStatsEvent.buildEvent( + atomId, success, protocol, cipherSuite, duration, source.ordinal(), uids); + + ReflexiveStatsLog.write(event); + } + + public static void write(int atomId, int status, int loadedCompatVersion, + int minCompatVersionAvailable, int majorVersion, int minorVersion) { + ReflexiveStatsEvent.Builder builder = ReflexiveStatsEvent.newBuilder(); + builder.setAtomId(atomId); + builder.writeInt(status); + builder.writeInt(loadedCompatVersion); + builder.writeInt(minCompatVersionAvailable); + builder.writeInt(majorVersion); + builder.writeInt(minorVersion); + builder.usePooledBuffer(); + ReflexiveStatsLog.write(builder.build()); + } +} diff --git a/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java b/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java index a47bac9d6..0cc6dd99c 100644 --- a/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java +++ b/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java @@ -85,7 +85,7 @@ public void countTlsHandshake( CipherSuite suite = CipherSuite.forName(cipherSuite); write(TLS_HANDSHAKE_REPORTED, success, proto.getId(), suite.getId(), (int) duration, - Platform.getStatsSource().ordinal(), Platform.getUids()); + Platform.getStatsSource(), Platform.getUids()); } private static int logStoreStateToMetricsState(LogStore.State state) { @@ -123,14 +123,12 @@ public void updateCTLogListStatusChanged(LogStore logStore) { } private void write(int atomId, boolean success, int protocol, int cipherSuite, int duration, - int source, int[] uids) { + org.conscrypt.metrics.Source source, int[] uids) { e.execute(new Runnable() { @Override public void run() { - ReflexiveStatsEvent event = ReflexiveStatsEvent.buildEvent( + ConscryptStatsLog.write( atomId, success, protocol, cipherSuite, duration, source, uids); - - ReflexiveStatsLog.write(event); } }); } @@ -140,15 +138,8 @@ private void write(int atomId, int status, int loadedCompatVersion, e.execute(new Runnable() { @Override public void run() { - ReflexiveStatsEvent.Builder builder = ReflexiveStatsEvent.newBuilder(); - builder.setAtomId(atomId); - builder.writeInt(status); - builder.writeInt(loadedCompatVersion); - builder.writeInt(minCompatVersionAvailable); - builder.writeInt(majorVersion); - builder.writeInt(minorVersion); - builder.usePooledBuffer(); - ReflexiveStatsLog.write(builder.build()); + ConscryptStatsLog.write(atomId, status, loadedCompatVersion, + minCompatVersionAvailable, majorVersion, minorVersion); } }); } From 34840bd2763622d7fd512ead572251126ce88772 Mon Sep 17 00:00:00 2001 From: Miguel Date: Tue, 3 Dec 2024 17:11:31 +0000 Subject: [PATCH 2/6] Re-add stats-log class for conscrypt metrics This class is created by codegen in the gmscore version of conscrypt and so we need the logging path to go through it in order for it to be replaced in gmscore --- .../main/java/org/conscrypt/metrics/ConscryptStatsLog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java index b4b9b906b..73ea27341 100644 --- a/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java +++ b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java @@ -45,9 +45,9 @@ public final class ConscryptStatsLog { private ConscryptStatsLog() {} public static void write(int atomId, boolean success, int protocol, int cipherSuite, - int duration, Source source, int[] uids) { + int duration, Source source) { ReflexiveStatsEvent event = ReflexiveStatsEvent.buildEvent( - atomId, success, protocol, cipherSuite, duration, source.ordinal(), uids); + atomId, success, protocol, cipherSuite, duration, source.ordinal()); ReflexiveStatsLog.write(event); } From 8f4e2be393f901697b9edc99f5cae8d62230df3b Mon Sep 17 00:00:00 2001 From: Miguel Aranda Date: Thu, 21 Nov 2024 15:07:16 +0000 Subject: [PATCH 3/6] Revert^4 "Add support for enabling/disabling TLS v1.0 and 1.1 in Conscrypt." This reverts commit 5ae7b5c2f272365d13c9ff3ba0c7a682375dc1d8. Reason for revert: fixed the failures by checking that API levels are not higher than a 100, and bumped API level check to 36 because I noticed that this version of using reflection is more resilient than the previous one. Change-Id: I42fcb922e046072eea0fa5aee07c513233d2b1e9 --- .../src/main/java/org/conscrypt/Platform.java | 18 ++- .../main/java/org/conscrypt/Conscrypt.java | 17 ++- .../main/java/org/conscrypt/NativeCrypto.java | 59 +++++---- .../java/org/conscrypt/OpenSSLProvider.java | 18 ++- .../src/main/java/org/conscrypt/Platform.java | 19 ++- .../src/main/java/org/conscrypt/Platform.java | 46 ++++--- .../org/conscrypt/TlsDeprecationTest.java | 112 +++++++++++++++--- .../main/java/org/conscrypt/TestUtils.java | 19 ++- 8 files changed, 233 insertions(+), 75 deletions(-) diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java index cc1e10148..3ebc1c217 100644 --- a/android/src/main/java/org/conscrypt/Platform.java +++ b/android/src/main/java/org/conscrypt/Platform.java @@ -69,6 +69,7 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.StandardConstants; import javax.net.ssl.X509TrustManager; +import org.conscrypt.NativeCrypto; /** * Platform-specific methods for unbundled Android. @@ -76,9 +77,13 @@ @Internal final public class Platform { private static final String TAG = "Conscrypt"; + static boolean DEPRECATED_TLS_V1 = true; + static boolean ENABLED_TLS_V1 = false; + private static boolean FILTERED_TLS_V1 = true; private static Method m_getCurveName; static { + NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); try { m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName"); m_getCurveName.setAccessible(true); @@ -89,7 +94,12 @@ final public class Platform { private Platform() {} - public static void setup() {} + public static void setup(boolean deprecatedTlsV1, boolean enabledTlsV1) { + DEPRECATED_TLS_V1 = deprecatedTlsV1; + ENABLED_TLS_V1 = enabledTlsV1; + FILTERED_TLS_V1 = !enabledTlsV1; + NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); + } /** * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider} @@ -955,14 +965,14 @@ public static boolean isJavaxCertificateSupported() { } public static boolean isTlsV1Deprecated() { - return true; + return DEPRECATED_TLS_V1; } public static boolean isTlsV1Filtered() { - return false; + return FILTERED_TLS_V1; } public static boolean isTlsV1Supported() { - return false; + return ENABLED_TLS_V1; } } diff --git a/common/src/main/java/org/conscrypt/Conscrypt.java b/common/src/main/java/org/conscrypt/Conscrypt.java index c48f8f036..53bc16e7a 100644 --- a/common/src/main/java/org/conscrypt/Conscrypt.java +++ b/common/src/main/java/org/conscrypt/Conscrypt.java @@ -160,6 +160,8 @@ public static class ProviderBuilder { private String name = Platform.getDefaultProviderName(); private boolean provideTrustManager = Platform.provideTrustManagerByDefault(); private String defaultTlsProtocol = NativeCrypto.SUPPORTED_PROTOCOL_TLSV1_3; + private boolean deprecatedTlsV1 = true; + private boolean enabledTlsV1 = false; private ProviderBuilder() {} @@ -200,8 +202,21 @@ public ProviderBuilder defaultTlsProtocol(String defaultTlsProtocol) { return this; } + /** Specifies whether TLS v1.0 and 1.1 should be deprecated */ + public ProviderBuilder isTlsV1Deprecated(boolean deprecatedTlsV1) { + this.deprecatedTlsV1 = deprecatedTlsV1; + return this; + } + + /** Specifies whether TLS v1.0 and 1.1 should be enabled */ + public ProviderBuilder isTlsV1Enabled(boolean enabledTlsV1) { + this.enabledTlsV1 = enabledTlsV1; + return this; + } + public Provider build() { - return new OpenSSLProvider(name, provideTrustManager, defaultTlsProtocol); + return new OpenSSLProvider(name, provideTrustManager, + defaultTlsProtocol, deprecatedTlsV1, enabledTlsV1); } } diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java index 445ac0de8..29c48f3e3 100644 --- a/common/src/main/java/org/conscrypt/NativeCrypto.java +++ b/common/src/main/java/org/conscrypt/NativeCrypto.java @@ -1025,29 +1025,48 @@ static native void SSL_set_client_CA_list(long ssl, NativeSsl ssl_holder, byte[] static native void set_SSL_psk_server_callback_enabled(long ssl, NativeSsl ssl_holder, boolean enabled); - private static final String[] ENABLED_PROTOCOLS_TLSV1 = Platform.isTlsV1Deprecated() - ? new String[0] - : new String[] { + public static void setTlsV1DeprecationStatus(boolean deprecated, boolean supported) { + if (deprecated) { + TLSV12_PROTOCOLS = new String[] { + SUPPORTED_PROTOCOL_TLSV1_2, + }; + TLSV13_PROTOCOLS = new String[] { + SUPPORTED_PROTOCOL_TLSV1_2, + SUPPORTED_PROTOCOL_TLSV1_3, + }; + } else { + TLSV12_PROTOCOLS = new String[] { DEPRECATED_PROTOCOL_TLSV1, DEPRECATED_PROTOCOL_TLSV1_1, + SUPPORTED_PROTOCOL_TLSV1_2, }; - - private static final String[] SUPPORTED_PROTOCOLS_TLSV1 = Platform.isTlsV1Supported() - ? new String[] { + TLSV13_PROTOCOLS = new String[] { DEPRECATED_PROTOCOL_TLSV1, DEPRECATED_PROTOCOL_TLSV1_1, - } : new String[0]; + SUPPORTED_PROTOCOL_TLSV1_2, + SUPPORTED_PROTOCOL_TLSV1_3, + }; + } + if (supported) { + SUPPORTED_PROTOCOLS = new String[] { + DEPRECATED_PROTOCOL_TLSV1, + DEPRECATED_PROTOCOL_TLSV1_1, + SUPPORTED_PROTOCOL_TLSV1_2, + SUPPORTED_PROTOCOL_TLSV1_3, + }; + } else { + SUPPORTED_PROTOCOLS = new String[] { + SUPPORTED_PROTOCOL_TLSV1_2, + SUPPORTED_PROTOCOL_TLSV1_3, + }; + } + } /** Protocols to enable by default when "TLSv1.3" is requested. */ - static final String[] TLSV13_PROTOCOLS = ArrayUtils.concatValues( - ENABLED_PROTOCOLS_TLSV1, - SUPPORTED_PROTOCOL_TLSV1_2, - SUPPORTED_PROTOCOL_TLSV1_3); + static String[] TLSV13_PROTOCOLS; /** Protocols to enable by default when "TLSv1.2" is requested. */ - static final String[] TLSV12_PROTOCOLS = ArrayUtils.concatValues( - ENABLED_PROTOCOLS_TLSV1, - SUPPORTED_PROTOCOL_TLSV1_2); + static String[] TLSV12_PROTOCOLS; /** Protocols to enable by default when "TLSv1.1" is requested. */ static final String[] TLSV11_PROTOCOLS = new String[] { @@ -1059,20 +1078,12 @@ static native void SSL_set_client_CA_list(long ssl, NativeSsl ssl_holder, byte[] /** Protocols to enable by default when "TLSv1" is requested. */ static final String[] TLSV1_PROTOCOLS = TLSV11_PROTOCOLS; - static final String[] DEFAULT_PROTOCOLS = TLSV13_PROTOCOLS; - // If we ever get a new protocol go look for tests which are skipped using // assumeTlsV11Enabled() - private static final String[] SUPPORTED_PROTOCOLS = ArrayUtils.concatValues( - SUPPORTED_PROTOCOLS_TLSV1, - SUPPORTED_PROTOCOL_TLSV1_2, - SUPPORTED_PROTOCOL_TLSV1_3); + private static String[] SUPPORTED_PROTOCOLS; public static String[] getDefaultProtocols() { - if (Platform.isTlsV1Deprecated()) { - return DEFAULT_PROTOCOLS.clone(); - } - return SUPPORTED_PROTOCOLS.clone(); + return TLSV13_PROTOCOLS.clone(); } static String[] getSupportedProtocols() { diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java index d0e7fd5c5..7c52c9b1b 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java +++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java @@ -51,17 +51,29 @@ public OpenSSLProvider() { @SuppressWarnings("deprecation") public OpenSSLProvider(String providerName) { - this(providerName, Platform.provideTrustManagerByDefault(), "TLSv1.3"); + this(providerName, Platform.provideTrustManagerByDefault(), "TLSv1.3", + Platform.DEPRECATED_TLS_V1, Platform.ENABLED_TLS_V1); } - OpenSSLProvider(String providerName, boolean includeTrustManager, String defaultTlsProtocol) { + OpenSSLProvider(String providerName, boolean includeTrustManager, + String defaultTlsProtocol) { + this(providerName, includeTrustManager, defaultTlsProtocol, + Platform.DEPRECATED_TLS_V1, Platform.ENABLED_TLS_V1); + } + + OpenSSLProvider(String providerName, boolean includeTrustManager, + String defaultTlsProtocol, boolean deprecatedTlsV1, + boolean enabledTlsV1) { super(providerName, 1.0, "Android's OpenSSL-backed security provider"); // Ensure that the native library has been loaded. NativeCrypto.checkAvailability(); + if (!deprecatedTlsV1 && !enabledTlsV1) { + throw new IllegalArgumentException("TLSv1 is not deprecated and cannot be disabled."); + } // Make sure the platform is initialized. - Platform.setup(); + Platform.setup(deprecatedTlsV1, enabledTlsV1); /* === SSL Contexts === */ String classOpenSSLContextImpl = PREFIX + "OpenSSLContextImpl"; diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java index a651d4788..55f871c03 100644 --- a/openjdk/src/main/java/org/conscrypt/Platform.java +++ b/openjdk/src/main/java/org/conscrypt/Platform.java @@ -84,6 +84,7 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; +import org.conscrypt.NativeCrypto; /** * Platform-specific methods for OpenJDK. @@ -94,9 +95,12 @@ final public class Platform { private static final int JAVA_VERSION = javaVersion0(); private static final Method GET_CURVE_NAME_METHOD; + static boolean DEPRECATED_TLS_V1 = true; + static boolean ENABLED_TLS_V1 = false; + private static boolean FILTERED_TLS_V1 = true; static { - + NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); Method getCurveNameMethod = null; try { getCurveNameMethod = ECParameterSpec.class.getDeclaredMethod("getCurveName"); @@ -109,7 +113,12 @@ final public class Platform { private Platform() {} - static void setup() {} + public static void setup(boolean deprecatedTlsV1, boolean enabledTlsV1) { + DEPRECATED_TLS_V1 = deprecatedTlsV1; + ENABLED_TLS_V1 = enabledTlsV1; + FILTERED_TLS_V1 = !enabledTlsV1; + NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); + } /** @@ -839,14 +848,14 @@ public static boolean isJavaxCertificateSupported() { } public static boolean isTlsV1Deprecated() { - return true; + return DEPRECATED_TLS_V1; } public static boolean isTlsV1Filtered() { - return false; + return FILTERED_TLS_V1; } public static boolean isTlsV1Supported() { - return true; + return ENABLED_TLS_V1; } } diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java index 9691c3de9..2c5301c05 100644 --- a/platform/src/main/java/org/conscrypt/Platform.java +++ b/platform/src/main/java/org/conscrypt/Platform.java @@ -75,18 +75,30 @@ import javax.net.ssl.StandardConstants; import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; - +import libcore.net.NetworkSecurityPolicy; +import org.conscrypt.NativeCrypto; import sun.security.x509.AlgorithmId; @Internal final public class Platform { private static class NoPreloadHolder { public static final Platform MAPPER = new Platform(); } + static boolean DEPRECATED_TLS_V1 = true; + static boolean ENABLED_TLS_V1 = false; + private static boolean FILTERED_TLS_V1 = true; + + static { + NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); + } /** * Runs all the setup for the platform that only needs to run once. */ - public static void setup() { + public static void setup(boolean deprecatedTlsV1, boolean enabledTlsV1) { + DEPRECATED_TLS_V1 = deprecatedTlsV1; + ENABLED_TLS_V1 = enabledTlsV1; + FILTERED_TLS_V1 = !enabledTlsV1; NoPreloadHolder.MAPPER.ping(); + NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); } /** @@ -552,34 +564,34 @@ public static boolean isJavaxCertificateSupported() { } public static boolean isTlsV1Deprecated() { - return true; + return DEPRECATED_TLS_V1; } public static boolean isTlsV1Filtered() { Object targetSdkVersion = getTargetSdkVersion(); - if ((targetSdkVersion != null) && ((int) targetSdkVersion > 34)) + if ((targetSdkVersion != null) && ((int) targetSdkVersion > 35) + && ((int) targetSdkVersion < 100)) return false; - return true; + return FILTERED_TLS_V1; } public static boolean isTlsV1Supported() { - return false; + return ENABLED_TLS_V1; } static Object getTargetSdkVersion() { try { - Class vmRuntime = Class.forName("dalvik.system.VMRuntime"); - if (vmRuntime == null) { - return null; - } - OptionalMethod getSdkVersion = - new OptionalMethod(vmRuntime, - "getTargetSdkVersion"); - return getSdkVersion.invokeStatic(); - } catch (ClassNotFoundException e) { - return null; - } catch (NullPointerException e) { + Class vmRuntimeClass = Class.forName("dalvik.system.VMRuntime"); + Method getRuntimeMethod = vmRuntimeClass.getDeclaredMethod("getRuntime"); + Method getTargetSdkVersionMethod = + vmRuntimeClass.getDeclaredMethod("getTargetSdkVersion"); + Object vmRuntime = getRuntimeMethod.invoke(null); + return getTargetSdkVersionMethod.invoke(vmRuntime); + } catch (IllegalAccessException | + NullPointerException | InvocationTargetException e) { return null; + } catch (Exception e) { + throw new RuntimeException(e); } } } diff --git a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java index 9894fbf3c..ca36e0ad7 100644 --- a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java +++ b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java @@ -16,28 +16,34 @@ package org.conscrypt; -import static org.conscrypt.TestUtils.isTlsV1Filtered; - import libcore.junit.util.SwitchTargetSdkVersionRule; import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion; +import java.security.Provider; import javax.net.ssl.SSLSocket; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.rules.TestRule; +import org.junit.Rule; import org.junit.runners.JUnit4; import org.conscrypt.javax.net.ssl.TestSSLContext; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @RunWith(JUnit4.class) public class TlsDeprecationTest { - @TargetSdkVersion(35) + @Rule + public final TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance(); + @Test - public void test_SSLSocket_SSLv3Unsupported_35() throws Exception { - assumeFalse(isTlsV1Filtered()); + @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + public void test_SSLSocket_SSLv3Unsupported_36() throws Exception { + assertFalse(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(); @@ -45,26 +51,23 @@ public void test_SSLSocket_SSLv3Unsupported_35() throws Exception { assertThrows(IllegalArgumentException.class, () -> client.setEnabledProtocols(new String[] {"SSL"})); } - @TargetSdkVersion(34) @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) public void test_SSLSocket_SSLv3Unsupported_34() throws Exception { + assertTrue(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(); // For app compatibility, SSLv3 is stripped out when setting only. client.setEnabledProtocols(new String[] {"SSLv3"}); assertEquals(0, client.getEnabledProtocols().length); - try { - client.setEnabledProtocols(new String[] {"SSL"}); - fail("SSLSocket should not support SSL protocol"); - } catch (IllegalArgumentException expected) { - // Ignored. - } + assertThrows(IllegalArgumentException.class, () -> client.setEnabledProtocols(new String[] {"SSL"})); } - @TargetSdkVersion(34) @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) public void test_TLSv1Filtered_34() throws Exception { + assertTrue(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(); @@ -73,14 +76,91 @@ public void test_TLSv1Filtered_34() throws Exception { assertEquals("TLSv1.2", client.getEnabledProtocols()[0]); } - @TargetSdkVersion(35) @Test - public void test_TLSv1Filtered_35() throws Exception { - assumeFalse(isTlsV1Filtered()); + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + public void test_TLSv1FilteredEmpty_34() throws Exception { + assertTrue(TestUtils.isTlsV1Filtered()); + TestSSLContext context = TestSSLContext.create(); + final SSLSocket client = + (SSLSocket) context.clientContext.getSocketFactory().createSocket(); + client.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1"}); + assertEquals(0, client.getEnabledProtocols().length); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + public void test_TLSv1Filtered_36() throws Exception { + assertFalse(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(); assertThrows(IllegalArgumentException.class, () -> client.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"})); } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + public void testInitializeDeprecatedEnabled_34() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, true); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + public void testInitializeDeprecatedEnabled_36() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, true); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + public void testInitializeDeprecatedDisabled_34() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, false); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertTrue(TestUtils.isTlsV1Filtered()); + assertFalse(TestUtils.isTlsV1Supported()); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + public void testInitializeDeprecatedDisabled_36() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, false); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertFalse(TestUtils.isTlsV1Supported()); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + public void testInitializeUndeprecatedEnabled_34() { + Provider conscryptProvider = TestUtils.getConscryptProvider(false, true); + assertFalse(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + public void testInitializeUndeprecatedEnabled_36() { + Provider conscryptProvider = TestUtils.getConscryptProvider(false, true); + assertFalse(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + public void testInitializeUndeprecatedDisabled_34() { + assertThrows(RuntimeException.class, () -> TestUtils.getConscryptProvider(false, false)); + } + + @Test + @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + public void testInitializeUndeprecatedDisabled_36() { + assertThrows(RuntimeException.class, () -> TestUtils.getConscryptProvider(false, false)); + } } \ No newline at end of file diff --git a/testing/src/main/java/org/conscrypt/TestUtils.java b/testing/src/main/java/org/conscrypt/TestUtils.java index c6e6e22ff..86967a004 100644 --- a/testing/src/main/java/org/conscrypt/TestUtils.java +++ b/testing/src/main/java/org/conscrypt/TestUtils.java @@ -234,24 +234,33 @@ public static InetAddress getLoopbackAddress() { } } - public static Provider getConscryptProvider() { + public static Provider getConscryptProvider(boolean isTlsV1Deprecated, + boolean isTlsV1Enabled) { try { String defaultName = (String) conscryptClass("Platform") .getDeclaredMethod("getDefaultProviderName") .invoke(null); - Constructor c = conscryptClass("OpenSSLProvider") - .getDeclaredConstructor(String.class, Boolean.TYPE, String.class); + Constructor c = + conscryptClass("OpenSSLProvider") + .getDeclaredConstructor(String.class, Boolean.TYPE, + String.class, Boolean.TYPE, Boolean.TYPE); if (!isClassAvailable("javax.net.ssl.X509ExtendedTrustManager")) { - return (Provider) c.newInstance(defaultName, false, "TLSv1.3"); + return (Provider) c.newInstance(defaultName, false, "TLSv1.3", + isTlsV1Deprecated, isTlsV1Enabled); } else { - return (Provider) c.newInstance(defaultName, true, "TLSv1.3"); + return (Provider) c.newInstance(defaultName, true, "TLSv1.3", + isTlsV1Deprecated, isTlsV1Enabled); } } catch (Exception e) { throw new RuntimeException(e); } } + public static Provider getConscryptProvider() { + return getConscryptProvider(true, false); + } + public static synchronized void installConscryptAsDefaultProvider() { Provider conscryptProvider = getConscryptProvider(); Provider[] providers = Security.getProviders(); From 7a1f5e58a8da53752f0a1db56830664fca4d7468 Mon Sep 17 00:00:00 2001 From: Miguel Aranda Date: Thu, 21 Nov 2024 15:07:16 +0000 Subject: [PATCH 4/6] Revert^4 "Add support for enabling/disabling TLS v1.0 and 1.1 in Conscrypt." This reverts commit 5ae7b5c2f272365d13c9ff3ba0c7a682375dc1d8. Reason for revert: fixed the failures by checking that API levels are not higher than a 100, and bumped API level check to 36 because I noticed that this version of using reflection is more resilient than the previous one. Change-Id: I42fcb922e046072eea0fa5aee07c513233d2b1e9 --- .../SSLSocketVersionCompatibilityTest.java | 30 ------------------- .../org/conscrypt/TlsDeprecationTest.java | 30 +++++++++---------- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java index a05746f40..84d4af44c 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java @@ -1837,22 +1837,6 @@ public void test_SSLSocket_TLSv1Supported() throws Exception { } } -// @TargetSdkVersion(35) - @Test - public void test_SSLSocket_SSLv3Unsupported_35() throws Exception { - assumeFalse(isTlsV1Filtered()); - TestSSLContext context = new TestSSLContext.Builder() - .clientProtocol(clientVersion) - .serverProtocol(serverVersion) - .build(); - final SSLSocket client = - (SSLSocket) context.clientContext.getSocketFactory().createSocket(); - assertThrows(IllegalArgumentException.class, - () -> client.setEnabledProtocols(new String[] {"SSLv3"})); - assertThrows(IllegalArgumentException.class, - () -> client.setEnabledProtocols(new String[] {"SSL"})); - } - // @TargetSdkVersion(34) @Test @Ignore("For platform CTS only") @@ -1889,20 +1873,6 @@ public void test_TLSv1Filtered_34() throws Exception { assertEquals("TLSv1.2", client.getEnabledProtocols()[0]); } -// @TargetSdkVersion(35) - @Test - public void test_TLSv1Filtered_35() throws Exception { - assumeTrue(isTlsV1Filtered()); - TestSSLContext context = new TestSSLContext.Builder() - .clientProtocol(clientVersion) - .serverProtocol(serverVersion) - .build(); - final SSLSocket client = - (SSLSocket) context.clientContext.getSocketFactory().createSocket(); - assertThrows(IllegalArgumentException.class, () -> - client.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"})); - } - @Test public void test_TLSv1Unsupported_notEnabled() { assumeTrue(!isTlsV1Supported()); diff --git a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java index ca36e0ad7..a04788625 100644 --- a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java +++ b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java @@ -37,11 +37,9 @@ @RunWith(JUnit4.class) public class TlsDeprecationTest { - @Rule - public final TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance(); @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + @TargetSdkVersion(36) public void test_SSLSocket_SSLv3Unsupported_36() throws Exception { assertFalse(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); @@ -52,7 +50,7 @@ public void test_SSLSocket_SSLv3Unsupported_36() throws Exception { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void test_SSLSocket_SSLv3Unsupported_34() throws Exception { assertTrue(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); @@ -65,7 +63,7 @@ public void test_SSLSocket_SSLv3Unsupported_34() throws Exception { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void test_TLSv1Filtered_34() throws Exception { assertTrue(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); @@ -77,7 +75,7 @@ public void test_TLSv1Filtered_34() throws Exception { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void test_TLSv1FilteredEmpty_34() throws Exception { assertTrue(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); @@ -88,7 +86,7 @@ public void test_TLSv1FilteredEmpty_34() throws Exception { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + @TargetSdkVersion(36) public void test_TLSv1Filtered_36() throws Exception { assertFalse(TestUtils.isTlsV1Filtered()); TestSSLContext context = TestSSLContext.create(); @@ -99,7 +97,7 @@ public void test_TLSv1Filtered_36() throws Exception { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void testInitializeDeprecatedEnabled_34() { Provider conscryptProvider = TestUtils.getConscryptProvider(true, true); assertTrue(TestUtils.isTlsV1Deprecated()); @@ -108,7 +106,7 @@ public void testInitializeDeprecatedEnabled_34() { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + @TargetSdkVersion(36) public void testInitializeDeprecatedEnabled_36() { Provider conscryptProvider = TestUtils.getConscryptProvider(true, true); assertTrue(TestUtils.isTlsV1Deprecated()); @@ -117,7 +115,7 @@ public void testInitializeDeprecatedEnabled_36() { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void testInitializeDeprecatedDisabled_34() { Provider conscryptProvider = TestUtils.getConscryptProvider(true, false); assertTrue(TestUtils.isTlsV1Deprecated()); @@ -126,7 +124,7 @@ public void testInitializeDeprecatedDisabled_34() { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + @TargetSdkVersion(36) public void testInitializeDeprecatedDisabled_36() { Provider conscryptProvider = TestUtils.getConscryptProvider(true, false); assertTrue(TestUtils.isTlsV1Deprecated()); @@ -135,7 +133,7 @@ public void testInitializeDeprecatedDisabled_36() { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void testInitializeUndeprecatedEnabled_34() { Provider conscryptProvider = TestUtils.getConscryptProvider(false, true); assertFalse(TestUtils.isTlsV1Deprecated()); @@ -144,7 +142,7 @@ public void testInitializeUndeprecatedEnabled_34() { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + @TargetSdkVersion(36) public void testInitializeUndeprecatedEnabled_36() { Provider conscryptProvider = TestUtils.getConscryptProvider(false, true); assertFalse(TestUtils.isTlsV1Deprecated()); @@ -153,14 +151,14 @@ public void testInitializeUndeprecatedEnabled_36() { } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(34) + @TargetSdkVersion(34) public void testInitializeUndeprecatedDisabled_34() { assertThrows(RuntimeException.class, () -> TestUtils.getConscryptProvider(false, false)); } @Test - @SwitchTargetSdkVersionRule.TargetSdkVersion(36) + @TargetSdkVersion(36) public void testInitializeUndeprecatedDisabled_36() { assertThrows(RuntimeException.class, () -> TestUtils.getConscryptProvider(false, false)); } -} \ No newline at end of file +} From c00a3a145382f4d782d8bb6f6f47a50926e468dd Mon Sep 17 00:00:00 2001 From: Miguel Aranda Date: Thu, 21 Nov 2024 15:07:16 +0000 Subject: [PATCH 5/6] Revert^4 "Add support for enabling/disabling TLS v1.0 and 1.1 in Conscrypt." This reverts commit 5ae7b5c2f272365d13c9ff3ba0c7a682375dc1d8. Reason for revert: fixed the failures by checking that API levels are not higher than a 100, and bumped API level check to 36 because I noticed that this version of using reflection is more resilient than the previous one. Change-Id: I42fcb922e046072eea0fa5aee07c513233d2b1e9 --- openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java index fbd041dee..cd83a8435 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java @@ -663,7 +663,7 @@ public AbstractConscryptSocket createSocket(ServerSocket listener) throws IOExce + ": " + connection.clientException.getMessage(), connection.clientException instanceof SSLHandshakeException); assertTrue( - connection.clientException.getMessage().contains("SSLv3 is no longer supported")); + connection.clientException.getMessage().contains("SSLv3")); assertTrue("Expected SSLHandshakeException, but got " + connection.serverException.getClass().getSimpleName() + ": " + connection.serverException.getMessage(), From 26f09ffb4baf84f80a50a9a9165b15c865921933 Mon Sep 17 00:00:00 2001 From: Miguel Aranda Date: Wed, 11 Dec 2024 02:52:39 -0800 Subject: [PATCH 6/6] Spake Change-Id: I6e5f04c1bbbc53ce9f29ecfd904b1c45ad9c0d78 --- .../jni/main/cpp/conscrypt/native_crypto.cc | 264 ++++++++++++++++++ .../main/java/org/conscrypt/NativeCrypto.java | 27 ++ .../main/java/org/conscrypt/NativeSsl.java | 33 ++- .../java/org/conscrypt/OpenSSLProvider.java | 4 + .../java/org/conscrypt/SSLParametersImpl.java | 58 +++- .../java/org/conscrypt/SpakeKeyManager.java | 66 +++++ .../java/org/conscrypt/SpakeTrustManager.java | 32 +++ .../javax/net/ssl/SSLSocketTest.java | 55 ++++ 8 files changed, 536 insertions(+), 3 deletions(-) create mode 100644 common/src/main/java/org/conscrypt/SpakeKeyManager.java create mode 100644 common/src/main/java/org/conscrypt/SpakeTrustManager.java diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc index d1bd6b089..33e518728 100644 --- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc +++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc @@ -10891,6 +10891,270 @@ static jbyteArray NativeCrypto_Scrypt_generate_key(JNIEnv* env, jclass, jbyteArr return key_bytes; } + +#define SPAKE2PLUS_PW_VERIFIER_SIZE 32 +#define SPAKE2PLUS_REGISTRATION_RECORD_SIZE 65 + +static jobjectArray NativeCrypto_SPAKE2PLUS_register( + JNIEnv* env, jclass, + jbyteArray pwArray, jint pwLen, jbyteArray idProverArray, + jlong idProverLen, jbyteArray idVerifierArray, jlong idVerifierLen) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("SPAKE2PLUS_register(%p, %d, %p, %ld, %p, %ld)", + pwArray, + pwLen, + idProverArray, + idProverLen, + idVerifierArray, + idVerifierLen); + + if (pwArray == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "pw == null"); + return {}; + } + + if (idProverArray == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "idProver == null"); + return {}; + } + + if (idVerifierArray == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "idVerifier == null"); + return {}; + } + + ScopedByteArrayRO pw_bytes(env, pwArray); + if (pw_bytes.get() == nullptr) { + return {}; + } + + ScopedByteArrayRO id_prover_bytes(env, idProverArray); + if (id_prover_bytes.get() == nullptr) { + return {}; + } + + ScopedByteArrayRO id_verifier_bytes(env, idVerifierArray); + if (id_verifier_bytes.get() == nullptr) { + return {}; + } + + ScopedByteArrayRO peer_public_key(env, pwArray); + + uint8_t pwVerifierW0[SPAKE2PLUS_PW_VERIFIER_SIZE]; + uint8_t pwVerifierW1[SPAKE2PLUS_PW_VERIFIER_SIZE]; + uint8_t registrationRecord[SPAKE2PLUS_REGISTRATION_RECORD_SIZE]; + + if (!SPAKE2PLUS_register( + /* out_pw_verifier_w0= */ pwVerifierW0, + /* out_pw_verifier_w1= */ pwVerifierW1, + /* out_registration_record= */ registrationRecord, + /* pw= */ pw_bytes, + /* pw_len= */ pwLen, + /* id_prover= */ id_prover_bytes, + /* id_prover_len= */ idProverLen, + /* id_verifier= */ id_verifier_bytes, + /* id_verifier_len= */ idVerifierLen)) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SPAKE2PLUS_register"); + return {}; + } + + ScopedLocalRef pwArray0(env, env->NewByteArray(static_cast(SPAKE2PLUS_PW_VERIFIER_SIZE))); + if (pwArray0.get() == nullptr) { + return {}; + } + ScopedByteArrayRW encBytes0(env, pwArray0.get()); + if (encBytes0.get() == nullptr) { + return {}; + } + memcpy(encBytes0.get(), reinterpret_cast(pwVerifierW0), SPAKE2PLUS_PW_VERIFIER_SIZE); + + ScopedLocalRef pwArray1(env, env->NewByteArray(static_cast(SPAKE2PLUS_PW_VERIFIER_SIZE))); + if (pwArray1.get() == nullptr) { + return {}; + } + ScopedByteArrayRW encBytes1(env, pwArray1.get()); + if (encBytes1.get() == nullptr) { + return {}; + } + memcpy(encBytes1.get(), reinterpret_cast(pwVerifierW1), SPAKE2PLUS_PW_VERIFIER_SIZE); + + ScopedLocalRef registrationRecordArray(env, env->NewByteArray(static_cast(SPAKE2PLUS_REGISTRATION_RECORD_SIZE))); + if (registrationRecordArray.get() == nullptr) { + return {}; + } + ScopedByteArrayRW registrationRecordBytes(env, registrationRecordArray.get()); + if (registrationRecordBytes.get() == nullptr) { + return {}; + } + memcpy(registrationRecordBytes.get(), reinterpret_cast(registrationRecord), SPAKE2PLUS_REGISTRATION_RECORD_SIZE); + + ScopedLocalRef result( + env, env->NewObjectArray(3, conscrypt::jniutil::objectClass, nullptr)); + + env->SetObjectArrayElement(result.get(), 0, pwArray0.release()); + env->SetObjectArrayElement(result.get(), 1, pwArray1.release()); + env->SetObjectArrayElement(result.get(), 2, registrationRecordArray.release()); + + return result.release(); + } + + static jobject NativeCrypto_SSL_CREDENTIAL_new_SPAKE2PLUSV1( + JNIEnv* env, jclass) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("SSL_CREDENTIAL_new_SPAKE2PLUSV1"); + SSL_CREDENTIAL* creds = SSL_CREDENTIAL_new_SPAKE2PLUSV1(); + if (creds == nullptr) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SSL_CREDENTIAL_new_SPAKE2PLUSV1 failed"); + return nullptr; + } + return reinterpret_cast(creds); + + } + + static void NativeCrypto_SSL_CREDENTIAL_set1_PAKE_identities( + JNIEnv* env, jclass, jobject sslCredential, jbyteArray context, + jlong contextLen, jbyteArray serverIdentityArray, jlong serverIdentityLen, + jbyteArray clientIdentityArray, jlong clientIdentityLen) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("SSL_CREDENTIAL_set1_PAKE_identities(%p, %p, %jd, %jd, %p, %jd, %p, %jd)", + sslCredential, context, contextLen, serverIdentityArray, serverIdentityLen, + clientIdentityArray, clientIdentityLen); + + SSL_CREDENTIAL* creds = reinterpret_cast(sslCredential); + if (creds == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "sslCredential cannot be null"); + return; + } + if (context == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "context cannot be null"); + return; + } + if (serverIdentityArray == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "serverIdentityArray cannot be null"); + return; + } + if (clientIdentityArray == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "clientIdentityArray cannot be null"); + return; + } + + ScopedByteArrayRO context_bytes(env, context); + if (context_bytes.get() == nullptr) { + return; + } + + ScopedByteArrayRO server_identity_bytes(env, serverIdentityArray); + if (server_identity_bytes.get() == nullptr) { + return; + } + + ScopedByteArrayRO client_identity_bytes(env, clientIdentityArray); + if (client_identity_bytes.get() == nullptr) { + return; + } + + int ret = SSL_CREDENTIAL_set1_PAKE_identities( + creds, reinterpret_cast(context_bytes.get()), contextLen, + reinterpret_cast(client_identity_bytes.get()), + clientIdentityLen, + reinterpret_cast(server_identity_bytes.get()), + serverIdentityLen); + + if (ret != 1) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SSL_CREDENTIAL_set1_PAKE_identities failed"); + } + } + + static void NativeCrypto_SSL_CREDENTIAL_set1_PAKE_client_password_record( + JNIEnv* env, jclass, + jobject sslCredential, jbyteArray password, jlong passwordLen + ) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("SSL_CREDENTIAL_set1_PAKE_client_password_record(%p, %p, %ld)", + sslCredential, password, passwordLen); + SSL_CREDENTIAL* creds = reinterpret_cast(sslCredential); + if (creds == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "sslCredential cannot be null"); + return; + } + if (password == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "password cannot be null"); + return; + } + + ScopedByteArrayRO password_bytes(env, password); + if (password_bytes.get() == nullptr) { + return; + } + + int ret = SSL_CREDENTIAL_set1_PAKE_client_password_record( + creds, reinterpret_cast(password_bytes.get()), passwordLen); + if (ret != 1) { + conscrypt::jniutil::throwNullPointerException( + env, "SSL_CREDENTIAL_set1_PAKE_client_password_record failed"); + } + } + + static void NativeCrypto_SSL_CREDENTIAL_set1_PAKE_server_password_record( + JNIEnv* env, jclass, jobject sslCredential, + jbyteArray password, jlong passwordLen, jbyteArray registration, jlong registrationLen + ) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("SSL_CREDENTIAL_set1_PAKE_server_password_record(%p, %p, %ld, %p, %ld)", + sslCredential, password, passwordLen, registration, registrationLen); + SSL_CREDENTIAL* creds = reinterpret_cast(sslCredential); + if (creds == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "sslCredential cannot be null"); + return; + } + if (password == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "password cannot be null"); + return; + } + if (registration == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "registration cannot be null"); + return; + } + + ScopedByteArrayRO password_bytes(env, password); + if (password_bytes.get() == nullptr) { + return; + } + + ScopedByteArrayRO registration_bytes(env, registration); + if (registration_bytes.get() == nullptr) { + return; + } + + int ret = SSL_CREDENTIAL_set1_PAKE_server_password_record( + creds, reinterpret_cast(password_bytes.get()), passwordLen, + reinterpret_cast(registration_bytes.get()), + registrationLen); + if (ret != 1) { + conscrypt::jniutil::throwNullPointerException( + env, "SSL_CREDENTIAL_set1_PAKE_server_password_record failed"); + } + } + + + static void NativeCrypto_SSL_CTX_add1_credential( + JNIEnv* env, jclass, jobject sslCtx, jlong sslCredential) { + CHECK_ERROR_QUEUE_ON_RETURN; + JNI_TRACE("SSL_CTX_add1_credential(%p, %p)", sslCtx, sslCredential); + SSL_CTX* ctx = reinterpret_cast(sslCtx); + if (ctx == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "sslCtx cannot be null"); + } + SSL_CREDENTIAL* creds = reinterpret_cast(sslCredential); + if (creds == nullptr) { + conscrypt::jniutil::throwNullPointerException(env, "sslCredential cannot be null"); + } + int ret = SSL_CTX_add1_credential(ctx, creds); + if (ret != 1) { + conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SSL_CTX_add1_credential failed"); + } +} + // TESTING METHODS BEGIN static int NativeCrypto_BIO_read(JNIEnv* env, jclass, jlong bioRef, jbyteArray outputJavaBytes) { diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java index 29c48f3e3..ee3bcdc24 100644 --- a/common/src/main/java/org/conscrypt/NativeCrypto.java +++ b/common/src/main/java/org/conscrypt/NativeCrypto.java @@ -639,6 +639,28 @@ static native long X509_CRL_get_nextUpdate(long x509CrlCtx, OpenSSLX509CRL holde static native int X509_supported_extension(long x509ExtensionRef); + // --- SPAKE --------------------------------------------------------------- + + static native Object[] SPAKE2PLUS_register( + byte[] pwArray, int pwLen, byte[] idProverArray, + long idProverLen, byte[] idVerifierArray, long idVerifierLen); + + static native Object SSL_CREDENTIAL_new_SPAKE2PLUSV1(); + + static native void SSL_CREDENTIAL_set1_PAKE_identities(Object sslCredential, + byte[] context, long contextLen, byte[] serverIdentityArray, long serverIdentityLen, + byte[] clientIdentityArray, long clientIdentityLen); + + static native void SSL_CREDENTIAL_set1_PAKE_client_password_record(Object sslCredential, + byte[] password, long passwordLen + ); + + static native void SSL_CREDENTIAL_set1_PAKE_server_password_record(Object sslCredential, + byte[] password, long passwordLen, byte[] registration, long registrationLen + ); + + static native int SSL_CTX_add1_credential(Object sslCtx, long sslCredential); + // --- ASN1_TIME ----------------------------------------------------------- static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal) throws ParsingException; @@ -963,6 +985,11 @@ static String cipherSuiteFromJava(String javaCipherSuite) { "TLS_PSK_WITH_AES_256_CBC_SHA", }; + /** TLS-SPAKE */ + static final String[] DEFAULT_SPAKE_CIPHER_SUITES = new String[] { + "TLS1_3_NAMED_PAKE_SPAKE2PLUSV1", + }; + static String[] getSupportedCipherSuites() { return SSLUtils.concat(SUPPORTED_TLS_1_3_CIPHER_SUITES, SUPPORTED_TLS_1_2_CIPHER_SUITES.clone()); } diff --git a/common/src/main/java/org/conscrypt/NativeSsl.java b/common/src/main/java/org/conscrypt/NativeSsl.java index 51ae84561..4dd66e71b 100644 --- a/common/src/main/java/org/conscrypt/NativeSsl.java +++ b/common/src/main/java/org/conscrypt/NativeSsl.java @@ -60,6 +60,7 @@ final class NativeSsl { private X509Certificate[] localCertificates; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private volatile long ssl; + private final boolean isSpake; private NativeSsl(long ssl, SSLParametersImpl parameters, SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser, @@ -69,6 +70,10 @@ private NativeSsl(long ssl, SSLParametersImpl parameters, this.handshakeCallbacks = handshakeCallbacks; this.aliasChooser = aliasChooser; this.pskCallbacks = pskCallbacks; + if (parameters.isSpake()) { + initSpake(); + this.isSpake = true; + } } static NativeSsl newInstance(SSLParametersImpl parameters, @@ -86,6 +91,30 @@ BioWrapper newBio() { } } + void initSpake() { + SpakeKeyManager spakeKeyManager = parameters.getSpakeKeyManager(); + byte[] context = spakeKeyManager.getContext(); + byte[] pwArray = spakeKeyManager.getPassword(); + byte[] idProverArray = spakeKeyManager.getIdentityProver(); + byte[] idVerifierArray = spakeKeyManager.getIdentityVerifier(); + Object[] result = NativeCrypto.SPAKE2PLUS_register(pwArray, pwArray.length, idProverArray, + idProverArray.length, idVerifierArray, idVerifierArray.length); + byte[] registration = (byte[]) result[2]; + long ctx = NativeCrypto.SSL_CTX_new(); + Object credential = NativeCrypto.SSL_CREDENTIAL_new_SPAKE2PLUSV1(); + NativeCrypto.SSL_CREDENTIAL_set1_PAKE_identities(credential, + context, context.length, idProverArray, idProverArray.length, + idVerifierArray, idVerifierArray.length); + if (isClient()) { + NativeCrypto.SSL_CREDENTIAL_set1_PAKE_client_password_record(credential, + pwArray, pwArray.length); + } else { + NativeCrypto.SSL_CREDENTIAL_set1_PAKE_server_password_record(credential, + pwArray, pwArray.length, registration, registration.length); + } + NativeCrypto.SSL_CTX_add1_credential(ctx, credential); + } + void offerToResumeSession(long sslSessionNativePointer) throws SSLException { NativeCrypto.SSL_set_session(ssl, this, sslSessionNativePointer); } @@ -349,7 +378,9 @@ void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOExcept // with TLSv1 and SSLv3). NativeCrypto.SSL_set_mode(ssl, this, SSL_MODE_CBC_RECORD_SPLITTING); - setCertificateValidation(); + if (!isSpake) { + setCertificateValidation(); + } setTlsChannelId(channelIdPrivateKey); } diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java index 7c52c9b1b..2d7d535ce 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java +++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java @@ -547,6 +547,10 @@ public OpenSSLProvider(String providerName) { baseClass + "$X25519_CHACHA20"); put("Alg.Alias.ConscryptHpke.DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_GhpkeCHACHA20POLY1305", "DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/CHACHA20POLY1305"); + + /* === SPAKE2+ - Conscrypt internal only === */ + put("TrustManagerFactory.SPAKE2+", SpakeTrustManagerFactory.class.getName()); + put("KeyManagerFactory.SPAKE2+", SpakeKeyManagerFactory.class.getName()); } private boolean classExists(String classname) { diff --git a/common/src/main/java/org/conscrypt/SSLParametersImpl.java b/common/src/main/java/org/conscrypt/SSLParametersImpl.java index 6c5bde587..6c4fd6cc9 100644 --- a/common/src/main/java/org/conscrypt/SSLParametersImpl.java +++ b/common/src/main/java/org/conscrypt/SSLParametersImpl.java @@ -70,6 +70,9 @@ final class SSLParametersImpl implements Cloneable { // source of X.509 certificate based authentication trust decisions or null if not provided private final X509TrustManager x509TrustManager; + private final SpakeTrustManager spakeTrustManager; + private final SpakeKeyManager spakeKeyManager; + // protocols enabled for SSL connection String[] enabledProtocols; // set to indicate when obsolete protocols are filtered @@ -104,6 +107,7 @@ final class SSLParametersImpl implements Cloneable { ApplicationProtocolSelectorAdapter applicationProtocolSelector; boolean useSessionTickets; private Boolean useSni; + private boolean isSpake; /** * Whether the TLS Channel ID extension is enabled. This field is @@ -131,16 +135,20 @@ final class SSLParametersImpl implements Cloneable { x509KeyManager = getDefaultX509KeyManager(); // There's no default PSK key manager pskKeyManager = null; + spakeKeyManager = null; } else { x509KeyManager = findFirstX509KeyManager(kms); pskKeyManager = findFirstPSKKeyManager(kms); + spakeKeyManager = findFirstSpakeKeyManager(kms); } // initialize x509TrustManager if (tms == null) { x509TrustManager = getDefaultX509TrustManager(); + spakeTrustManager = null; } else { x509TrustManager = findFirstX509TrustManager(tms); + spakeTrustManager = findFirstSpakeTrustManager(tms); } // initialize the list of cipher suites and protocols enabled by default @@ -160,8 +168,10 @@ final class SSLParametersImpl implements Cloneable { } boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null); boolean pskCipherSuitesNeeded = pskKeyManager != null; + this.isSpake = spakeKeyManager != null; enabledCipherSuites = getDefaultCipherSuites( - x509CipherSuitesNeeded, pskCipherSuitesNeeded); + x509CipherSuitesNeeded, pskCipherSuitesNeeded, spakeCipherSuitesNeeded); + // We ignore the SecureRandom passed in by the caller. The native code below // directly accesses /dev/urandom, which makes it irrelevant. @@ -174,12 +184,16 @@ private SSLParametersImpl(ClientSessionContext clientSessionContext, X509KeyManager x509KeyManager, PSKKeyManager pskKeyManager, X509TrustManager x509TrustManager, + SpakeTrustManager spakeTrustManager, + SpakeKeyManager spakeKeyManager, SSLParametersImpl sslParams) { this.clientSessionContext = clientSessionContext; this.serverSessionContext = serverSessionContext; this.x509KeyManager = x509KeyManager; this.pskKeyManager = pskKeyManager; this.x509TrustManager = x509TrustManager; + this.spakeTrustManager = spakeTrustManager; + this.spakeKeyManager = spakeKeyManager; this.enabledProtocols = (sslParams.enabledProtocols == null) ? null : sslParams.enabledProtocols.clone(); @@ -248,6 +262,14 @@ PSKKeyManager getPSKKeyManager() { return pskKeyManager; } + /* + * Returns Spake key manager or null for none. + */ + SpakeKeyManager getSpakeKeyManager() { + return spakeKeyManager; + } + + /* * Returns X.509 trust manager or null for none. */ @@ -596,6 +618,18 @@ private static PSKKeyManager findFirstPSKKeyManager(KeyManager[] kms) { return null; } + /* + * Returns the first SpakeKeyManager element in the provided array. + */ + private static SpakeKeyManager findFirstSpakeKeyManager(KeyManager[] kms) { + for (KeyManager km : kms) { + if (km instanceof SpakeKeyManager) { + return (SpakeKeyManager)km; + } + } + return null; + } + /* * Returns the default X.509 trust manager. */ @@ -642,6 +676,18 @@ private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) { return null; } + /* + * Returns the first SpakeTrustManager element in the provided array. + */ + private static SpakeTrustManager findFirstSpakeTrustManager(TrustManager[] tms) { + for (TrustManager tm : tms) { + if (tm instanceof SpakeTrustManager) { + return (SpakeTrustManager) tm; + } + } + return null; + } + String getEndpointIdentificationAlgorithm() { return endpointIdentificationAlgorithm; } @@ -679,7 +725,8 @@ void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) { private static String[] getDefaultCipherSuites( boolean x509CipherSuitesNeeded, - boolean pskCipherSuitesNeeded) { + boolean pskCipherSuitesNeeded, + boolean spakeCipherSuitesNeeded) { if (x509CipherSuitesNeeded) { // X.509 based cipher suites need to be listed. if (pskCipherSuitesNeeded) { @@ -708,6 +755,9 @@ private static String[] getDefaultCipherSuites( // Neither X.509 nor PSK cipher suites need to be listed. return new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV}; } + if (isSpake()) { + return new String[] {NativeCrypto.DEFAULT_SPAKE_CIPHER_SUITES}; + } } /* @@ -724,4 +774,8 @@ boolean isCTVerificationEnabled(String hostname) { } return Platform.isCTVerificationRequired(hostname); } + + boolean isSpake() { + return isSpake; + } } diff --git a/common/src/main/java/org/conscrypt/SpakeKeyManager.java b/common/src/main/java/org/conscrypt/SpakeKeyManager.java new file mode 100644 index 000000000..55cab223c --- /dev/null +++ b/common/src/main/java/org/conscrypt/SpakeKeyManager.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.conscrypt; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.KeyManager; +import java.security.Principal; + +/** + * @hide This class is not part of the Android public SDK API + */ +@Internal +public class SpakeKeyManager implements KeyManager { + byte[] password; + byte[] idProver; + byte[] idVerifier; + byte[] context; + + SpakeKeyManager(byte[] password, byte[] idProver, + byte[] idVerifier, byte[] context) { + this.password = password; + this.idProver = idProver; + this.idVerifier = idVerifier; + this.context = context; + } + + public String chooseEngineAlias(String keyType, + Principal[] issuers, SSLEngine engine) { + return null; + } + + public String chooseEngineClientAlias(String[] keyType, + Principal[] issuers, SSLEngine engine) { + throw new UnsupportedOperationException("Not implemented"); + } + + public byte[] getContext() { + return context; + } + + public byte[] getPassword() { + return password; + } + + public byte[] getIdProver() { + return idProver; + } + + public byte[] getIdVerifier() { + return idVerifier; + } +} \ No newline at end of file diff --git a/common/src/main/java/org/conscrypt/SpakeTrustManager.java b/common/src/main/java/org/conscrypt/SpakeTrustManager.java new file mode 100644 index 000000000..1553e4dd0 --- /dev/null +++ b/common/src/main/java/org/conscrypt/SpakeTrustManager.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.conscrypt; + +import javax.net.ssl.TrustManager; + +/** + * @hide This class is not part of the Android public SDK API + */ +@Internal +public class SpakeTrustManager implements TrustManager { + + SpakeTrustManager() {} + + public void checkClientTrusted() {} + + public void checkServerTrusted() {} +} \ No newline at end of file diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java index c45766076..83c49de59 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java @@ -1080,6 +1080,61 @@ public void writeFromHandshakeListener() throws Exception { assertArrayEquals(ping, buffer); } + @Test + public void testSpake() { + byte[] password; + byte[] context = "osmosis".getBytes(); + Socket plainSocketC; + Socket plainSocketS; + InetAddress hostC = TestUtils.getLoopbackAddress(); + InetAddress hostS = TestUtils.getLoopbackAddress(); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SPAKE2+"); + tmf.init(null); + + SpakeClientKeyManagerParameters kmfParamsClient = new SpakeClientKeyManagerParameters.Builder + .setClientPassword(password) + .setContext(context) + .build(); + + KeyManagerFactory kmfClient = KeyManagerFactory.getInstance("SPAKE2+"); + kmfClient.init(kmfParamsClient); + + SSLContext contextClient = SSLContext.getInstance("TlsV1.3"); + contextClient.init(kmfClient.getKeyManagers(), tmf.getTrustMananagers(), null); + + SocketFactory sfClient = contextClient.getSocketFactory(); + + SSLSocket sslSocketClient = sfClient.createSocket(plainSocketC, hostC, 0, true); + + SpakeServerKeyManagerParameters kmfParamsServer = new SpakeServerKeyManagerParameters.Builder + .setServerPassword(password) + .setContext(context) + .build(); + + KeyManagerFactory kmfServer = KeyManagerFactory.getInstance("SPAKE2+"); + kmfServer.init(kmfParamsServer); + + SSLContext contextServer = SSLContext.getInstance("TlsV1.3"); + contextServer.init(kmfServer.getKeyManagers(), tmf.getTrustMananagers, null); + + SocketFactory sfServer = contextServer.getSocketFactory(); + SSLSocket sslSocketServer = sfServer.createSocket(plainSocketS, hostS, 1, true); + sslSocketServer.setUseClientMode(false); + Future s = runAsync(() -> { + sslSocketServer.startHandshake(); + return null; + }); + Future c = runAsync(() -> { + sslSocketClient.startHandshake(); + return null; + }); + s.get(); + c.get(); + sslSocketServer.close(); + sslSocketClient.close(); + } + private void socketClose(Socket socket) { try { socket.close();