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/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/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..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()); } @@ -1025,29 +1052,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 +1105,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/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 d0e7fd5c5..2d7d535ce 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"; @@ -535,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/main/java/org/conscrypt/metrics/ConscryptStatsLog.java b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java new file mode 100644 index 000000000..73ea27341 --- /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) { + ReflexiveStatsEvent event = ReflexiveStatsEvent.buildEvent( + atomId, success, protocol, cipherSuite, duration, source.ordinal()); + + 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); } }); } 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(); 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/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/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(), 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..a04788625 100644 --- a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java +++ b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java @@ -16,28 +16,32 @@ 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) + @Test - public void test_SSLSocket_SSLv3Unsupported_35() throws Exception { - assumeFalse(isTlsV1Filtered()); + @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 +49,23 @@ public void test_SSLSocket_SSLv3Unsupported_35() throws Exception { assertThrows(IllegalArgumentException.class, () -> client.setEnabledProtocols(new String[] {"SSL"})); } - @TargetSdkVersion(34) @Test + @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 + @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 +74,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()); + @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 + @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"})); } -} \ No newline at end of file + + @Test + @TargetSdkVersion(34) + public void testInitializeDeprecatedEnabled_34() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, true); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @TargetSdkVersion(36) + public void testInitializeDeprecatedEnabled_36() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, true); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @TargetSdkVersion(34) + public void testInitializeDeprecatedDisabled_34() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, false); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertTrue(TestUtils.isTlsV1Filtered()); + assertFalse(TestUtils.isTlsV1Supported()); + } + + @Test + @TargetSdkVersion(36) + public void testInitializeDeprecatedDisabled_36() { + Provider conscryptProvider = TestUtils.getConscryptProvider(true, false); + assertTrue(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertFalse(TestUtils.isTlsV1Supported()); + } + + @Test + @TargetSdkVersion(34) + public void testInitializeUndeprecatedEnabled_34() { + Provider conscryptProvider = TestUtils.getConscryptProvider(false, true); + assertFalse(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @TargetSdkVersion(36) + public void testInitializeUndeprecatedEnabled_36() { + Provider conscryptProvider = TestUtils.getConscryptProvider(false, true); + assertFalse(TestUtils.isTlsV1Deprecated()); + assertFalse(TestUtils.isTlsV1Filtered()); + assertTrue(TestUtils.isTlsV1Supported()); + } + + @Test + @TargetSdkVersion(34) + public void testInitializeUndeprecatedDisabled_34() { + assertThrows(RuntimeException.class, () -> TestUtils.getConscryptProvider(false, false)); + } + + @Test + @TargetSdkVersion(36) + public void testInitializeUndeprecatedDisabled_36() { + assertThrows(RuntimeException.class, () -> TestUtils.getConscryptProvider(false, false)); + } +} 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();