diff --git a/android/lint.xml b/android/lint.xml index 5d43d7ff6..8ad971986 100644 --- a/android/lint.xml +++ b/android/lint.xml @@ -28,4 +28,10 @@ + + + + + + diff --git a/android/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java b/android/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java new file mode 100644 index 000000000..4f5f0e6ad --- /dev/null +++ b/android/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 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; + +import org.conscrypt.metrics.CertificateTransparencyVerificationReason; + +/** + * A default NetworkSecurityPolicy for unbundled Android. + */ +@Internal +public class ConscryptNetworkSecurityPolicy implements NetworkSecurityPolicy { + public static ConscryptNetworkSecurityPolicy getDefault() { + return new ConscryptNetworkSecurityPolicy(); + } + + @Override + public boolean isCertificateTransparencyVerificationRequired(String hostname) { + return false; + } + + @Override + public CertificateTransparencyVerificationReason getCertificateTransparencyVerificationReason( + String hostname) { + return CertificateTransparencyVerificationReason.UNKNOWN; + } + + @Override + public DomainEncryptionMode getDomainEncryptionMode(String hostname) { + return DomainEncryptionMode.UNKNOWN; + } +} diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java index b3433f12c..905835ada 100644 --- a/android/src/main/java/org/conscrypt/Platform.java +++ b/android/src/main/java/org/conscrypt/Platform.java @@ -59,11 +59,13 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; @@ -859,59 +861,8 @@ static boolean supportsX509ExtendedTrustManager() { return Build.VERSION.SDK_INT > 23; } - /** - * Check if SCT verification is required for a given hostname. - * - * SCT Verification is enabled using {@code Security} properties. - * The "conscrypt.ct.enable" property must be true, as well as a per domain property. - * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce." - * is used as the property name. - * Basic globbing is also supported. - * - * For example, for the domain foo.bar.com, the following properties will be - * looked up, in order of precedence. - * - conscrypt.ct.enforce.com.bar.foo - * - conscrypt.ct.enforce.com.bar.* - * - conscrypt.ct.enforce.com.* - * - conscrypt.ct.enforce.* - */ - public static boolean isCTVerificationRequired(String hostname) { - if (hostname == null) { - return false; - } - // TODO: Use the platform version on platforms that support it - - String property = Security.getProperty("conscrypt.ct.enable"); - if (property == null || !Boolean.parseBoolean(property)) { - return false; - } - - List parts = Arrays.asList(hostname.split("\\.")); - Collections.reverse(parts); - - boolean enable = false; - String propertyName = "conscrypt.ct.enforce"; - // The loop keeps going on even once we've found a match - // This allows for finer grained settings on subdomains - for (String part : parts) { - property = Security.getProperty(propertyName + ".*"); - if (property != null) { - enable = Boolean.parseBoolean(property); - } - - propertyName = propertyName + "." + part; - } - - property = Security.getProperty(propertyName); - if (property != null) { - enable = Boolean.parseBoolean(property); - } - return enable; - } - - public static CertificateTransparencyVerificationReason reasonCTVerificationRequired( - String hostname) { - return CertificateTransparencyVerificationReason.UNKNOWN; + static SSLException wrapInvalidEchDataException(SSLException e) { + return e; } static boolean supportsConscryptCertStore() { @@ -940,7 +891,8 @@ static CertBlocklist newDefaultBlocklist() { return null; } - static CertificateTransparency newDefaultCertificateTransparency() { + static CertificateTransparency newDefaultCertificateTransparency( + Supplier policySupplier) { return null; } diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc index eaea7717f..7ede18d2b 100644 --- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc +++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc @@ -58,6 +58,8 @@ #include #include +#include "jni.h" + using conscrypt::AppData; using conscrypt::BioInputStream; using conscrypt::BioOutputStream; @@ -8419,8 +8421,7 @@ static SSL_SESSION* server_session_requested_callback(SSL* ssl, const uint8_t* i return ssl_session_ptr; } -static jint NativeCrypto_EVP_has_aes_hardware(JNIEnv* env, jclass) { - CHECK_ERROR_QUEUE_ON_RETURN; +static jint NativeCrypto_EVP_has_aes_hardware(CRITICAL_JNI_PARAMS) { int ret = 0; ret = EVP_has_aes_hardware(); JNI_TRACE("EVP_has_aes_hardware => %d", ret); @@ -10714,9 +10715,8 @@ static jlong NativeCrypto_SSL_get_timeout(JNIEnv* env, jclass, jlong ssl_address return result; } -static jint NativeCrypto_SSL_get_signature_algorithm_key_type(JNIEnv* env, jclass, - jint signatureAlg) { - CHECK_ERROR_QUEUE_ON_RETURN; +static jint NativeCrypto_SSL_get_signature_algorithm_key_type( + CRITICAL_JNI_PARAMS_COMMA jint signatureAlg) { return SSL_get_signature_algorithm_key_type(signatureAlg); } @@ -11189,7 +11189,7 @@ static jint NativeCrypto_SSL_get_error(JNIEnv* env, jclass, jlong ssl_address, return SSL_get_error(ssl, ret); } -static void NativeCrypto_SSL_clear_error(JNIEnv*, jclass) { +static void NativeCrypto_SSL_clear_error(CRITICAL_JNI_PARAMS) { ERR_clear_error(); } diff --git a/common/src/jni/main/include/conscrypt/jniutil.h b/common/src/jni/main/include/conscrypt/jniutil.h index 70ae3965e..4193d49f9 100644 --- a/common/src/jni/main/include/conscrypt/jniutil.h +++ b/common/src/jni/main/include/conscrypt/jniutil.h @@ -26,6 +26,14 @@ namespace conscrypt { namespace jniutil { +#ifdef __ANDROID__ + #define CRITICAL_JNI_PARAMS + #define CRITICAL_JNI_PARAMS_COMMA +#else + #define CRITICAL_JNI_PARAMS JNIEnv*, jclass + #define CRITICAL_JNI_PARAMS_COMMA JNIEnv*, jclass, +#endif + extern JavaVM* gJavaVM; extern jclass cryptoUpcallsClass; extern jclass openSslInputStreamClass; diff --git a/common/src/main/java/org/conscrypt/AbstractConscryptEngine.java b/common/src/main/java/org/conscrypt/AbstractConscryptEngine.java index 3f0ac2914..6bbd260da 100644 --- a/common/src/main/java/org/conscrypt/AbstractConscryptEngine.java +++ b/common/src/main/java/org/conscrypt/AbstractConscryptEngine.java @@ -135,6 +135,13 @@ public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcs */ abstract void setUseSessionTickets(boolean useSessionTickets); + /** + * This method sets the ECH config data to be used in the TLS handshake. + * + * @param echConfigList the ECH config data to be used in the TLS handshake + */ + abstract void setEchConfigList(byte[] echConfigList); + /** * Sets the list of ALPN protocols. * diff --git a/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java b/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java index b917fb405..e77e37562 100644 --- a/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java +++ b/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java @@ -627,6 +627,13 @@ private boolean isDelegating() { */ abstract void setUseSessionTickets(boolean useSessionTickets); + /** + * This method sets the ECH config data to be used in the TLS handshake. + * + * @param echConfigList the ECH config data to be used in the TLS handshake + */ + abstract void setEchConfigList(byte[] echConfigList); + /** * Enables/disables TLS Channel ID for this server socket. * diff --git a/common/src/main/java/org/conscrypt/BufferUtils.java b/common/src/main/java/org/conscrypt/BufferUtils.java index 3c5ee965a..a5cd5a208 100644 --- a/common/src/main/java/org/conscrypt/BufferUtils.java +++ b/common/src/main/java/org/conscrypt/BufferUtils.java @@ -22,13 +22,18 @@ import java.nio.ByteBuffer; -final class BufferUtils { +/** + * Utility methods for dealing with arrays of ByteBuffers. + * + * @hide This class is not part of the Android public SDK API + */ +public final class BufferUtils { private BufferUtils() {} /** * Throws {@link IllegalArgumentException} if any of the buffers in the array are null. */ - static void checkNotNull(ByteBuffer[] buffers) { + public static void checkNotNull(ByteBuffer[] buffers) { for (ByteBuffer buffer : buffers) { if (buffer == null) { throw new IllegalArgumentException("Null buffer in array"); @@ -39,7 +44,7 @@ static void checkNotNull(ByteBuffer[] buffers) { /** * Returns the total number of bytes remaining in the buffer array. */ - static long remaining(ByteBuffer[] buffers) { + public static long remaining(ByteBuffer[] buffers) { long size = 0; for (ByteBuffer buffer : buffers) { size += buffer.remaining(); @@ -52,7 +57,7 @@ static long remaining(ByteBuffer[] buffers) { * * @throws IllegalArgumentException if there are fewer than {@code toConsume} bytes remaining */ - static void consume(ByteBuffer[] sourceBuffers, int toConsume) { + public static void consume(ByteBuffer[] sourceBuffers, int toConsume) { for (ByteBuffer sourceBuffer : sourceBuffers) { int amount = min(sourceBuffer.remaining(), toConsume); if (amount > 0) { @@ -72,7 +77,7 @@ static void consume(ByteBuffer[] sourceBuffers, int toConsume) { * Looks for a buffer in the buffer array which EITHER is larger than {@code minSize} AND * has no preceding non-empty buffers OR is the only non-empty buffer in the array. */ - static ByteBuffer getBufferLargerThan(ByteBuffer[] buffers, int minSize) { + public static ByteBuffer getBufferLargerThan(ByteBuffer[] buffers, int minSize) { int length = buffers.length; for (int i = 0; i < length; i++) { ByteBuffer buffer = buffers[i]; diff --git a/common/src/main/java/org/conscrypt/CertBlocklist.java b/common/src/main/java/org/conscrypt/CertBlocklist.java index 0ed68b0ab..056718f02 100644 --- a/common/src/main/java/org/conscrypt/CertBlocklist.java +++ b/common/src/main/java/org/conscrypt/CertBlocklist.java @@ -16,12 +16,15 @@ package org.conscrypt; +import org.conscrypt.Internal; + import java.math.BigInteger; import java.security.PublicKey; /** * A set of certificates that are blacklisted from trust. */ +@Internal public interface CertBlocklist { /** * Returns whether the given public key is in the blacklist. diff --git a/common/src/main/java/org/conscrypt/CertBlocklistEntry.java b/common/src/main/java/org/conscrypt/CertBlocklistEntry.java new file mode 100644 index 000000000..950d568d5 --- /dev/null +++ b/common/src/main/java/org/conscrypt/CertBlocklistEntry.java @@ -0,0 +1,37 @@ +/* + * Copyright 2025 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; + +import org.conscrypt.Internal; + +/** + * An entry in the blocklist, for the purpose of reporting. + */ +@Internal +public interface CertBlocklistEntry { + enum Origin { SHA1_TEST, SHA1_BUILT_IN, SHA1_FILE, SHA256_TEST, SHA256_BUILT_IN, SHA256_FILE } + + /** + * Returns the origin of this entry. + */ + Origin getOrigin(); + + /** + * Returns the index of this entry in its blocklist. + */ + int getIndex(); +} diff --git a/common/src/main/java/org/conscrypt/Conscrypt.java b/common/src/main/java/org/conscrypt/Conscrypt.java index b868d6959..819469d27 100644 --- a/common/src/main/java/org/conscrypt/Conscrypt.java +++ b/common/src/main/java/org/conscrypt/Conscrypt.java @@ -400,6 +400,16 @@ public static void setUseSessionTickets(SSLSocket socket, boolean useSessionTick toConscrypt(socket).setUseSessionTickets(useSessionTickets); } + /** + * This method sets the ECH config data to be used in the TLS handshake. + * + * @param socket the socket + * @param echConfigList the ECH config data to be used in the TLS handshake + */ + public static void setEchConfigList(SSLSocket socket, byte[] echConfigList) { + toConscrypt(socket).setEchConfigList(echConfigList); + } + /** * Enables/disables TLS Channel ID for the given server-side socket. * @@ -699,6 +709,16 @@ public static void setUseSessionTickets(SSLEngine engine, boolean useSessionTick toConscrypt(engine).setUseSessionTickets(useSessionTickets); } + /** + * This method sets the ECH config data to be used in the TLS handshake. + * + * @param engine the engine + * @param echConfigList the ECH config data to be used in the TLS handshake + */ + public static void setEchConfigList(SSLEngine engine, byte[] echConfigList) { + toConscrypt(engine).setEchConfigList(echConfigList); + } + /** * Sets the application-layer protocols (ALPN) in prioritization order. * diff --git a/common/src/main/java/org/conscrypt/ConscryptEngine.java b/common/src/main/java/org/conscrypt/ConscryptEngine.java index 8dd220e0d..ac721065a 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngine.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngine.java @@ -1745,6 +1745,11 @@ void setUseSessionTickets(boolean useSessionTickets) { sslParameters.setUseSessionTickets(useSessionTickets); } + @Override + void setEchConfigList(byte[] echConfigList) { + sslParameters.setEchConfigList(echConfigList); + } + @Override String[] getApplicationProtocols() { return sslParameters.getApplicationProtocols(); diff --git a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java index 93de52def..c4e4bd439 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java @@ -463,6 +463,11 @@ public final void setUseSessionTickets(boolean useSessionTickets) { engine.setUseSessionTickets(useSessionTickets); } + @Override + public final void setEchConfigList(byte[] echConfigList) { + engine.setEchConfigList(echConfigList); + } + @Override public final void setChannelIdEnabled(boolean enabled) { engine.setChannelIdEnabled(enabled); diff --git a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java index ba5a525cb..85e36f187 100644 --- a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java @@ -773,6 +773,11 @@ public final void setUseSessionTickets(boolean useSessionTickets) { sslParameters.setUseSessionTickets(useSessionTickets); } + @Override + public final void setEchConfigList(byte[] echConfigList) { + sslParameters.setEchConfigList(echConfigList); + } + /** * This method enables Server Name Indication. If the hostname is not a valid SNI hostname, * the SNI extension will be omitted from the handshake. diff --git a/common/src/main/java/org/conscrypt/ConscryptX509TrustManager.java b/common/src/main/java/org/conscrypt/ConscryptX509TrustManager.java new file mode 100644 index 000000000..f3586d6f9 --- /dev/null +++ b/common/src/main/java/org/conscrypt/ConscryptX509TrustManager.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2025 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; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * Interface for TrustManager methods implemented in Conscrypt but not part of + * the standard X509TrustManager or X509ExtendedTrustManager. + * + * These methods can be called by the Android framework. Extend + * X509TrustManagerExtensions if these need to be visible to apps. + */ +@Internal +public interface ConscryptX509TrustManager { + /** + * Verifies the given certificate chain. + * + *

See {@link X509TrustManager#checkServerTrusted(X509Certificate[], String)} for a + * description of the chain and authType parameters. The final parameter, host, should be the + * hostname of the server.

+ * + * @throws CertificateException if the chain does not verify correctly. + * @return the properly ordered chain used for verification as a list of X509Certificates. + */ + public List checkServerTrusted(X509Certificate[] chain, String authType, + String hostname) throws CertificateException; + + /** + * Verifies the given certificate chain. + * + *

See {@link X509TrustManager#checkServerTrusted(X509Certificate[], String)} for a + * description of the chain and authType parameters. The final parameter, host, should be the + * hostname of the server. + * + *

ocspData and tlsSctData may be provided to verify any Signed Certificate Timestamp (SCT) + * attached to the connection. These are ASN.1 octet strings (SignedCertificateTimestampList) as + * described in RFC 6962, Section 3.3. Note that SCTs embedded in the certificate chain will + * automatically be processed. + * + * @throws CertificateException if the chain does not verify correctly. + * @return the properly ordered chain used for verification as a list of X509Certificates. + */ + public List checkServerTrusted(X509Certificate[] chain, byte[] ocspData, + byte[] tlsSctData, String authType, + String hostname) throws CertificateException; +} diff --git a/common/src/main/java/org/conscrypt/DomainEncryptionMode.java b/common/src/main/java/org/conscrypt/DomainEncryptionMode.java new file mode 100644 index 000000000..ab878a468 --- /dev/null +++ b/common/src/main/java/org/conscrypt/DomainEncryptionMode.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2025 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; + +@Internal enum DomainEncryptionMode { UNKNOWN, DISABLED, OPPORTUNISTIC, ENABLED, REQUIRED } diff --git a/common/src/main/java/org/conscrypt/EchOptions.java b/common/src/main/java/org/conscrypt/EchOptions.java new file mode 100644 index 000000000..3715a4819 --- /dev/null +++ b/common/src/main/java/org/conscrypt/EchOptions.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2025 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; + +@Internal +class EchOptions { + private final byte[] configList; + private final boolean enableGrease; + + EchOptions(byte[] configList, boolean enableGrease) { + this.configList = configList; + this.enableGrease = enableGrease; + } + + byte[] getConfigList() { + return configList; + } + + boolean isGreaseEnabled() { + return enableGrease; + } +} diff --git a/common/src/main/java/org/conscrypt/Java8EngineWrapper.java b/common/src/main/java/org/conscrypt/Java8EngineWrapper.java index 62f89d264..c6a0b7d38 100644 --- a/common/src/main/java/org/conscrypt/Java8EngineWrapper.java +++ b/common/src/main/java/org/conscrypt/Java8EngineWrapper.java @@ -275,6 +275,11 @@ void setUseSessionTickets(boolean useSessionTickets) { delegate.setUseSessionTickets(useSessionTickets); } + @Override + void setEchConfigList(byte[] echConfigList) { + delegate.setEchConfigList(echConfigList); + } + @Override void setApplicationProtocols(String[] protocols) { delegate.setApplicationProtocols(protocols); diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java index e939f43b7..d4fe34101 100644 --- a/common/src/main/java/org/conscrypt/NativeCrypto.java +++ b/common/src/main/java/org/conscrypt/NativeCrypto.java @@ -18,6 +18,8 @@ import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException; +// android-add: import dalvik.annotation.optimization.CriticalNative; +// android-add: import dalvik.annotation.optimization.FastNative; import java.io.FileDescriptor; import java.io.IOException; import java.io.OutputStream; @@ -61,6 +63,7 @@ public final class NativeCrypto { // --- OpenSSL library initialization -------------------------------------- private static final UnsatisfiedLinkError loadError; + static { UnsatisfiedLinkError error = null; try { @@ -75,8 +78,8 @@ public final class NativeCrypto { } /** - * Checks to see whether or not the native library was successfully loaded. If not, throws - * the {@link UnsatisfiedLinkError} that was encountered while attempting to load the library. + * Checks to see whether or not the native library was successfully loaded. If not, throws the + * {@link UnsatisfiedLinkError} that was encountered while attempting to load the library. */ static void checkAvailability() { if (loadError != null) { @@ -86,23 +89,32 @@ static void checkAvailability() { // --- DSA/RSA public/private key handling functions ----------------------- + // android-add: @FastNative static native long EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q, byte[] dmp1, byte[] dmq1, byte[] iqmp); + // android-add: @FastNative static native int EVP_PKEY_type(NativeRef.EVP_PKEY pkey); + // android-add: @FastNative static native String EVP_PKEY_print_public(NativeRef.EVP_PKEY pkeyRef); + // android-add: @FastNative static native String EVP_PKEY_print_params(NativeRef.EVP_PKEY pkeyRef); + // android-add: @FastNative static native void EVP_PKEY_free(long pkey); + // android-add: @FastNative static native int EVP_PKEY_cmp(NativeRef.EVP_PKEY pkey1, NativeRef.EVP_PKEY pkey2); + // android-add: @FastNative static native byte[] EVP_marshal_private_key(NativeRef.EVP_PKEY pkey); + // android-add: @FastNative static native long EVP_parse_private_key(byte[] data) throws ParsingException; + // android-add: @FastNative static native byte[] EVP_marshal_public_key(NativeRef.EVP_PKEY pkey); static native long EVP_PKEY_from_private_key_info(byte[] data, int[] algs) @@ -123,33 +135,44 @@ static native long EVP_PKEY_from_subject_public_key_info(byte[] data, int[] algs static native byte[] EVP_PKEY_get_private_seed(NativeRef.EVP_PKEY pkey); + // android-add: @FastNative static native byte[] EVP_raw_X25519_private_key(byte[] data) throws ParsingException, InvalidKeyException; + // android-add: @FastNative static native long EVP_parse_public_key(byte[] data) throws ParsingException; + // android-add: @FastNative static native long PEM_read_bio_PUBKEY(long bioCtx); + // android-add: @FastNative static native long PEM_read_bio_PrivateKey(long bioCtx); + // android-add: @FastNative static native long getRSAPrivateKeyWrapper(PrivateKey key, byte[] modulus); + // android-add: @FastNative static native long getECPrivateKeyWrapper(PrivateKey key, NativeRef.EC_GROUP ecGroupRef); static native long RSA_generate_key_ex(int modulusBits, byte[] publicExponent); + // android-add: @FastNative static native int RSA_size(NativeRef.EVP_PKEY pkey); + // android-add: @FastNative static native int RSA_private_encrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding); + // android-add: @FastNative static native int RSA_public_decrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding) throws BadPaddingException, SignatureException; + // android-add: @FastNative static native int RSA_public_encrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding); + // android-add: @FastNative static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding) throws BadPaddingException, SignatureException; @@ -157,11 +180,13 @@ static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, NativeRe /* * Returns array of {n, e} */ + // android-add: @FastNative static native byte[][] get_RSA_public_params(NativeRef.EVP_PKEY rsa); /* * Returns array of {n, e, d, p, q, dmp1, dmq1, iqmp} */ + // android-add: @FastNative static native byte[][] get_RSA_private_params(NativeRef.EVP_PKEY rsa); // --- ChaCha20 ----------------------- @@ -169,91 +194,122 @@ static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, NativeRe /* * Returns the encrypted or decrypted version of the data. */ + // android-add: @FastNative static native void chacha20_encrypt_decrypt(byte[] in, int inOffset, byte[] out, int outOffset, int length, byte[] key, byte[] nonce, int blockCounter); // --- EC functions -------------------------- + // android-add: @FastNative static native long EVP_PKEY_new_EC_KEY(NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pubkeyRef, byte[] privkey); + // android-add: @FastNative static native long EC_GROUP_new_by_curve_name(String curveName); + // android-add: @FastNative static native long EC_GROUP_new_arbitrary(byte[] p, byte[] a, byte[] b, byte[] x, byte[] y, byte[] order, int cofactor); + // android-add: @FastNative static native String EC_GROUP_get_curve_name(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native byte[][] EC_GROUP_get_curve(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native void EC_GROUP_clear_free(long groupRef); + // android-add: @FastNative static native long EC_GROUP_get_generator(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native byte[] EC_GROUP_get_order(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native int EC_GROUP_get_degree(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native byte[] EC_GROUP_get_cofactor(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native long EC_POINT_new(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native void EC_POINT_clear_free(long pointRef); + // android-add: @FastNative static native byte[][] EC_POINT_get_affine_coordinates(NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pointRef); + // android-add: @FastNative static native void EC_POINT_set_affine_coordinates(NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pointRef, byte[] x, byte[] y); static native long EC_KEY_generate_key(NativeRef.EC_GROUP groupRef); + // android-add: @FastNative static native long EC_KEY_get1_group(NativeRef.EVP_PKEY pkeyRef); + // android-add: @FastNative static native byte[] EC_KEY_get_private_key(NativeRef.EVP_PKEY keyRef); + // android-add: @FastNative static native long EC_KEY_get_public_key(NativeRef.EVP_PKEY keyRef); + // android-add: @FastNative static native byte[] EC_KEY_marshal_curve_name(NativeRef.EC_GROUP groupRef) throws IOException; + // android-add: @FastNative static native long EC_KEY_parse_curve_name(byte[] encoded) throws IOException; + // android-add: @FastNative static native int ECDH_compute_key(byte[] out, int outOffset, NativeRef.EVP_PKEY publicKeyRef, NativeRef.EVP_PKEY privateKeyRef) throws InvalidKeyException, IndexOutOfBoundsException; + // android-add: @FastNative static native int ECDSA_size(NativeRef.EVP_PKEY pkey); + // android-add: @FastNative static native int ECDSA_sign(byte[] data, int dataLen, byte[] sig, NativeRef.EVP_PKEY pkey); + // android-add: @FastNative static native int ECDSA_verify(byte[] data, int dataLen, byte[] sig, NativeRef.EVP_PKEY pkey); // --- MLDSA65 -------------------------------------------------------------- + // android-add: @FastNative static native byte[] MLDSA65_public_key_from_seed(byte[] privateKeySeed); // --- MLDSA87 -------------------------------------------------------------- + // android-add: @FastNative static native byte[] MLDSA87_public_key_from_seed(byte[] privateKeySeed); // --- SLHDSA_SHA2_128S -------------------------------------------------------------- static native void SLHDSA_SHA2_128S_generate_key(byte[] outPublicKey, byte[] outPrivateKey); + // android-add: @FastNative static native byte[] SLHDSA_SHA2_128S_sign(byte[] data, int dataLen, byte[] privateKey); + // android-add: @FastNative static native int SLHDSA_SHA2_128S_verify(byte[] data, int dataLen, byte[] sig, byte[] publicKey); // --- Curve25519 -------------- + // android-add: @FastNative static native boolean X25519(byte[] out, byte[] privateKey, byte[] publicKey) throws InvalidKeyException; + // android-add: @FastNative static native void X25519_keypair(byte[] outPublicKey, byte[] outPrivateKey); + // android-add: @FastNative static native void ED25519_keypair(byte[] outPublicKey, byte[] outPrivateKey); // --- X-Wing -------------- @@ -263,154 +319,207 @@ static native boolean X25519(byte[] out, byte[] privateKey, byte[] publicKey) // --- Message digest functions -------------- // These return const references + // android-add: @FastNative static native long EVP_get_digestbyname(String name); + // android-add: @FastNative static native int EVP_MD_size(long evp_md_const); // --- Message digest context functions -------------- + // android-add: @FastNative static native long EVP_MD_CTX_create(); + // android-add: @FastNative static native void EVP_MD_CTX_cleanup(NativeRef.EVP_MD_CTX ctx); + // android-add: @FastNative static native void EVP_MD_CTX_destroy(long ctx); + // android-add: @FastNative static native int EVP_MD_CTX_copy_ex(NativeRef.EVP_MD_CTX dst_ctx, NativeRef.EVP_MD_CTX src_ctx); // --- Digest handling functions ------------------------------------------- + // android-add: @FastNative static native int EVP_DigestInit_ex(NativeRef.EVP_MD_CTX ctx, long evp_md); + // android-add: @FastNative static native void EVP_DigestUpdate(NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length); + // android-add: @FastNative static native void EVP_DigestUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length); + // android-add: @FastNative static native int EVP_DigestFinal_ex(NativeRef.EVP_MD_CTX ctx, byte[] hash, int offset); // --- Signature handling functions ---------------------------------------- + // android-add: @FastNative static native long EVP_DigestSignInit(NativeRef.EVP_MD_CTX ctx, long evpMdRef, NativeRef.EVP_PKEY key); + // android-add: @FastNative static native long EVP_DigestVerifyInit(NativeRef.EVP_MD_CTX ctx, long evpMdRef, NativeRef.EVP_PKEY key); + // android-add: @FastNative static native void EVP_DigestSignUpdate(NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length); + // android-add: @FastNative static native void EVP_DigestSignUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length); + // android-add: @FastNative static native void EVP_DigestVerifyUpdate(NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length); + // android-add: @FastNative static native void EVP_DigestVerifyUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length); + // android-add: @FastNative static native byte[] EVP_DigestSignFinal(NativeRef.EVP_MD_CTX ctx); + // android-add: @FastNative static native boolean EVP_DigestVerifyFinal(NativeRef.EVP_MD_CTX ctx, byte[] signature, int offset, int length) throws IndexOutOfBoundsException; + // android-add: @FastNative static native byte[] EVP_DigestSign(NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length); + // android-add: @FastNative static native boolean EVP_DigestVerify(NativeRef.EVP_MD_CTX ctx, byte[] sigBuffer, int sigOffset, int sigLen, byte[] dataBuffer, int dataOffset, int dataLen); + // android-add: @FastNative static native long EVP_PKEY_encrypt_init(NativeRef.EVP_PKEY pkey) throws InvalidKeyException; + // android-add: @FastNative static native int EVP_PKEY_encrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset, byte[] input, int inOffset, int inLength) throws IndexOutOfBoundsException, BadPaddingException; + // android-add: @FastNative static native long EVP_PKEY_decrypt_init(NativeRef.EVP_PKEY pkey) throws InvalidKeyException; + // android-add: @FastNative static native int EVP_PKEY_decrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset, byte[] input, int inOffset, int inLength) throws IndexOutOfBoundsException, BadPaddingException; + // android-add: @FastNative static native void EVP_PKEY_CTX_free(long pkeyCtx); + // android-add: @FastNative static native void EVP_PKEY_CTX_set_rsa_padding(long ctx, int pad) throws InvalidAlgorithmParameterException; + // android-add: @FastNative static native void EVP_PKEY_CTX_set_rsa_pss_saltlen(long ctx, int len) throws InvalidAlgorithmParameterException; + // android-add: @FastNative static native void EVP_PKEY_CTX_set_rsa_mgf1_md(long ctx, long evpMdRef) throws InvalidAlgorithmParameterException; + // android-add: @FastNative static native void EVP_PKEY_CTX_set_rsa_oaep_md(long ctx, long evpMdRef) throws InvalidAlgorithmParameterException; + // android-add: @FastNative static native void EVP_PKEY_CTX_set_rsa_oaep_label(long ctx, byte[] label) throws InvalidAlgorithmParameterException; // --- Block ciphers ------------------------------------------------------- // These return const references + // android-add: @FastNative static native long EVP_get_cipherbyname(String string); + // android-add: @FastNative static native void EVP_CipherInit_ex(NativeRef.EVP_CIPHER_CTX ctx, long evpCipher, byte[] key, byte[] iv, boolean encrypting); + // android-add: @FastNative static native int EVP_CipherUpdate(NativeRef.EVP_CIPHER_CTX ctx, byte[] out, int outOffset, byte[] in, int inOffset, int inLength) throws IndexOutOfBoundsException; + // android-add: @FastNative static native int EVP_CipherFinal_ex(NativeRef.EVP_CIPHER_CTX ctx, byte[] out, int outOffset) throws BadPaddingException, IllegalBlockSizeException; + // android-add: @FastNative static native int EVP_CIPHER_iv_length(long evpCipher); + // android-add: @FastNative static native long EVP_CIPHER_CTX_new(); + // android-add: @FastNative static native int EVP_CIPHER_CTX_block_size(NativeRef.EVP_CIPHER_CTX ctx); + // android-add: @FastNative static native int get_EVP_CIPHER_CTX_buf_len(NativeRef.EVP_CIPHER_CTX ctx); + // android-add: @FastNative static native boolean get_EVP_CIPHER_CTX_final_used(NativeRef.EVP_CIPHER_CTX ctx); + // android-add: @FastNative static native void EVP_CIPHER_CTX_set_padding(NativeRef.EVP_CIPHER_CTX ctx, boolean enablePadding); + // android-add: @FastNative static native void EVP_CIPHER_CTX_set_key_length(NativeRef.EVP_CIPHER_CTX ctx, int keyBitSize); + // android-add: @FastNative static native void EVP_CIPHER_CTX_free(long ctx); // --- AEAD ---------------------------------------------------------------- + // android-add: @FastNative static native long EVP_aead_aes_128_gcm(); + // android-add: @FastNative static native long EVP_aead_aes_256_gcm(); + // android-add: @FastNative static native long EVP_aead_chacha20_poly1305(); + // android-add: @FastNative static native long EVP_aead_aes_128_gcm_siv(); + // android-add: @FastNative static native long EVP_aead_aes_256_gcm_siv(); + // android-add: @FastNative static native int EVP_AEAD_max_overhead(long evpAead); + // android-add: @FastNative static native int EVP_AEAD_nonce_length(long evpAead); + // android-add: @FastNative static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes, byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad) throws ShortBufferException, BadPaddingException; + // android-add: @FastNative static native int EVP_AEAD_CTX_seal_buf(long evpAead, byte[] key, int tagLengthInBytes, ByteBuffer out, byte[] nonce, ByteBuffer input, byte[] ad) throws ShortBufferException, BadPaddingException; + // android-add: @FastNative static native int EVP_AEAD_CTX_open(long evpAead, byte[] key, int tagLengthInBytes, byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad) throws ShortBufferException, BadPaddingException; + // android-add: @FastNative static native int EVP_AEAD_CTX_open_buf(long evpAead, byte[] key, int tagLengthInBytes, ByteBuffer out, byte[] nonce, ByteBuffer input, byte[] ad) @@ -418,48 +527,66 @@ static native int EVP_AEAD_CTX_open_buf(long evpAead, byte[] key, int tagLengthI // --- CMAC functions ------------------------------------------------------ + // android-add: @FastNative static native long CMAC_CTX_new(); + // android-add: @FastNative static native void CMAC_CTX_free(long ctx); + // android-add: @FastNative static native void CMAC_Init(NativeRef.CMAC_CTX ctx, byte[] key); + // android-add: @FastNative static native void CMAC_Update(NativeRef.CMAC_CTX ctx, byte[] in, int inOffset, int inLength); + // android-add: @FastNative static native void CMAC_UpdateDirect(NativeRef.CMAC_CTX ctx, long inPtr, int inLength); + // android-add: @FastNative static native byte[] CMAC_Final(NativeRef.CMAC_CTX ctx); + // android-add: @FastNative static native void CMAC_Reset(NativeRef.CMAC_CTX ctx); // --- HMAC functions ------------------------------------------------------ + // android-add: @FastNative static native long HMAC_CTX_new(); + // android-add: @FastNative static native void HMAC_CTX_free(long ctx); + // android-add: @FastNative static native void HMAC_Init_ex(NativeRef.HMAC_CTX ctx, byte[] key, long evp_md); + // android-add: @FastNative static native void HMAC_Update(NativeRef.HMAC_CTX ctx, byte[] in, int inOffset, int inLength); + // android-add: @FastNative static native void HMAC_UpdateDirect(NativeRef.HMAC_CTX ctx, long inPtr, int inLength); + // android-add: @FastNative static native byte[] HMAC_Final(NativeRef.HMAC_CTX ctx); + // android-add: @FastNative static native void HMAC_Reset(NativeRef.HMAC_CTX ctx); // --- HPKE functions ------------------------------------------------------ + // android-add: @FastNative static native byte[] EVP_HPKE_CTX_export(NativeRef.EVP_HPKE_CTX ctx, byte[] exporterCtx, int length); static native void EVP_HPKE_CTX_free(long ctx); + // android-add: @FastNative static native byte[] EVP_HPKE_CTX_open(NativeRef.EVP_HPKE_CTX ctx, byte[] ciphertext, byte[] aad) throws BadPaddingException; + // android-add: @FastNative static native byte[] EVP_HPKE_CTX_seal(NativeRef.EVP_HPKE_CTX ctx, byte[] plaintext, byte[] aad); + // android-add: @FastNative static native Object EVP_HPKE_CTX_setup_base_mode_recipient(int kem, int kdf, int aead, byte[] privateKey, byte[] enc, byte[] info); @@ -471,6 +598,7 @@ static Object EVP_HPKE_CTX_setup_base_mode_recipient(HpkeSuite suite, byte[] pri enc, info); } + // android-add: @FastNative static native Object[] EVP_HPKE_CTX_setup_base_mode_sender(int kem, int kdf, int aead, byte[] publicKey, byte[] info); @@ -479,6 +607,8 @@ static Object[] EVP_HPKE_CTX_setup_base_mode_sender(HpkeSuite suite, byte[] publ return EVP_HPKE_CTX_setup_base_mode_sender(suite.getKem().getId(), suite.getKdf().getId(), suite.getAead().getId(), publicKey, info); } + + // android-add: @FastNative static native Object[] EVP_HPKE_CTX_setup_base_mode_sender_with_seed_for_testing( int kem, int kdf, int aead, byte[] publicKey, byte[] info, byte[] seed); @@ -493,6 +623,7 @@ static Object[] EVP_HPKE_CTX_setup_base_mode_sender_with_seed_for_testing(HpkeSu // --- RAND ---------------------------------------------------------------- + // android-add: @FastNative static native void RAND_bytes(byte[] output); // --- X509_NAME ----------------------------------------------------------- @@ -504,6 +635,7 @@ static int X509_NAME_hash(X500Principal principal) { public static int X509_NAME_hash_old(X500Principal principal) { return X509_NAME_hash(principal, "MD5"); } + private static int X509_NAME_hash(X500Principal principal, String algorithm) { try { byte[] digest = MessageDigest.getInstance(algorithm).digest(principal.getEncoded()); @@ -520,102 +652,128 @@ private static int X509_NAME_hash(X500Principal principal, String algorithm) { /** Used to request get_X509_GENERAL_NAME_stack get the "altname" field. */ static final int GN_STACK_SUBJECT_ALT_NAME = 1; - /** - * Used to request get_X509_GENERAL_NAME_stack get the issuerAlternativeName - * extension. - */ + /** Used to request get_X509_GENERAL_NAME_stack get the issuerAlternativeName extension. */ static final int GN_STACK_ISSUER_ALT_NAME = 2; - /** - * Used to request only non-critical types in get_X509*_ext_oids. - */ + /** Used to request only non-critical types in get_X509*_ext_oids. */ static final int EXTENSION_TYPE_NON_CRITICAL = 0; - /** - * Used to request only critical types in get_X509*_ext_oids. - */ + /** Used to request only critical types in get_X509*_ext_oids. */ static final int EXTENSION_TYPE_CRITICAL = 1; + // android-add: @FastNative static native long d2i_X509_bio(long bioCtx); + // android-add: @FastNative static native long d2i_X509(byte[] encoded) throws ParsingException; + // android-add: @FastNative static native long PEM_read_bio_X509(long bioCtx); + // android-add: @FastNative static native byte[] i2d_X509(long x509ctx, OpenSSLX509Certificate holder); /** Takes an X509 context not an X509_PUBKEY context. */ + // android-add: @FastNative static native byte[] i2d_X509_PUBKEY(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native byte[] ASN1_seq_pack_X509(long[] x509CertRefs); + // android-add: @FastNative static native long[] ASN1_seq_unpack_X509_bio(long bioRef) throws ParsingException; static native void X509_free(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native int X509_cmp(long x509ctx1, OpenSSLX509Certificate holder, long x509ctx2, OpenSSLX509Certificate holder2); + // android-add: @FastNative static native void X509_print_ex(long bioCtx, long x509ctx, OpenSSLX509Certificate holder, long nmflag, long certflag); + // android-add: @FastNative static native byte[] X509_get_issuer_name(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native byte[] X509_get_subject_name(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native String get_X509_sig_alg_oid(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native byte[] get_X509_sig_alg_parameter(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native boolean[] get_X509_issuerUID(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native boolean[] get_X509_subjectUID(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native long X509_get_pubkey(long x509ctx, OpenSSLX509Certificate holder) throws NoSuchAlgorithmException, InvalidKeyException; + // android-add: @FastNative static native String get_X509_pubkey_oid(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native byte[] X509_get_ext_oid(long x509ctx, OpenSSLX509Certificate holder, String oid); + // android-add: @FastNative static native String[] get_X509_ext_oids(long x509ctx, OpenSSLX509Certificate holder, int critical); + // android-add: @FastNative static native Object[][] get_X509_GENERAL_NAME_stack(long x509ctx, OpenSSLX509Certificate holder, int type) throws CertificateParsingException; + // android-add: @FastNative static native boolean[] get_X509_ex_kusage(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native String[] get_X509_ex_xkusage(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native int get_X509_ex_pathlen(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native long X509_get_notBefore(long x509ctx, OpenSSLX509Certificate holder) throws ParsingException; + // android-add: @FastNative static native long X509_get_notAfter(long x509ctx, OpenSSLX509Certificate holder) throws ParsingException; + // android-add: @FastNative static native long X509_get_version(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native byte[] X509_get_serialNumber(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native void X509_verify(long x509ctx, OpenSSLX509Certificate holder, NativeRef.EVP_PKEY pkeyCtx) throws BadPaddingException, IllegalBlockSizeException; + // android-add: @FastNative static native byte[] get_X509_tbs_cert(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native byte[] get_X509_tbs_cert_without_ext(long x509ctx, OpenSSLX509Certificate holder, String oid); + // android-add: @FastNative static native byte[] get_X509_signature(long x509ctx, OpenSSLX509Certificate holder); + // android-add: @FastNative static native int get_X509_ex_flags(long x509ctx, OpenSSLX509Certificate holder); // Used by Android platform TrustedCertificateStore. @SuppressWarnings("unused") + // android-add: @FastNative static native int X509_check_issued(long ctx, OpenSSLX509Certificate holder, long ctx2, OpenSSLX509Certificate holder2); @@ -628,104 +786,138 @@ static native int X509_check_issued(long ctx, OpenSSLX509Certificate holder, lon static final int PKCS7_CRLS = 2; /** Returns an array of X509 or X509_CRL pointers. */ + // android-add: @FastNative static native long[] d2i_PKCS7_bio(long bioCtx, int which) throws ParsingException; /** Returns an array of X509 or X509_CRL pointers. */ + // android-add: @FastNative static native byte[] i2d_PKCS7(long[] certs); /** Returns an array of X509 or X509_CRL pointers. */ + // android-add: @FastNative static native long[] PEM_read_bio_PKCS7(long bioCtx, int which); // --- X509_CRL ------------------------------------------------------------ + // android-add: @FastNative static native long d2i_X509_CRL_bio(long bioCtx); + // android-add: @FastNative static native long PEM_read_bio_X509_CRL(long bioCtx); + // android-add: @FastNative static native byte[] i2d_X509_CRL(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native void X509_CRL_free(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native void X509_CRL_print(long bioCtx, long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native String get_X509_CRL_sig_alg_oid(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native byte[] get_X509_CRL_sig_alg_parameter(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native byte[] X509_CRL_get_issuer_name(long x509CrlCtx, OpenSSLX509CRL holder); /** Returns X509_REVOKED reference that is not duplicated! */ + // android-add: @FastNative static native long X509_CRL_get0_by_cert(long x509CrlCtx, OpenSSLX509CRL holder, long x509Ctx, OpenSSLX509Certificate holder2); /** Returns X509_REVOKED reference that is not duplicated! */ + // android-add: @FastNative static native long X509_CRL_get0_by_serial(long x509CrlCtx, OpenSSLX509CRL holder, byte[] serial); /** Returns an array of X509_REVOKED that are owned by the caller. */ + // android-add: @FastNative static native long[] X509_CRL_get_REVOKED(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native String[] get_X509_CRL_ext_oids(long x509Crlctx, OpenSSLX509CRL holder, int critical); + // android-add: @FastNative static native byte[] X509_CRL_get_ext_oid(long x509CrlCtx, OpenSSLX509CRL holder, String oid); + // android-add: @FastNative static native long X509_CRL_get_version(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native long X509_CRL_get_ext(long x509CrlCtx, OpenSSLX509CRL holder, String oid); + // android-add: @FastNative static native byte[] get_X509_CRL_signature(long x509ctx, OpenSSLX509CRL holder); + // android-add: @FastNative static native void X509_CRL_verify(long x509CrlCtx, OpenSSLX509CRL holder, NativeRef.EVP_PKEY pkeyCtx) throws BadPaddingException, SignatureException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException; + // android-add: @FastNative static native byte[] get_X509_CRL_crl_enc(long x509CrlCtx, OpenSSLX509CRL holder); + // android-add: @FastNative static native long X509_CRL_get_lastUpdate(long x509CrlCtx, OpenSSLX509CRL holder) throws ParsingException; + // android-add: @FastNative static native long X509_CRL_get_nextUpdate(long x509CrlCtx, OpenSSLX509CRL holder) throws ParsingException; // --- X509_REVOKED -------------------------------------------------------- + // android-add: @FastNative static native long X509_REVOKED_dup(long x509RevokedCtx); + // android-add: @FastNative static native byte[] i2d_X509_REVOKED(long x509RevokedCtx, OpenSSLX509CRLEntry holder); + // android-add: @FastNative static native String[] get_X509_REVOKED_ext_oids(long x509ctx, int critical, OpenSSLX509CRLEntry holder); + // android-add: @FastNative static native byte[] X509_REVOKED_get_ext_oid(long x509RevokedCtx, String oid, OpenSSLX509CRLEntry holder); + // android-add: @FastNative static native byte[] X509_REVOKED_get_serialNumber(long x509RevokedCtx, OpenSSLX509CRLEntry holder); + // android-add: @FastNative static native long X509_REVOKED_get_ext(long x509RevokedCtx, String oid, OpenSSLX509CRLEntry holder); /** Returns ASN1_TIME reference. */ + // android-add: @FastNative static native long get_X509_REVOKED_revocationDate(long x509RevokedCtx, OpenSSLX509CRLEntry holder); + // android-add: @FastNative static native void X509_REVOKED_print(long bioRef, long x509RevokedCtx, OpenSSLX509CRLEntry holder); + // android-add: @FastNative static native void X509_REVOKED_free(long x509RevokedCtx, OpenSSLX509CRLEntry holder); // --- X509_EXTENSION ------------------------------------------------------ + // android-add: @FastNative static native int X509_supported_extension(long x509ExtensionRef); // --- SPAKE --------------------------------------------------------------- /** - * Sets the SPAKE credential for the given SSL context using a password. - * Used for both client and server. + * Sets the SPAKE credential for the given SSL context using a password. Used for both client + * and server. */ + // android-add: @FastNative static native void SSL_CTX_set_spake_credential(byte[] context, byte[] pw_array, byte[] id_prover_array, byte[] id_verifier_array, boolean is_client, @@ -735,6 +927,7 @@ static native void SSL_CTX_set_spake_credential(byte[] context, byte[] pw_array, // --- ASN1_TIME ----------------------------------------------------------- + // android-add: @FastNative static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal) throws ParsingException; @@ -742,144 +935,151 @@ static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal) /** * Allocates and returns an opaque reference to an object that can be used with other - * asn1_read_* functions to read the ASN.1-encoded data in val. The returned object must - * be freed after use by calling asn1_read_free. + * asn1_read_* functions to read the ASN.1-encoded data in val. The returned object must be + * freed after use by calling asn1_read_free. */ + // android-add: @FastNative static native long asn1_read_init(byte[] val) throws IOException; /** * Allocates and returns an opaque reference to an object that can be used with other - * asn1_read_* functions to read the ASN.1 sequence pointed to by cbsRef. The returned - * object must be freed after use by calling asn1_read_free. + * asn1_read_* functions to read the ASN.1 sequence pointed to by cbsRef. The returned object + * must be freed after use by calling asn1_read_free. */ + // android-add: @FastNative static native long asn1_read_sequence(long cbsRef) throws IOException; /** - * Returns whether the next object in the given reference is explicitly tagged with the - * given tag number. + * Returns whether the next object in the given reference is explicitly tagged with the given + * tag number. */ + // android-add: @FastNative static native boolean asn1_read_next_tag_is(long cbsRef, int tag) throws IOException; /** - * Allocates and returns an opaque reference to an object that can be used with - * other asn1_read_* functions to read the ASN.1 data pointed to by cbsRef. The returned - * object must be freed after use by calling asn1_read_free. + * Allocates and returns an opaque reference to an object that can be used with other + * asn1_read_* functions to read the ASN.1 data pointed to by cbsRef. The returned object must + * be freed after use by calling asn1_read_free. */ + // android-add: @FastNative static native long asn1_read_tagged(long cbsRef) throws IOException; - /** - * Returns the contents of an ASN.1 octet string from the given reference. - */ + /** Returns the contents of an ASN.1 octet string from the given reference. */ + // android-add: @FastNative static native byte[] asn1_read_octetstring(long cbsRef) throws IOException; /** - * Returns an ASN.1 integer from the given reference. If the integer doesn't fit - * in a uint64, this method will throw an IOException. + * Returns an ASN.1 integer from the given reference. If the integer doesn't fit in a uint64, + * this method will throw an IOException. */ + // android-add: @FastNative static native long asn1_read_uint64(long cbsRef) throws IOException; - /** - * Consumes an ASN.1 NULL from the given reference. - */ + /** Consumes an ASN.1 NULL from the given reference. */ + // android-add: @FastNative static native void asn1_read_null(long cbsRef) throws IOException; /** * Returns an ASN.1 OID in dotted-decimal notation (eg, "1.3.14.3.2.26" for SHA-1) from the * given reference. */ + // android-add: @FastNative static native String asn1_read_oid(long cbsRef) throws IOException; - /** - * Returns whether or not the given reference has been read completely. - */ + /** Returns whether or not the given reference has been read completely. */ + // android-add: @FastNative static native boolean asn1_read_is_empty(long cbsRef); /** - * Frees any resources associated with the given reference. After calling, the reference - * must not be used again. This may be called with a zero reference, in which case nothing - * will be done. + * Frees any resources associated with the given reference. After calling, the reference must + * not be used again. This may be called with a zero reference, in which case nothing will be + * done. */ + // android-add: @FastNative static native void asn1_read_free(long cbsRef); /** * Allocates and returns an opaque reference to an object that can be used with other - * asn1_write_* functions to write ASN.1-encoded data. The returned object must be finalized - * after use by calling either asn1_write_finish or asn1_write_cleanup, and its resources - * must be freed by calling asn1_write_free. + * asn1_write_* functions to write ASN.1-encoded data. The returned object must be finalized + * after use by calling either asn1_write_finish or asn1_write_cleanup, and its resources must + * be freed by calling asn1_write_free. */ + // android-add: @FastNative static native long asn1_write_init() throws IOException; /** * Allocates and returns an opaque reference to an object that can be used with other - * asn1_write_* functions to write an ASN.1 sequence into the given reference. The returned - * reference may only be used until the next call on the parent reference. The returned - * object must be freed after use by calling asn1_write_free. + * asn1_write_* functions to write an ASN.1 sequence into the given reference. The returned + * reference may only be used until the next call on the parent reference. The returned object + * must be freed after use by calling asn1_write_free. */ + // android-add: @FastNative static native long asn1_write_sequence(long cbbRef) throws IOException; /** * Allocates and returns an opaque reference to an object that can be used with other - * asn1_write_* functions to write a explicitly-tagged ASN.1 object with the given tag - * into the given reference. The returned reference may only be used until the next - * call on the parent reference. The returned object must be freed after use by - * calling asn1_write_free. + * asn1_write_* functions to write a explicitly-tagged ASN.1 object with the given tag into the + * given reference. The returned reference may only be used until the next call on the parent + * reference. The returned object must be freed after use by calling asn1_write_free. */ + // android-add: @FastNative static native long asn1_write_tag(long cbbRef, int tag) throws IOException; - /** - * Writes the given data into the given reference as an ASN.1-encoded octet string. - */ + /** Writes the given data into the given reference as an ASN.1-encoded octet string. */ + // android-add: @FastNative static native void asn1_write_octetstring(long cbbRef, byte[] data) throws IOException; - /** - * Writes the given value into the given reference as an ASN.1-encoded integer. - */ + /** Writes the given value into the given reference as an ASN.1-encoded integer. */ + // android-add: @FastNative static native void asn1_write_uint64(long cbbRef, long value) throws IOException; - /** - * Writes a NULL value into the given reference. - */ + /** Writes a NULL value into the given reference. */ + // android-add: @FastNative static native void asn1_write_null(long cbbRef) throws IOException; - /** - * Writes the given OID (which must be in dotted-decimal notation) into the given reference. - */ + /** Writes the given OID (which must be in dotted-decimal notation) into the given reference. */ + // android-add: @FastNative static native void asn1_write_oid(long cbbRef, String oid) throws IOException; /** * Flushes the given reference, invalidating any child references and completing their - * operations. This must be called if the child references are to be freed before - * asn1_write_finish is called on the ultimate parent. The child references must still - * be freed. + * operations. This must be called if the child references are to be freed before + * asn1_write_finish is called on the ultimate parent. The child references must still be freed. */ + // android-add: @FastNative static native void asn1_write_flush(long cbbRef) throws IOException; /** - * Completes any in-progress operations and returns the ASN.1-encoded data. Either this - * or asn1_write_cleanup must be called on any reference returned from asn1_write_init - * before it is freed. + * Completes any in-progress operations and returns the ASN.1-encoded data. Either this or + * asn1_write_cleanup must be called on any reference returned from asn1_write_init before it is + * freed. */ + // android-add: @FastNative static native byte[] asn1_write_finish(long cbbRef) throws IOException; /** - * Cleans up intermediate state in the given reference. Either this or asn1_write_finish - * must be called on any reference returned from asn1_write_init before it is freed. + * Cleans up intermediate state in the given reference. Either this or asn1_write_finish must be + * called on any reference returned from asn1_write_init before it is freed. */ + // android-add: @FastNative static native void asn1_write_cleanup(long cbbRef); /** - * Frees resources associated with the given reference. After calling, the reference - * must not be used again. This may be called with a zero reference, in which case nothing - * will be done. + * Frees resources associated with the given reference. After calling, the reference must not be + * used again. This may be called with a zero reference, in which case nothing will be done. */ + // android-add: @FastNative static native void asn1_write_free(long cbbRef); // --- BIO stream creation ------------------------------------------------- + // android-add: @FastNative static native long create_BIO_InputStream(OpenSSLBIOInputStream is, boolean isFinite); + // android-add: @FastNative static native long create_BIO_OutputStream(OutputStream os); + // android-add: @FastNative static native void BIO_free_all(long bioRef); // --- SSL handling -------------------------------------------------------- @@ -909,23 +1109,18 @@ static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal) new HashSet(Arrays.asList(SUPPORTED_TLS_1_3_CIPHER_SUITES)); /** - * TLS_EMPTY_RENEGOTIATION_INFO_SCSV is RFC 5746's renegotiation - * indication signaling cipher suite value. It is not a real - * cipher suite. It is just an indication in the default and - * supported cipher suite lists indicates that the implementation - * supports secure renegotiation. - *

- * In the RI, its presence means that the SCSV is sent in the - * cipher suite list to indicate secure renegotiation support and - * its absense means to send an empty TLS renegotiation info + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV is RFC 5746's renegotiation indication signaling cipher + * suite value. It is not a real cipher suite. It is just an indication in the default and + * supported cipher suite lists indicates that the implementation supports secure renegotiation. + * + *

In the RI, its presence means that the SCSV is sent in the cipher suite list to indicate + * secure renegotiation support and its absense means to send an empty TLS renegotiation info * extension instead. - *

- * However, OpenSSL doesn't provide an API to give this level of - * control, instead always sending the SCSV and always including - * the empty renegotiation info if TLS is used (as opposed to - * SSL). So we simply allow TLS_EMPTY_RENEGOTIATION_INFO_SCSV to - * be passed for compatibility as to provide the hint that we - * support secure renegotiation. + * + *

However, OpenSSL doesn't provide an API to give this level of control, instead always + * sending the SCSV and always including the empty renegotiation info if TLS is used (as opposed + * to SSL). So we simply allow TLS_EMPTY_RENEGOTIATION_INFO_SCSV to be passed for compatibility + * as to provide the hint that we support secure renegotiation. */ static final String TLS_EMPTY_RENEGOTIATION_INFO_SCSV = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; @@ -945,15 +1140,14 @@ static String cipherSuiteFromJava(String javaCipherSuite) { } /** - * TLS_FALLBACK_SCSV is from - * https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 - * to indicate to the server that this is a fallback protocol - * request. + * TLS_FALLBACK_SCSV is from https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 to + * indicate to the server that this is a fallback protocol request. */ private static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV"; private static final boolean HAS_AES_HARDWARE; private static final String[] SUPPORTED_TLS_1_2_CIPHER_SUITES; + static { if (loadError == null) { // If loadError is not null, it means the native code was not loaded, so @@ -987,11 +1181,13 @@ static String cipherSuiteFromJava(String javaCipherSuite) { } /** - * Returns 1 if the BoringSSL believes the CPU has AES accelerated hardware - * instructions. Used to determine cipher suite ordering. + * Returns 1 if the BoringSSL believes the CPU has AES accelerated hardware instructions. Used + * to determine cipher suite ordering. */ + // android-add: @FastNative static native int EVP_has_aes_hardware(); + // android-add: @FastNative static native long SSL_CTX_new(); // IMPLEMENTATION NOTE: The default list of cipher suites is a trade-off between what we'd like @@ -1013,39 +1209,40 @@ static String cipherSuiteFromJava(String javaCipherSuite) { // prevent apps from connecting to servers they were previously able to connect to. /** X.509 based cipher suites enabled by default (if requested), in preference order. */ - static final String[] DEFAULT_X509_CIPHER_SUITES = HAS_AES_HARDWARE ? - new String[] { - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_256_CBC_SHA", - } : - new String[] { - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_256_CBC_SHA", - }; + static final String[] DEFAULT_X509_CIPHER_SUITES = + HAS_AES_HARDWARE + ? new String[] { + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + } + : new String[] { + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + }; /** TLS-PSK cipher suites enabled by default (if requested), in preference order. */ static final String[] DEFAULT_PSK_CIPHER_SUITES = new String[] { @@ -1066,21 +1263,28 @@ static String[] getSupportedCipherSuites() { SUPPORTED_TLS_1_2_CIPHER_SUITES.clone()); } + // android-add: @FastNative static native void SSL_CTX_free(long ssl_ctx, AbstractSessionContext holder); + // android-add: @FastNative static native void SSL_CTX_set_session_id_context(long ssl_ctx, AbstractSessionContext holder, byte[] sid_ctx); + // android-add: @FastNative static native long SSL_CTX_set_timeout(long ssl_ctx, AbstractSessionContext holder, long seconds); + // android-add: @FastNative static native long SSL_new(long ssl_ctx, AbstractSessionContext holder) throws SSLException; + // android-add: @FastNative static native void SSL_enable_tls_channel_id(long ssl, NativeSsl ssl_holder) throws SSLException; + // android-add: @FastNative static native byte[] SSL_get_tls_channel_id(long ssl, NativeSsl ssl_holder) throws SSLException; + // android-add: @FastNative static native void SSL_set1_tls_channel_id(long ssl, NativeSsl ssl_holder, NativeRef.EVP_PKEY pkey); @@ -1092,48 +1296,65 @@ static native void SSL_set1_tls_channel_id(long ssl, NativeSsl ssl_holder, * @param pkey a reference to the private key. * @throws SSLException if a problem occurs setting the cert/key. */ + // android-add: @FastNative static native void setLocalCertsAndPrivateKey(long ssl, NativeSsl ssl_holder, byte[][] encodedCertificates, NativeRef.EVP_PKEY pkey) throws SSLException; + // android-add: @FastNative static native void SSL_set_client_CA_list(long ssl, NativeSsl ssl_holder, byte[][] asn1DerEncodedX500Principals) throws SSLException; + // android-add: @FastNative static native long SSL_set_mode(long ssl, NativeSsl ssl_holder, long mode); + // android-add: @FastNative static native long SSL_set_options(long ssl, NativeSsl ssl_holder, long options); + // android-add: @FastNative static native long SSL_clear_options(long ssl, NativeSsl ssl_holder, long options); + // android-add: @FastNative static native int SSL_set_protocol_versions(long ssl, NativeSsl ssl_holder, int min_version, int max_version); + // android-add: @FastNative static native void SSL_enable_signed_cert_timestamps(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native byte[] SSL_get_signed_cert_timestamp_list(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native void SSL_set_signed_cert_timestamp_list(long ssl, NativeSsl ssl_holder, byte[] list); + // android-add: @FastNative static native void SSL_enable_ocsp_stapling(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native byte[] SSL_get_ocsp_response(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native void SSL_set_ocsp_response(long ssl, NativeSsl ssl_holder, byte[] response); + // android-add: @FastNative static native byte[] SSL_get_tls_unique(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native byte[] SSL_export_keying_material(long ssl, NativeSsl ssl_holder, byte[] label, byte[] context, int num_bytes) throws SSLException; + // android-add: @FastNative static native void SSL_use_psk_identity_hint(long ssl, NativeSsl ssl_holder, String identityHint) throws SSLException; + // android-add: @FastNative static native void set_SSL_psk_client_callback_enabled(long ssl, NativeSsl ssl_holder, boolean enabled); + // android-add: @FastNative static native void set_SSL_psk_server_callback_enabled(long ssl, NativeSsl ssl_holder, boolean enabled); @@ -1205,6 +1426,7 @@ static String[] getSupportedProtocols() { private static class Range { public final String min; public final String max; + public Range(String min, String max) { this.min = min; this.max = max; @@ -1273,6 +1495,7 @@ static String[] checkEnabledProtocols(String[] protocols) { return protocols; } + // android-add: @FastNative static native void SSL_set_cipher_lists(long ssl, NativeSsl ssl_holder, String[] ciphers); /** @@ -1280,6 +1503,7 @@ static String[] checkEnabledProtocols(String[] protocols) { * * @return array of {@code SSL_CIPHER} references. */ + // android-add: @FastNative static native long[] SSL_get_ciphers(long ssl, NativeSsl ssl_holder); static void setEnabledCipherSuites(long ssl, NativeSsl ssl_holder, String[] cipherSuites, @@ -1341,101 +1565,130 @@ static String[] checkEnabledCipherSuites(String[] cipherSuites) { return cipherSuites; } + // android-add: @FastNative static native void SSL_set_accept_state(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native void SSL_set_connect_state(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native void SSL_set_verify(long ssl, NativeSsl ssl_holder, int mode); + // android-add: @FastNative static native void SSL_set_session(long ssl, NativeSsl ssl_holder, long sslSessionNativePointer) throws SSLException; + // android-add: @FastNative static native void SSL_set_session_creation_enabled(long ssl, NativeSsl ssl_holder, boolean creationEnabled) throws SSLException; + // android-add: @FastNative static native boolean SSL_session_reused(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native void SSL_accept_renegotiations(long ssl, NativeSsl ssl_holder) throws SSLException; + // android-add: @FastNative static native void SSL_set_tlsext_host_name(long ssl, NativeSsl ssl_holder, String hostname) throws SSLException; + + // android-add: @FastNative static native String SSL_get_servername(long ssl, NativeSsl ssl_holder); static native void SSL_do_handshake(long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc, int timeoutMillis) throws SSLException, SocketTimeoutException, CertificateException; + // android-add: @FastNative public static native String SSL_get_current_cipher(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative + public static native String SSL_get_version(long ssl, NativeSsl ssl_holder); + public static native void SSL_set1_groups(long ssl, NativeSsl sslHolder, int[] groups); public static native String SSL_get_curve_name(long ssl, NativeSsl sslHolder); - public static native String SSL_get_version(long ssl, NativeSsl ssl_holder); - - /** - * Returns the peer certificate chain. - */ + /** Returns the peer certificate chain. */ + // android-add: @FastNative static native byte[][] SSL_get0_peer_certificates(long ssl, NativeSsl ssl_holder); /** * Reads with the native SSL_read function from the encrypted data stream + * * @return -1 if error or the end of the stream is reached. */ static native int SSL_read(long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc, byte[] b, int off, int len, int readTimeoutMillis) throws IOException; - /** - * Writes with the native SSL_write function to the encrypted data stream. - */ + /** Writes with the native SSL_write function to the encrypted data stream. */ static native void SSL_write(long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis) throws IOException; + // android-add: @FastNative static native void SSL_interrupt(long ssl, NativeSsl ssl_holder); + static native void SSL_shutdown(long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc) throws IOException; + // android-add: @FastNative static native int SSL_get_shutdown(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native void SSL_free(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native long SSL_get_time(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native long SSL_set_timeout(long ssl, NativeSsl ssl_holder, long millis); + // android-add: @FastNative static native long SSL_get_timeout(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native int SSL_get_signature_algorithm_key_type(int signatureAlg); + // android-add: @FastNative static native byte[] SSL_session_id(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native byte[] SSL_SESSION_session_id(long sslSessionNativePointer); + // android-add: @FastNative static native long SSL_SESSION_get_time(long sslSessionNativePointer); + // android-add: @FastNative static native long SSL_SESSION_get_timeout(long sslSessionNativePointer); + // android-add: @FastNative static native String SSL_SESSION_get_version(long sslSessionNativePointer); + // android-add: @FastNative static native String SSL_SESSION_cipher(long sslSessionNativePointer); + // android-add: @FastNative static native boolean SSL_SESSION_should_be_single_use(long sslSessionNativePointer); + // android-add: @FastNative static native void SSL_SESSION_up_ref(long sslSessionNativePointer); + // android-add: @FastNative static native void SSL_SESSION_free(long sslSessionNativePointer); + // android-add: @FastNative static native byte[] i2d_SSL_SESSION(long sslSessionNativePointer); + // android-add: @FastNative static native long d2i_SSL_SESSION(byte[] data) throws IOException; /** - * A collection of callbacks from the native OpenSSL code that are - * related to the SSL handshake initiated by SSL_do_handshake. + * A collection of callbacks from the native OpenSSL code that are related to the SSL handshake + * initiated by SSL_do_handshake. */ interface SSLHandshakeCallbacks { /** @@ -1443,7 +1696,6 @@ interface SSLHandshakeCallbacks { * * @param certificateChain chain of X.509 certificates in their encoded form * @param authMethod auth algorithm name - * * @throws CertificateException if the certificate is untrusted */ @SuppressWarnings("unused") @@ -1464,9 +1716,9 @@ void clientCertificateRequested(byte[] keyTypes, int[] signatureAlgs, throws CertificateEncodingException, SSLException; /** - * Called when acting as a server during ClientHello processing before a decision - * to resume a session is made. This allows the selection of the correct server - * certificate based on things like Server Name Indication (SNI). + * Called when acting as a server during ClientHello processing before a decision to resume + * a session is made. This allows the selection of the correct server certificate based on + * things like Server Name Indication (SNI). * * @throws IOException if there was an error during certificate selection. */ @@ -1477,13 +1729,12 @@ void clientCertificateRequested(byte[] keyTypes, int[] signatureAlgs, * exchange. * * @param identityHint PSK identity hint provided by the server or {@code null} if no hint - * provided. + * provided. * @param identity buffer to be populated with PSK identity (NULL-terminated modified UTF-8) - * by this method. This identity will be provided to the server. + * by this method. This identity will be provided to the server. * @param key buffer to be populated with key material by this method. - * * @return number of bytes this method stored in the {@code key} buffer or {@code 0} if an - * error occurred in which case the handshake will be aborted. + * error occurred in which case the handshake will be aborted. */ int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key); @@ -1491,33 +1742,30 @@ void clientCertificateRequested(byte[] keyTypes, int[] signatureAlgs, * Gets the key to be used in server mode for this connection in Pre-Shared Key (PSK) key * exchange. * - * @param identityHint PSK identity hint provided by this server to the client or - * {@code null} if no hint was provided. + * @param identityHint PSK identity hint provided by this server to the client or {@code + * null} if no hint was provided. * @param identity PSK identity provided by the client. * @param key buffer to be populated with key material by this method. - * * @return number of bytes this method stored in the {@code key} buffer or {@code 0} if an - * error occurred in which case the handshake will be aborted. + * error occurred in which case the handshake will be aborted. */ int serverPSKKeyRequested(String identityHint, String identity, byte[] key); - /** - * Called when SSL state changes. This could be handshake completion. - */ + /** Called when SSL state changes. This could be handshake completion. */ @SuppressWarnings("unused") void onSSLStateChange(int type, int val); /** - * Called when a new session has been established and may be added to the session cache. - * The callee is responsible for incrementing the reference count on the returned session. + * Called when a new session has been established and may be added to the session cache. The + * callee is responsible for incrementing the reference count on the returned session. */ @SuppressWarnings("unused") void onNewSessionEstablished(long sslSessionNativePtr); /** - * Called for servers where TLS < 1.3 (TLS 1.3 uses session tickets rather than - * application session caches). + * Called for servers where TLS < 1.3 (TLS 1.3 uses session tickets rather than application + * session caches). * - *

Looks up the session by ID in the application's session cache. If a valid session - * is returned, this callback is responsible for incrementing the reference count (and any + *

Looks up the session by ID in the application's session cache. If a valid session is + * returned, this callback is responsible for incrementing the reference count (and any * required synchronization). * * @param id the ID of the session to find. @@ -1527,7 +1775,7 @@ void clientCertificateRequested(byte[] keyTypes, int[] signatureAlgs, /** * Called when acting as a server, the socket has an {@link - * ApplicationProtocolSelectorAdapter} associated with it, and the application protocol + * ApplicationProtocolSelectorAdapter} associated with it, and the application protocol * needs to be selected. * * @param applicationProtocols list of application protocols in length-prefix format @@ -1536,10 +1784,13 @@ void clientCertificateRequested(byte[] keyTypes, int[] signatureAlgs, @SuppressWarnings("unused") int selectApplicationProtocol(byte[] applicationProtocols); } + // android-add: @FastNative static native String SSL_CIPHER_get_kx_name(long cipherAddress); + // android-add: @FastNative static native String[] get_cipher_names(String selection); + // android-add: @FastNative public static native byte[] get_ocsp_single_extension(byte[] ocspResponse, String oid, long x509Ref, OpenSSLX509Certificate holder, @@ -1547,59 +1798,67 @@ public static native byte[] get_ocsp_single_extension(byte[] ocspResponse, Strin OpenSSLX509Certificate holder2); /** - * Returns the starting address of the memory region referenced by the provided direct - * {@link Buffer} or {@code 0} if the provided buffer is not direct or if such access to direct - * buffers is not supported by the platform. + * Returns the starting address of the memory region referenced by the provided direct {@link + * Buffer} or {@code 0} if the provided buffer is not direct or if such access to direct buffers + * is not supported by the platform. * *

NOTE: This method ignores the buffer's current {@code position}. */ + // android-add: @FastNative static native long getDirectBufferAddress(Buffer buf); + // android-add: @FastNative static native long SSL_BIO_new(long ssl, NativeSsl ssl_holder) throws SSLException; + // android-add: @FastNative static native int SSL_get_error(long ssl, NativeSsl ssl_holder, int ret); + // android-add: @FastNative static native void SSL_clear_error(); + // android-add: @FastNative static native int SSL_pending_readable_bytes(long ssl, NativeSsl ssl_holder); + // android-add: @FastNative static native int SSL_pending_written_bytes_in_BIO(long bio); - /** - * Returns the maximum overhead, in bytes, of sealing a record with SSL. - */ + /** Returns the maximum overhead, in bytes, of sealing a record with SSL. */ + // android-add: @FastNative static native int SSL_max_seal_overhead(long ssl, NativeSsl ssl_holder); /** * Enables ALPN for this TLS endpoint and sets the list of supported ALPN protocols in * wire-format (length-prefixed 8-bit strings). */ + // android-add: @FastNative static native void setApplicationProtocols(long ssl, NativeSsl ssl_holder, boolean client, byte[] protocols) throws IOException; /** * Called for a server endpoint only. Enables ALPN and indicates that the {@link - * SSLHandshakeCallbacks#selectApplicationProtocol} will be called to select the - * correct protocol during a handshake. Calling this method overrides - * {@link #setApplicationProtocols(long, NativeSsl, boolean, byte[])}. + * SSLHandshakeCallbacks#selectApplicationProtocol} will be called to select the correct + * protocol during a handshake. Calling this method overrides {@link + * #setApplicationProtocols(long, NativeSsl, boolean, byte[])}. */ + // android-add: @FastNative static native void setHasApplicationProtocolSelector(long ssl, NativeSsl ssl_holder, boolean hasSelector) throws IOException; /** - * Returns the selected ALPN protocol. If the server did not select a - * protocol, {@code null} will be returned. + * Returns the selected ALPN protocol. If the server did not select a protocol, {@code null} + * will be returned. */ + // android-add: @FastNative static native byte[] getApplicationProtocol(long ssl, NativeSsl ssl_holder); /** * Variant of the {@link #SSL_do_handshake} used by {@link ConscryptEngine}. This differs - * slightly from the raw BoringSSL API in that it returns the SSL error code from the - * operation, rather than the return value from {@code SSL_do_handshake}. This is done in - * order to allow to properly handle SSL errors and propagate useful exceptions. + * slightly from the raw BoringSSL API in that it returns the SSL error code from the operation, + * rather than the return value from {@code SSL_do_handshake}. This is done in order to allow to + * properly handle SSL errors and propagate useful exceptions. * * @return Returns the SSL error code for the operation when the error was {@code - * SSL_ERROR_NONE}, {@code SSL_ERROR_WANT_READ}, or {@code SSL_ERROR_WANT_WRITE}. + * SSL_ERROR_NONE}, {@code SSL_ERROR_WANT_READ}, or {@code SSL_ERROR_WANT_WRITE}. * @throws IOException when the error code is anything except those returned by this method. */ static native int ENGINE_SSL_do_handshake(long ssl, NativeSsl ssl_holder, @@ -1609,14 +1868,13 @@ static native int ENGINE_SSL_do_handshake(long ssl, NativeSsl ssl_holder, * Variant of the {@link #SSL_read} for a direct {@link java.nio.ByteBuffer} used by {@link * ConscryptEngine}. * - * @return if positive, represents the number of bytes read into the given buffer. - * Returns {@code -SSL_ERROR_WANT_READ} if more data is needed. Returns - * {@code -SSL_ERROR_WANT_WRITE} if data needs to be written out to flush the BIO. - * + * @return if positive, represents the number of bytes read into the given buffer. Returns + * {@code -SSL_ERROR_WANT_READ} if more data is needed. Returns {@code + * -SSL_ERROR_WANT_WRITE} if data needs to be written out to flush the BIO. * @throws java.io.InterruptedIOException if the read was interrupted. * @throws java.io.EOFException if the end of stream has been reached. * @throws CertificateException if the application's certificate verification callback failed. - * Only occurs during handshake processing. + * Only occurs during handshake processing. * @throws SSLException if any other error occurs. */ static native int ENGINE_SSL_read_direct(long ssl, NativeSsl ssl_holder, long address, @@ -1664,9 +1922,8 @@ static native void ENGINE_SSL_shutdown(long ssl, NativeSsl ssl_holder, static native byte[] Scrypt_generate_key(byte[] password, byte[] salt, int n, int r, int p, int key_len); - /** - * Return {@code true} if BoringSSL has been built in FIPS mode. - */ + /** Return {@code true} if BoringSSL has been built in FIPS mode. */ + // android-add: @FastNative static native boolean usesBoringSsl_FIPS_mode(); /* ECH */ @@ -1698,11 +1955,22 @@ static native boolean SSL_CTX_ech_enable_server(long sslCtx, AbstractSessionCont /** * Used for testing only. */ + // android-add: @FastNative static native int BIO_read(long bioRef, byte[] buffer) throws IOException; + + // android-add: @FastNative static native void BIO_write(long bioRef, byte[] buffer, int offset, int length) throws IOException, IndexOutOfBoundsException; + + // android-add: @FastNative static native long SSL_clear_mode(long ssl, NativeSsl ssl_holder, long mode); + + // android-add: @FastNative static native long SSL_get_mode(long ssl, NativeSsl ssl_holder); + + // android-add: @FastNative static native long SSL_get_options(long ssl, NativeSsl ssl_holder); + + // android-add: @FastNative static native long SSL_get1_session(long ssl, NativeSsl ssl_holder); } diff --git a/common/src/main/java/org/conscrypt/NativeSsl.java b/common/src/main/java/org/conscrypt/NativeSsl.java index 7fe7c49d7..02bc62496 100644 --- a/common/src/main/java/org/conscrypt/NativeSsl.java +++ b/common/src/main/java/org/conscrypt/NativeSsl.java @@ -363,6 +363,7 @@ void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOExcept if (parameters.isCTVerificationEnabled(hostname)) { NativeCrypto.SSL_enable_signed_cert_timestamps(ssl, this); } + enableEchBasedOnPolicy(hostname); } else { NativeCrypto.SSL_set_accept_state(ssl, this); @@ -584,6 +585,27 @@ private void setTlsChannelId(OpenSSLKey channelIdPrivateKey) throws SSLException } } + private void enableEchBasedOnPolicy(String hostname) throws SSLException { + EchOptions opts = parameters.getEchOptions(hostname); + if (opts == null) { + return; + } + + byte[] configList = opts.getConfigList(); + if (configList != null) { + try { + NativeCrypto.SSL_set1_ech_config_list(ssl, this, configList); + } catch (SSLException e) { + // The platform may provide a more specialized exception type for this error. + throw Platform.wrapInvalidEchDataException(e); + } + } + + if (opts.isGreaseEnabled()) { + NativeCrypto.SSL_set_enable_ech_grease(ssl, this, /* enable= */ true); + } + } + private void setCertificateValidation() throws SSLException { // setup peer certificate verification if (!isClient()) { diff --git a/common/src/main/java/org/conscrypt/NetworkSecurityPolicy.java b/common/src/main/java/org/conscrypt/NetworkSecurityPolicy.java new file mode 100644 index 000000000..499c5eda0 --- /dev/null +++ b/common/src/main/java/org/conscrypt/NetworkSecurityPolicy.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 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; + +import org.conscrypt.metrics.CertificateTransparencyVerificationReason; + +/** + * A policy provided by the platform to decide on the behaviour of TrustManagerImpl. + * + * See the platform-specific implementations in PlatformNetworkSecurityPolicy. + */ +@Internal +public interface NetworkSecurityPolicy { + boolean isCertificateTransparencyVerificationRequired(String hostname); + + CertificateTransparencyVerificationReason getCertificateTransparencyVerificationReason( + String hostname); + + DomainEncryptionMode getDomainEncryptionMode(String hostname); +} diff --git a/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java b/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java index a9368fd28..7ff4b3925 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java +++ b/common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java @@ -24,6 +24,7 @@ import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; +import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; @@ -33,6 +34,8 @@ @Internal public abstract class OpenSSLAeadCipher extends OpenSSLCipher { + private static final Logger logger = Logger.getLogger(OpenSSLAeadCipher.class.getName()); + /** * Controls whether no-copy optimizations for direct ByteBuffers are enabled. */ diff --git a/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java b/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java index 2b564e7ad..755d63cd8 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java +++ b/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java @@ -67,6 +67,10 @@ public static OpenSSLContextImpl getPreferred() { serverSessionContext = new ServerSessionContext(); } + private OpenSSLContextImpl() throws GeneralSecurityException, IOException { + this(NativeCrypto.TLSV13_PROTOCOLS, true); + } + /** * Constructor for the DefaultSSLContextImpl. The unused boolean parameter is solely to * indicate that this constructor is desired. diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java index 3e28cec21..327f77e3c 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java +++ b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java @@ -96,6 +96,8 @@ public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketE @Override public abstract void setUseSessionTickets(boolean useSessionTickets); + @Override public abstract void setEchConfigList(byte[] echConfigList); + @Override public abstract void setChannelIdEnabled(boolean enabled); @Override public abstract byte[] getChannelId() throws SSLException; diff --git a/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java index aa85e29bf..af7bb7767 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX25519PrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright (C) 2022 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. diff --git a/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java b/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java index 7ec4c87a6..ddeed1f51 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX25519PublicKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright (C) 2022 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. diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java b/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java index 22b13f87d..b778e69a8 100644 --- a/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java +++ b/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java @@ -31,7 +31,7 @@ * An implementation of {@link X509CRLEntry} based on BoringSSL. */ final class OpenSSLX509CRLEntry extends X509CRLEntry { - private final long mContext; + private long mContext; private final Date revocationDate; OpenSSLX509CRLEntry(long ctx) throws ParsingException { diff --git a/common/src/main/java/org/conscrypt/SSLParametersImpl.java b/common/src/main/java/org/conscrypt/SSLParametersImpl.java index e1bb9e4de..e85cf203e 100644 --- a/common/src/main/java/org/conscrypt/SSLParametersImpl.java +++ b/common/src/main/java/org/conscrypt/SSLParametersImpl.java @@ -17,6 +17,8 @@ package org.conscrypt; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.security.AlgorithmConstraints; import java.security.KeyManagementException; import java.security.KeyStore; @@ -29,11 +31,13 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.logging.Logger; import javax.crypto.SecretKey; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SSLException; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509KeyManager; @@ -49,6 +53,8 @@ * socket or not. */ final class SSLParametersImpl implements Cloneable { + private static final Logger logger = Logger.getLogger(SSLParametersImpl.class.getName()); + // default source of X.509 certificate based authentication keys private static volatile X509KeyManager defaultX509KeyManager; // default source of X.509 certificate based authentication trust decisions @@ -73,6 +79,8 @@ final class SSLParametersImpl implements Cloneable { private final Spake2PlusTrustManager spake2PlusTrustManager; // source of Spake authentication or null if not provided private final Spake2PlusKeyManager spake2PlusKeyManager; + // getNetworkSecurityPolicy reflected method for x509TrustManager + private final Method getNetworkSecurityPolicy; // protocols enabled for SSL connection String[] enabledProtocols; @@ -109,6 +117,7 @@ final class SSLParametersImpl implements Cloneable { byte[] applicationProtocols = EmptyArray.BYTE; ApplicationProtocolSelectorAdapter applicationProtocolSelector; boolean useSessionTickets; + byte[] echConfigList; private Boolean useSni; /** @@ -168,6 +177,8 @@ final class SSLParametersImpl implements Cloneable { "Spake2PlusTrustManager and Spake2PlusKeyManager should be set together"); } + getNetworkSecurityPolicy = getNetworkSecurityPolicyMethod(x509TrustManager); + // initialize the list of cipher suites and protocols enabled by default if (isSpake()) { enabledProtocols = new String[] {NativeCrypto.SUPPORTED_PROTOCOL_TLSV1_3}; @@ -213,6 +224,7 @@ private SSLParametersImpl(ClientSessionContext clientSessionContext, this.x509KeyManager = x509KeyManager; this.pskKeyManager = pskKeyManager; this.x509TrustManager = x509TrustManager; + this.getNetworkSecurityPolicy = getNetworkSecurityPolicyMethod(x509TrustManager); this.spake2PlusKeyManager = spake2PlusKeyManager; this.spake2PlusTrustManager = spake2PlusTrustManager; @@ -238,6 +250,8 @@ private SSLParametersImpl(ClientSessionContext clientSessionContext, : sslParams.applicationProtocols.clone(); this.applicationProtocolSelector = sslParams.applicationProtocolSelector; this.useSessionTickets = sslParams.useSessionTickets; + this.echConfigList = + (sslParams.echConfigList == null) ? null : sslParams.echConfigList.clone(); this.useSni = sslParams.useSni; this.channelIdEnabled = sslParams.channelIdEnabled; } @@ -253,6 +267,17 @@ void initSpake() throws KeyManagementException { } } + private Method getNetworkSecurityPolicyMethod(X509TrustManager tm) { + if (tm == null) { + return null; + } + try { + return tm.getClass().getMethod("getNetworkSecurityPolicy"); + } catch (NoSuchMethodException ignored) { + return null; + } + } + static SSLParametersImpl getDefault() throws KeyManagementException { SSLParametersImpl result = defaultParameters; if (result == null) { @@ -475,6 +500,10 @@ void setUseSessionTickets(boolean useSessionTickets) { this.useSessionTickets = useSessionTickets; } + void setEchConfigList(byte[] echConfigList) { + this.echConfigList = echConfigList; + } + /* * Whether connections using this SSL connection should use the TLS * extension Server Name Indication (SNI). @@ -813,6 +842,37 @@ private static String[] getDefaultCipherSuites(boolean x509CipherSuitesNeeded, } } + private NetworkSecurityPolicy getPolicy() { + // Google3-only: Skip getPolicy (b/477326565 b/450387911). + // + // If the TrustManager has a security policy attached, use it. We are using reflection here. + // The Android framework may provide a high-level TrustManager (e.g., RootTrustManager or + // NetworkSecurityTrustManager), which we need to query. + // if (getNetworkSecurityPolicy != null) { + // try { + // Object objPolicy = getNetworkSecurityPolicy.invoke(x509TrustManager); + // if (objPolicy instanceof NetworkSecurityPolicy) { + // return (NetworkSecurityPolicy) objPolicy; + // } + // } catch (IllegalAccessException | IllegalArgumentException e) { + // // This is the unlikely scenario where an external TrustManager is being used and + // it + // // defines a getNetworkSecurityPolicy method which does not match our + // expectations. logger.warning("Unable to call getNetworkSecurityPolicy on + // TrustManager: " + // + e.getMessage()); + // } catch (InvocationTargetException e) { + // // getNetworkSecurityPolicy raised an exception. Unwrap it. + // throw new RuntimeException( + // "Unable to retrieve the NetworkSecurityPolicy associated " + // + "with the TrustManager", + // e.getCause()); + // } + //} + // Otherwise, rely on the global platform policy. + return ConscryptNetworkSecurityPolicy.getDefault(); + } + /* * Checks whether SCT verification is enforced for a given hostname. */ @@ -825,7 +885,26 @@ boolean isCTVerificationEnabled(String hostname) { if (ctVerificationEnabled) { return true; } - return Platform.isCTVerificationRequired(hostname); + + return getPolicy().isCertificateTransparencyVerificationRequired(hostname); + } + + EchOptions getEchOptions(String hostname) throws SSLException { + switch (getPolicy().getDomainEncryptionMode(hostname)) { + case DISABLED: + return null; + case OPPORTUNISTIC: + return new EchOptions(echConfigList, /* enableGrease= */ false); + case ENABLED: + return new EchOptions(echConfigList, /* enableGrease= */ true); + case REQUIRED: + if (echConfigList == null) { + throw new SSLException("No ECH config provided when required"); + } + return new EchOptions(echConfigList, /* enableGrease= */ false); + default: + return null; + } } boolean isSpake() { diff --git a/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java b/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java index bf44d5531..87c5f0bb0 100644 --- a/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java +++ b/common/src/main/java/org/conscrypt/ScryptSecretKeyFactory.java @@ -16,9 +16,9 @@ package org.conscrypt; +import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; @@ -61,9 +61,13 @@ protected SecretKey engineGenerateSecret(KeySpec inKeySpec) throws InvalidKeySpe throw new InvalidKeySpecException("Cannot produce fractional-byte outputs"); } - return new ScryptKey(NativeCrypto.Scrypt_generate_key( - new String(password).getBytes(StandardCharsets.UTF_8), salt, n, r, p, - keyOutputBits / 8)); + try { + return new ScryptKey(NativeCrypto.Scrypt_generate_key( + new String(password).getBytes("UTF-8"), salt, n, r, p, keyOutputBits / 8)); + } catch (UnsupportedEncodingException e) { + // Impossible according to the Java docs: UTF-8 is always supported. + throw new IllegalStateException(e); + } } private Object getValue(KeySpec spec, String methodName) @@ -116,8 +120,6 @@ public byte[] getEncoded() { } private static class NotImplementedException extends RuntimeException { - private static final long serialVersionUID = -7755435858585859108L; - NotImplementedException() { super("Not implemented"); } diff --git a/common/src/main/java/org/conscrypt/TrustManagerImpl.java b/common/src/main/java/org/conscrypt/TrustManagerImpl.java index adb206e5b..352e0a75a 100644 --- a/common/src/main/java/org/conscrypt/TrustManagerImpl.java +++ b/common/src/main/java/org/conscrypt/TrustManagerImpl.java @@ -61,6 +61,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Supplier; import java.util.logging.Logger; import javax.net.ssl.HttpsURLConnection; @@ -72,14 +73,16 @@ /** * TrustManager implementation. The implementation is based on CertPathValidator - * PKIX and CertificateFactory X509 implementations. This implementations should + * PKIX and CertificateFactory X509 implementations. These implementations should * be provided by some certification provider. * * @see javax.net.ssl.X509ExtendedTrustManager + * @see org.conscrypt.ConscryptX509TrustManager */ @Internal @SuppressWarnings("CustomX509TrustManager") -public final class TrustManagerImpl extends X509ExtendedTrustManager { +public final class TrustManagerImpl + extends X509ExtendedTrustManager implements ConscryptX509TrustManager { private static final Logger logger = Logger.getLogger(TrustManagerImpl.class.getName()); /** @@ -102,6 +105,15 @@ public final class TrustManagerImpl extends X509ExtendedTrustManager { */ private final CertPinManager pinManager; + /** + * The ConscryptNetworkSecurityPolicy associated with this TrustManager. + * + * The policy is used to decide if various mechanisms should be enabled, + * mostly based on the process configuration and the hostname queried. The + * policy is aligned with Android's libcore NetworkSecurityPolicy. + */ + private ConscryptNetworkSecurityPolicy policy; + /** * The backing store for the AndroidCAStore if non-null. This will * be null when the rootKeyStore is null, implying we are not @@ -153,12 +165,6 @@ public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) { public TrustManagerImpl(KeyStore keyStore, CertPinManager manager, ConscryptCertStore certStore) { - this(keyStore, manager, certStore, null, null); - } - - private TrustManagerImpl(KeyStore keyStore, CertPinManager manager, - ConscryptCertStore certStore, CertBlocklist blocklist, - org.conscrypt.ct.CertificateTransparency ct) { CertPathValidator validatorLocal = null; CertificateFactory factoryLocal = null; KeyStore rootKeyStoreLocal = null; @@ -188,13 +194,6 @@ private TrustManagerImpl(KeyStore keyStore, CertPinManager manager, errLocal = e; } - if (ct == null) { - ct = Platform.newDefaultCertificateTransparency(); - } - if (blocklist == null) { - blocklist = Platform.newDefaultBlocklist(); - } - this.pinManager = manager; this.rootKeyStore = rootKeyStoreLocal; this.trustedCertificateStore = trustedCertificateStoreLocal; @@ -204,8 +203,25 @@ private TrustManagerImpl(KeyStore keyStore, CertPinManager manager, this.intermediateIndex = new TrustedCertificateIndex(); this.acceptedIssuers = acceptedIssuersLocal; this.err = errLocal; - this.blocklist = blocklist; - this.ct = ct; + this.policy = ConscryptNetworkSecurityPolicy.getDefault(); + this.blocklist = Platform.newDefaultBlocklist(); + this.ct = Platform.newDefaultCertificateTransparency(new Supplier() { + @Override + public NetworkSecurityPolicy get() { + return policy; + } + }); + } + + /** + * Attach a ConscryptNetworkSecurityPolicy to this TrustManager. + */ + public void setNetworkSecurityPolicy(ConscryptNetworkSecurityPolicy policy) { + this.policy = policy; + } + + public ConscryptNetworkSecurityPolicy getNetworkSecurityPolicy() { + return policy; } @SuppressWarnings("JdkObsolete") // KeyStore#aliases is the only API available @@ -298,12 +314,24 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) /** * For backward compatibility with older Android API that used String for the hostname only. */ + @Override public List checkServerTrusted(X509Certificate[] chain, String authType, String hostname) throws CertificateException { return checkTrusted(chain, null /* ocspData */, null /* tlsSctData */, authType, hostname, false); } + /** + * For compatibility with network stacks that cannot provide an SSLSession nor a + * Socket (e.g., Cronet). + */ + @Override + public List checkServerTrusted(X509Certificate[] chain, byte[] ocspData, + byte[] tlsSctData, String authType, + String hostname) throws CertificateException { + return checkTrusted(chain, ocspData, tlsSctData, authType, hostname, false); + } + /** * Returns the full trusted certificate chain found from {@code certs}. *

@@ -662,7 +690,8 @@ private List verifyChain(List untrustedChain, } // Check Certificate Transparency (if required). - if (!clientAuth && host != null && ct != null && ct.isCTVerificationRequired(host)) { + if (!clientAuth && host != null && ct != null + && policy.isCertificateTransparencyVerificationRequired(host)) { ct.checkCT(wholeChain, ocspData, tlsSctData, host); } diff --git a/common/src/main/java/org/conscrypt/XdhKeySpec.java b/common/src/main/java/org/conscrypt/XdhKeySpec.java index 7ac38c188..83d22d907 100644 --- a/common/src/main/java/org/conscrypt/XdhKeySpec.java +++ b/common/src/main/java/org/conscrypt/XdhKeySpec.java @@ -43,7 +43,7 @@ public boolean equals(Object o) { return false; EncodedKeySpec that = (EncodedKeySpec) o; return (getFormat().equals(that.getFormat()) - && Arrays.equals(getEncoded(), that.getEncoded())); + && (Arrays.equals(getEncoded(), that.getEncoded()))); } @Override diff --git a/common/src/main/java/org/conscrypt/ct/CertificateEntry.java b/common/src/main/java/org/conscrypt/ct/CertificateEntry.java index eae9fbadb..d28b03cd2 100644 --- a/common/src/main/java/org/conscrypt/ct/CertificateEntry.java +++ b/common/src/main/java/org/conscrypt/ct/CertificateEntry.java @@ -143,4 +143,17 @@ public void encode(OutputStream output) throws SerializationException { } Serialization.writeVariableBytes(output, certificate, Constants.CERTIFICATE_LENGTH_BYTES); } + + /** + * Expected size of the encoded CertificateEntry structure. + */ + public int encodedLength() { + int size = Constants.LOG_ENTRY_TYPE_LENGTH; + if (entryType == LogEntryType.PRECERT_ENTRY) { + size += issuerKeyHash.length; + } + size += Constants.CERTIFICATE_LENGTH_BYTES; + size += certificate.length; + return size; + } } diff --git a/common/src/main/java/org/conscrypt/ct/CertificateTransparency.java b/common/src/main/java/org/conscrypt/ct/CertificateTransparency.java index 1e13cdf6d..502f33f1b 100644 --- a/common/src/main/java/org/conscrypt/ct/CertificateTransparency.java +++ b/common/src/main/java/org/conscrypt/ct/CertificateTransparency.java @@ -17,6 +17,7 @@ package org.conscrypt.ct; import org.conscrypt.Internal; +import org.conscrypt.NetworkSecurityPolicy; import org.conscrypt.Platform; import org.conscrypt.metrics.CertificateTransparencyVerificationReason; import org.conscrypt.metrics.StatsLog; @@ -25,6 +26,7 @@ import java.security.cert.X509Certificate; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; /** * Certificate Transparency subsystem. The implementation contains references @@ -36,9 +38,11 @@ public class CertificateTransparency { private Verifier verifier; private Policy policy; private StatsLog statsLog; + private Supplier policySupplier; public CertificateTransparency(LogStore logStore, Policy policy, Verifier verifier, - StatsLog statsLog) { + StatsLog statsLog, + Supplier policySupplier) { Objects.requireNonNull(logStore); Objects.requireNonNull(policy); Objects.requireNonNull(verifier); @@ -48,14 +52,11 @@ public CertificateTransparency(LogStore logStore, Policy policy, Verifier verifi this.policy = policy; this.verifier = verifier; this.statsLog = statsLog; + this.policySupplier = policySupplier; } - public boolean isCTVerificationRequired(String host) { - return Platform.isCTVerificationRequired(host); - } - - public CertificateTransparencyVerificationReason reasonCTVerificationRequired(String host) { - return Platform.reasonCTVerificationRequired(host); + public CertificateTransparencyVerificationReason getVerificationReason(String host) { + return policySupplier.get().getCertificateTransparencyVerificationReason(host); } public void checkCT(List chain, byte[] ocspData, byte[] tlsData, String host) @@ -67,7 +68,7 @@ public void checkCT(List chain, byte[] ocspData, byte[] tlsData statsLog.reportCTVerificationResult(logStore, /* VerificationResult */ null, /* PolicyCompliance */ null, - reasonCTVerificationRequired(host)); + getVerificationReason(host)); return; } VerificationResult result = @@ -76,7 +77,7 @@ public void checkCT(List chain, byte[] ocspData, byte[] tlsData X509Certificate leaf = chain.get(0); PolicyCompliance compliance = policy.doesResultConformToPolicy(result, leaf); statsLog.reportCTVerificationResult(logStore, result, compliance, - reasonCTVerificationRequired(host)); + getVerificationReason(host)); if (compliance != PolicyCompliance.COMPLY) { throw new CertificateException( "Certificate chain does not conform to required transparency policy: " diff --git a/common/src/main/java/org/conscrypt/ct/LogInfo.java b/common/src/main/java/org/conscrypt/ct/LogInfo.java index 8c3b82638..6412f2fe7 100644 --- a/common/src/main/java/org/conscrypt/ct/LogInfo.java +++ b/common/src/main/java/org/conscrypt/ct/LogInfo.java @@ -29,7 +29,7 @@ /** * Properties about a Certificate Transparency Log. - * This object stores information about a CT log, its public key, description and URL. + * This object stores information about a CT log, its public key and URL. * It allows verification of SCTs against the log's public key. */ @Internal @@ -42,13 +42,16 @@ public class LogInfo { public static final int STATE_RETIRED = 5; public static final int STATE_REJECTED = 6; + public static final int TYPE_UNKNOWN = 0; + public static final int TYPE_RFC6962 = 1; + public static final int TYPE_STATIC_CT_API = 2; + private final byte[] logId; private final PublicKey publicKey; private final int state; private final long stateTimestamp; - private final String description; - private final String url; private final String operator; + private final int type; private LogInfo(Builder builder) { /* Based on the required fields for the log list schema v3. Notably, @@ -56,16 +59,14 @@ private LogInfo(Builder builder) { * is validated in the builder. */ Objects.requireNonNull(builder.logId); Objects.requireNonNull(builder.publicKey); - Objects.requireNonNull(builder.url); Objects.requireNonNull(builder.operator); this.logId = builder.logId; this.publicKey = builder.publicKey; this.state = builder.state; this.stateTimestamp = builder.stateTimestamp; - this.description = builder.description; - this.url = builder.url; this.operator = builder.operator; + this.type = builder.type; } public static class Builder { @@ -73,9 +74,8 @@ public static class Builder { private PublicKey publicKey; private int state; private long stateTimestamp; - private String description; - private String url; private String operator; + private int type; public Builder setPublicKey(PublicKey publicKey) { Objects.requireNonNull(publicKey); @@ -98,24 +98,20 @@ public Builder setState(int state, long timestamp) { return this; } - public Builder setDescription(String description) { - Objects.requireNonNull(description); - this.description = description; - return this; - } - - public Builder setUrl(String url) { - Objects.requireNonNull(url); - this.url = url; - return this; - } - public Builder setOperator(String operator) { Objects.requireNonNull(operator); this.operator = operator; return this; } + public Builder setType(int type) { + if (type < 0 || type > TYPE_STATIC_CT_API) { + throw new IllegalArgumentException("invalid type value"); + } + this.type = type; + return this; + } + public LogInfo build() { return new LogInfo(this); } @@ -132,14 +128,6 @@ public PublicKey getPublicKey() { return publicKey; } - public String getDescription() { - return description; - } - - public String getUrl() { - return url; - } - public int getState() { return state; } @@ -159,6 +147,10 @@ public String getOperator() { return operator; } + public int getType() { + return type; + } + @Override public boolean equals(Object other) { if (this == other) { @@ -169,16 +161,14 @@ public boolean equals(Object other) { } LogInfo that = (LogInfo) other; - return this.state == that.state && this.description.equals(that.description) - && this.url.equals(that.url) && this.operator.equals(that.operator) - && this.stateTimestamp == that.stateTimestamp + return this.state == that.state && this.operator.equals(that.operator) + && this.stateTimestamp == that.stateTimestamp && this.type == that.type && Arrays.equals(this.logId, that.logId); } @Override public int hashCode() { - return Objects.hash(Arrays.hashCode(logId), description, url, state, stateTimestamp, - operator); + return Objects.hash(Arrays.hashCode(logId), state, stateTimestamp, operator, type); } /** diff --git a/common/src/main/java/org/conscrypt/ct/PolicyCompliance.java b/common/src/main/java/org/conscrypt/ct/PolicyCompliance.java index 68cffbb6e..c84297937 100644 --- a/common/src/main/java/org/conscrypt/ct/PolicyCompliance.java +++ b/common/src/main/java/org/conscrypt/ct/PolicyCompliance.java @@ -18,4 +18,11 @@ import org.conscrypt.Internal; -@Internal public enum PolicyCompliance { COMPLY, NOT_ENOUGH_SCTS, NOT_ENOUGH_DIVERSE_SCTS } +@Internal +public enum PolicyCompliance { + COMPLY, + NOT_ENOUGH_SCTS, + + NOT_ENOUGH_DIVERSE_SCTS, + NO_RFC6962_LOG +} diff --git a/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java b/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java index f9a737f66..9e89f5ce9 100644 --- a/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java +++ b/common/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java @@ -135,11 +135,22 @@ public void encodeTBS(OutputStream output, CertificateEntry certEntry) Serialization.writeVariableBytes(output, extensions, Constants.EXTENSIONS_LENGTH_BYTES); } + private int encodedLength(CertificateEntry certEntry) { + int size = Constants.VERSION_LENGTH; + size += Constants.SIGNATURE_TYPE_LENGTH; + size += Constants.TIMESTAMP_LENGTH; + size += certEntry.encodedLength(); + size += Constants.EXTENSIONS_LENGTH_BYTES; + size += extensions.length; + return size; + } + /** * TLS encode the signed part of the SCT, as described by RFC6962 section 3.2. */ public byte[] encodeTBS(CertificateEntry certEntry) throws SerializationException { - ByteArrayOutputStream output = new ByteArrayOutputStream(); + int bufferSize = encodedLength(certEntry); + ByteArrayOutputStream output = new ByteArrayOutputStream(bufferSize); encodeTBS(output, certEntry); return output.toByteArray(); } diff --git a/common/src/main/java/org/conscrypt/metrics/CertificateTransparencyVerificationReason.java b/common/src/main/java/org/conscrypt/metrics/CertificateTransparencyVerificationReason.java index 0c7fab7aa..ef4a5b999 100644 --- a/common/src/main/java/org/conscrypt/metrics/CertificateTransparencyVerificationReason.java +++ b/common/src/main/java/org/conscrypt/metrics/CertificateTransparencyVerificationReason.java @@ -16,8 +16,10 @@ package org.conscrypt.metrics; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_DRY_RUN; import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_APP_OPT_IN; import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_DOMAIN_OPT_IN; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_SDK_TARGET_DEFAULT_ENABLED; import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_UNKNOWN; import org.conscrypt.Internal; @@ -30,7 +32,10 @@ public enum CertificateTransparencyVerificationReason { UNKNOWN(CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_UNKNOWN), APP_OPT_IN(CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_APP_OPT_IN), DOMAIN_OPT_IN( - CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_DOMAIN_OPT_IN); + CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_DOMAIN_OPT_IN), + SDK_TARGET_DEFAULT_ENABLED( + CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_SDK_TARGET_DEFAULT_ENABLED), + DRY_RUN(CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_DRY_RUN); final int id; diff --git a/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java index a94a4e760..489fd0d4e 100644 --- a/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java +++ b/common/src/main/java/org/conscrypt/metrics/ConscryptStatsLog.java @@ -66,6 +66,12 @@ public final class ConscryptStatsLog { */ public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED = 989; + /** + * CertificateBlocklistBlockReported certificate_blocklist_block_reported
+ * Usage: StatsLog.write(StatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED, int source, int index, int uid);
+ */ + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED = 1143; + // Constants for enum values. // Values for TlsHandshakeReported.protocol @@ -179,10 +185,22 @@ public final class ConscryptStatsLog { public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_SDK_TARGET_DEFAULT_ENABLED = 2; public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_APP_OPT_IN = 3; public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_NSCONFIG_DOMAIN_OPT_IN = 4; + public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__REASON__REASON_DRY_RUN = 5; + // Values for CertificateTransparencyVerificationReported.policy_compatibility_version public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__POLICY_COMPATIBILITY_VERSION__COMPAT_VERSION_UNKNOWN = 0; public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__POLICY_COMPATIBILITY_VERSION__COMPAT_VERSION_V1 = 1; + public static final int CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__POLICY_COMPATIBILITY_VERSION__COMPAT_VERSION_V2 = 2; + + // Values for CertificateBlocklistBlockReported.source + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_UNKNOWN = 0; + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_TEST = 1; + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_BUILT_IN = 2; + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_FILE = 3; + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_TEST = 4; + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_BUILT_IN = 5; + public static final int CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_FILE = 6; // Write methods public static void write(int code, boolean arg1, int arg2, int arg3, int arg4, int arg5, int[] arg6) { @@ -224,7 +242,18 @@ public static void write(int code, int arg1, int arg2, int arg3, int arg4, int a ReflexiveStatsLog.write(builder.build()); } - public static void write(int code, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8) { + public static void write(int code, int arg1, int arg2, int arg3) { + final ReflexiveStatsEvent.Builder builder = ReflexiveStatsEvent.newBuilder(); + builder.setAtomId(code); + builder.writeInt(arg1); + builder.writeInt(arg2); + builder.writeInt(arg3); + + builder.usePooledBuffer(); + ReflexiveStatsLog.write(builder.build()); + } + + public static void write(int code, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9) { final ReflexiveStatsEvent.Builder builder = ReflexiveStatsEvent.newBuilder(); builder.setAtomId(code); builder.writeInt(arg1); @@ -235,6 +264,7 @@ public static void write(int code, int arg1, int arg2, int arg3, int arg4, int a builder.writeInt(arg6); builder.writeInt(arg7); builder.writeInt(arg8); + builder.writeInt(arg9); builder.usePooledBuffer(); ReflexiveStatsLog.write(builder.build()); diff --git a/common/src/main/java/org/conscrypt/metrics/NoopStatsLog.java b/common/src/main/java/org/conscrypt/metrics/NoopStatsLog.java index 1144e3350..b4456fea5 100644 --- a/common/src/main/java/org/conscrypt/metrics/NoopStatsLog.java +++ b/common/src/main/java/org/conscrypt/metrics/NoopStatsLog.java @@ -15,6 +15,7 @@ */ package org.conscrypt.metrics; +import org.conscrypt.CertBlocklistEntry; import org.conscrypt.Internal; import org.conscrypt.ct.LogStore; import org.conscrypt.ct.PolicyCompliance; @@ -38,4 +39,6 @@ public void updateCTLogListStatusChanged(LogStore logStore) {} public void reportCTVerificationResult(LogStore logStore, VerificationResult result, PolicyCompliance compliance, CertificateTransparencyVerificationReason reason) {} + + public void reportBlocklistHit(CertBlocklistEntry entry) {} } diff --git a/common/src/main/java/org/conscrypt/metrics/StatsLog.java b/common/src/main/java/org/conscrypt/metrics/StatsLog.java index 0779946d7..f5711cc4c 100644 --- a/common/src/main/java/org/conscrypt/metrics/StatsLog.java +++ b/common/src/main/java/org/conscrypt/metrics/StatsLog.java @@ -15,6 +15,7 @@ */ package org.conscrypt.metrics; +import org.conscrypt.CertBlocklistEntry; import org.conscrypt.Internal; import org.conscrypt.ct.LogStore; import org.conscrypt.ct.PolicyCompliance; @@ -30,4 +31,6 @@ public void countTlsHandshake(boolean success, String protocol, String cipherSui public void reportCTVerificationResult(LogStore logStore, VerificationResult result, PolicyCompliance compliance, CertificateTransparencyVerificationReason reason); + + public void reportBlocklistHit(CertBlocklistEntry entry); } diff --git a/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java b/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java index 9a5365ef1..6b4d1e43c 100644 --- a/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java +++ b/common/src/main/java/org/conscrypt/metrics/StatsLogImpl.java @@ -15,6 +15,14 @@ */ package org.conscrypt.metrics; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_BUILT_IN; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_FILE; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_TEST; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_BUILT_IN; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_FILE; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_TEST; +import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_UNKNOWN; import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_STATE_CHANGED; import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_STATE_CHANGED__STATUS__STATUS_EXPIRED; import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_STATE_CHANGED__STATUS__STATUS_NOT_FOUND; @@ -30,21 +38,77 @@ import static org.conscrypt.metrics.ConscryptStatsLog.CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__RESULT__RESULT_UNKNOWN; import static org.conscrypt.metrics.ConscryptStatsLog.TLS_HANDSHAKE_REPORTED; +import org.conscrypt.CertBlocklistEntry; import org.conscrypt.Internal; import org.conscrypt.Platform; import org.conscrypt.ct.LogStore; import org.conscrypt.ct.PolicyCompliance; import org.conscrypt.ct.VerificationResult; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; + /** * Implements logging for Conscrypt metrics. */ @Internal public final class StatsLogImpl implements StatsLog { - private static final StatsLog INSTANCE = new StatsLogImpl(); - private StatsLogImpl() {} + private final BlockingQueue logQueue; + private final ExecutorService writerThreadExecutor; + private boolean running = false; + + private StatsLogImpl() { + this.logQueue = new LinkedBlockingQueue<>(100); + this.writerThreadExecutor = + Executors.newSingleThreadExecutor(new LowPriorityThreadFactory()); + startWriterThread(); + } public static StatsLog getInstance() { - return INSTANCE; + return new StatsLogImpl(); + } + + public void stop() { + running = false; + writerThreadExecutor.shutdownNow(); + try { + writerThreadExecutor.awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void startWriterThread() { + writerThreadExecutor.execute(() -> { + while (running) { + try { + // Blocks until a log task is available + Runnable logTask = logQueue.take(); + logTask.run(); // Execute the specific ConscryptStatsLog.write() call + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + running = false; + } + } + // Process remaining logs + while (!logQueue.isEmpty()) { + Runnable logTask = logQueue.poll(); + if (logTask != null) { + logTask.run(); + } + } + }); + } + + private static class LowPriorityThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "ConscryptStatsLogWriter"); + thread.setPriority(Thread.MIN_PRIORITY); + return thread; + } } @Override @@ -89,12 +153,21 @@ private static int policyComplianceToMetrics(VerificationResult result, } else if (result.getValidSCTs().size() == 0) { return CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__RESULT__RESULT_FAILURE_NO_SCTS_FOUND; } else if (compliance == PolicyCompliance.NOT_ENOUGH_SCTS - || compliance == PolicyCompliance.NOT_ENOUGH_DIVERSE_SCTS) { + || compliance == PolicyCompliance.NOT_ENOUGH_DIVERSE_SCTS + || compliance == PolicyCompliance.NO_RFC6962_LOG) { return CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__RESULT__RESULT_FAILURE_SCTS_NOT_COMPLIANT; } return CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__RESULT__RESULT_UNKNOWN; } + private static int getUid() { + int[] uids = Platform.getUids(); + if (uids != null && uids.length != 0) { + return uids[0]; + } + return 0; + } + @Override public void reportCTVerificationResult(LogStore store, VerificationResult result, PolicyCompliance compliance, @@ -103,19 +176,44 @@ public void reportCTVerificationResult(LogStore store, VerificationResult result || store.getState() == LogStore.State.MALFORMED) { write(CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED, CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__RESULT__RESULT_FAIL_OPEN_NO_LOG_LIST_AVAILABLE, - reason.getId(), 0, 0, 0, 0, 0, 0); + reason.getId(), 0, 0, 0, 0, 0, 0, getUid()); } else if (store.getState() == LogStore.State.NON_COMPLIANT) { write(CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED, CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED__RESULT__RESULT_FAIL_OPEN_LOG_LIST_NOT_COMPLIANT, - reason.getId(), 0, 0, 0, 0, 0, 0); + reason.getId(), 0, 0, 0, 0, 0, 0, getUid()); } else if (store.getState() == LogStore.State.COMPLIANT) { int comp = policyComplianceToMetrics(result, compliance); write(CERTIFICATE_TRANSPARENCY_VERIFICATION_REPORTED, comp, reason.getId(), store.getCompatVersion(), store.getMajorVersion(), store.getMinorVersion(), - result.numCertSCTs(), result.numOCSPSCTs(), result.numTlsSCTs()); + result.numCertSCTs(), result.numOCSPSCTs(), result.numTlsSCTs(), getUid()); + } + } + + private static int blocklistOriginToMetrics(CertBlocklistEntry.Origin origin) { + switch (origin) { + case SHA1_TEST: + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_TEST; + case SHA1_BUILT_IN: + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_BUILT_IN; + case SHA1_FILE: + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA1_FILE; + case SHA256_TEST: + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_TEST; + case SHA256_BUILT_IN: + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_BUILT_IN; + case SHA256_FILE: + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_SHA256_FILE; } + return CERTIFICATE_BLOCKLIST_BLOCK_REPORTED__SOURCE__BLOCKLIST_SOURCE_UNKNOWN; } + @Override + public void reportBlocklistHit(CertBlocklistEntry entry) { + write(CERTIFICATE_BLOCKLIST_BLOCK_REPORTED, blocklistOriginToMetrics(entry.getOrigin()), + entry.getIndex(), getUid()); + } + + private static final boolean sdkVersionBiggerThan32; static { @@ -137,7 +235,9 @@ private void write(int atomId, boolean success, int protocol, int cipherSuite, i builder.usePooledBuffer(); ReflexiveStatsLog.write(builder.build()); } else { - ConscryptStatsLog.write(atomId, success, protocol, cipherSuite, duration, source, uids); + logQueue.offer(() + -> ConscryptStatsLog.write(atomId, success, protocol, + cipherSuite, duration, source, uids)); } } @@ -149,9 +249,13 @@ private void write(int atomId, int status, int loadedCompatVersion, private void write(int atomId, int verificationResult, int verificationReason, int policyCompatVersion, int majorVersion, int minorVersion, - int numEmbeddedScts, int numOcspScts, int numTlsScts) { + int numEmbeddedScts, int numOcspScts, int numTlsScts, int uid) { ConscryptStatsLog.write(atomId, verificationResult, verificationReason, policyCompatVersion, majorVersion, minorVersion, numEmbeddedScts, numOcspScts, - numTlsScts); + numTlsScts, uid); + } + + private void write(int atomId, int origin, int index, int uid) { + ConscryptStatsLog.write(atomId, origin, index, uid); } } diff --git a/common/src/test/java/org/conscrypt/ArrayUtilsTest.java b/common/src/test/java/org/conscrypt/ArrayUtilsTest.java index 959cc5638..71836c1c6 100644 --- a/common/src/test/java/org/conscrypt/ArrayUtilsTest.java +++ b/common/src/test/java/org/conscrypt/ArrayUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright (C) 2023 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. diff --git a/common/src/test/java/org/conscrypt/HostnameVerifierTest.java b/common/src/test/java/org/conscrypt/HostnameVerifierTest.java index 2dc6b0fdf..127f695af 100644 --- a/common/src/test/java/org/conscrypt/HostnameVerifierTest.java +++ b/common/src/test/java/org/conscrypt/HostnameVerifierTest.java @@ -64,8 +64,6 @@ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { } private static final Charset UTF_8 = Charset.forName("UTF-8"); - // BEGIN Android-changed: Run tests for both default and strict verifiers. http://b/144694112 - // private HostnameVerifier verifier = OkHostnameVerifier.INSTANCE; @Parameters() public static Collection data() { // Both verifiers should behave the same in all tests except for @@ -75,7 +73,6 @@ public static Collection data() { } @Parameter public OkHostnameVerifier verifier; - // END Android-changed: Run tests for both default and strict verifiers. http://b/144694112 @Test public void verify() throws Exception { @@ -114,8 +111,6 @@ public void verifyCn() throws Exception { + "HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n" + "SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n" + "-----END CERTIFICATE-----\n"); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("foo.com", session)); X509Certificate[] certs = {}; assertFalse(verifier.verify(certs, "foo.com", session)); assertFalse(verifier.verify(certs, "a.foo.com", session)); @@ -152,8 +147,6 @@ public void verifyNonAsciiCn() throws Exception { + "9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n" + "UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n" + "-----END CERTIFICATE-----\n"); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); X509Certificate[] certs = {}; assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); assertFalse(verifier.verify(certs, "a.\u82b1\u5b50.co.jp", session)); @@ -321,8 +314,6 @@ public void verifyMultipleCn() throws Exception { assertFalse(verifier.verify(certs, "a.foo.com", session)); assertFalse(verifier.verify(certs, "bar.com", session)); assertFalse(verifier.verify(certs, "a.bar.com", session)); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); assertFalse(verifier.verify(certs, "a.\u82b1\u5b50.co.jp", session)); } @@ -359,11 +350,7 @@ public void verifyWilcardCn() throws Exception { + "-----END CERTIFICATE-----\n"); X509Certificate[] certs = {}; assertFalse(verifier.verify(certs, "foo.com", session)); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("www.foo.com", session)); assertFalse(verifier.verify(certs, "www.foo.com", session)); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session)); assertFalse(verifier.verify(certs, "\u82b1\u5b50.foo.com", session)); assertFalse(verifier.verify(certs, "a.b.foo.com", session)); } @@ -400,11 +387,7 @@ public void verifyWilcardCnOnTld() throws Exception { + "EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n" + "-----END CERTIFICATE-----\n"); X509Certificate[] certs = {}; - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("foo.co.jp", session)); assertFalse(verifier.verify(certs, "foo.co.jp", session)); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); assertFalse(verifier.verify(certs, "\u82b1\u5b50.co.jp", session)); } @@ -545,8 +528,6 @@ public void wildcardsDoesNotNeedTwoDots() throws Exception { + "U6LFxmZr31lFyis2/T68PpjAppc0DpNQuA2m/Y7oTHBDi55Fw6HVHCw3lucuWZ5d\n" + "qUYo4ES548JdpQtcLrW2sA==\n" + "-----END CERTIFICATE-----"); - // Android-changed: Ignore common name in hostname verification. http://b/70278814 - // assertTrue(verifier.verify("google.com", session)); X509Certificate[] certs = {}; assertFalse(verifier.verify(certs, "google.com", session)); } @@ -619,7 +600,6 @@ public void subjectAltNameWithWildcard() throws Exception { assertFalse(verifier.verify(certs, "quux.com", session)); } - // BEGIN Android-added: Verify behaviour with top level wildcard SAN. http://b/144694112 @Test public void subjectAltNameWithToplevelWildcard() throws Exception { // Default OkHostnameVerifier instance should allow SANs which @@ -645,8 +625,9 @@ public void subjectAltNameWithToplevelWildcard() throws Exception { assertTrue(OkHostnameVerifier.INSTANCE.verify(certs, "google.com", session)); assertFalse(OkHostnameVerifier.strictInstance().verify(certs, "google.com", session)); } - // END Android-added: Verify behaviour with top level wildcard SAN. http://b/144694112 + // android-add: OkHostnameVerifier.verifyAsIpAddress not accessible on platform builds + @Ignore @Test public void verifyAsIpAddress() { // IPv4 @@ -682,4 +663,4 @@ private X509Certificate certificate(String certificate) throws Exception { private SSLSession session(String certificate) throws Exception { return new FakeSSLSession(certificate(certificate)); } -} \ No newline at end of file +} diff --git a/common/src/test/java/org/conscrypt/MacTest.java b/common/src/test/java/org/conscrypt/MacTest.java index 4381e92dd..470ee6166 100644 --- a/common/src/test/java/org/conscrypt/MacTest.java +++ b/common/src/test/java/org/conscrypt/MacTest.java @@ -37,6 +37,7 @@ import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.spec.AlgorithmParameterSpec; import java.util.HashSet; diff --git a/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java b/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java index e8a0f182f..cc36a0f56 100644 --- a/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java +++ b/common/src/test/java/org/conscrypt/NativeCryptoArgTest.java @@ -66,12 +66,6 @@ public class NativeCryptoArgTest { private final Map> classCache = new HashMap<>(); private final Map methodMap = buildMethodMap(); - @AfterClass - public static void after() { - // TODO(prb): Temporary hacky check - remove - assertTrue(testedMethods.size() >= 190); - } - @Test public void ecMethods() throws Throwable { markTestRun(); diff --git a/common/src/test/java/org/conscrypt/ct/SerializationTest.java b/common/src/test/java/org/conscrypt/ct/SerializationTest.java index 5ce182e10..9151fa49c 100644 --- a/common/src/test/java/org/conscrypt/ct/SerializationTest.java +++ b/common/src/test/java/org/conscrypt/ct/SerializationTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +// android-add: import libcore.test.annotation.NonCts; +// android-add: import libcore.test.reasons.NonCtsReasons; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -30,6 +32,7 @@ @RunWith(JUnit4.class) public class SerializationTest { @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_decode_SignedCertificateTimestamp() throws Exception { byte[] in = new byte[] { 0x00, // version @@ -57,6 +60,7 @@ public void test_decode_SignedCertificateTimestamp() throws Exception { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_decode_invalid_SignedCertificateTimestamp() throws Exception { byte[] sct = new byte[] { 0x00, // version @@ -91,6 +95,7 @@ public void test_decode_invalid_SignedCertificateTimestamp() throws Exception { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_decode_DigitallySigned() throws Exception { byte[] in = new byte[] { 0x04, 0x03, // hash & signature algorithm @@ -105,6 +110,7 @@ public void test_decode_DigitallySigned() throws Exception { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_decode_invalid_DigitallySigned() throws Exception { try { DigitallySigned.decode(new byte[] { @@ -147,6 +153,7 @@ public void test_decode_invalid_DigitallySigned() throws Exception { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_encode_CertificateEntry_X509Certificate() throws Exception { // Use a dummy certificate. It doesn't matter, CertificateEntry doesn't care about the // contents. @@ -165,6 +172,7 @@ public void test_encode_CertificateEntry_X509Certificate() throws Exception { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_encode_CertificateEntry_PreCertificate() throws Exception { // Use a dummy certificate and issuer key hash. It doesn't matter, // CertificateEntry doesn't care about the contents. @@ -187,6 +195,7 @@ public void test_encode_CertificateEntry_PreCertificate() throws Exception { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_readDEROctetString() throws Exception { byte[] in, expected; diff --git a/common/src/test/java/org/conscrypt/ct/VerifierTest.java b/common/src/test/java/org/conscrypt/ct/VerifierTest.java index 02e8280d4..d612bedcb 100644 --- a/common/src/test/java/org/conscrypt/ct/VerifierTest.java +++ b/common/src/test/java/org/conscrypt/ct/VerifierTest.java @@ -20,6 +20,8 @@ import static org.conscrypt.TestUtils.readTestFile; import static org.junit.Assert.assertEquals; +// android-add: import libcore.test.annotation.NonCts; +// android-add: import libcore.test.reasons.NonCtsReasons; import org.conscrypt.OpenSSLX509Certificate; import org.conscrypt.TestUtils; import org.junit.Before; @@ -48,8 +50,7 @@ public void setUp() throws Exception { final LogInfo log = new LogInfo.Builder() .setPublicKey(key) - .setDescription("Test Log") - .setUrl("http://example.com") + .setType(LogInfo.TYPE_RFC6962) .setOperator("LogOperator") .setState(LogInfo.STATE_USABLE, 1643709600000L) .build(); @@ -98,6 +99,7 @@ public LogInfo getKnownLog(byte[] logId) { } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withOCSPResponse() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -109,6 +111,7 @@ public void test_verifySignedCertificateTimestamps_withOCSPResponse() throws Exc } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withTLSExtension() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -120,6 +123,7 @@ public void test_verifySignedCertificateTimestamps_withTLSExtension() throws Exc } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withEmbeddedExtension() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {certEmbedded, ca}; @@ -129,6 +133,7 @@ public void test_verifySignedCertificateTimestamps_withEmbeddedExtension() throw } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withoutTimestamp() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -138,6 +143,7 @@ public void test_verifySignedCertificateTimestamps_withoutTimestamp() throws Exc } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withInvalidSignature() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -152,6 +158,7 @@ public void test_verifySignedCertificateTimestamps_withInvalidSignature() throws } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withUnknownLog() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -165,6 +172,7 @@ public void test_verifySignedCertificateTimestamps_withUnknownLog() throws Excep } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withInvalidEncoding() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -178,6 +186,7 @@ public void test_verifySignedCertificateTimestamps_withInvalidEncoding() throws } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withInvalidOCSPResponse() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; @@ -191,6 +200,7 @@ public void test_verifySignedCertificateTimestamps_withInvalidOCSPResponse() thr } @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) public void test_verifySignedCertificateTimestamps_withMultipleTimestamps() throws Exception { OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] {cert, ca}; diff --git a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java index 0e52ad47b..dc5b00fcf 100644 --- a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java +++ b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java @@ -18,8 +18,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,6 +35,8 @@ @RunWith(JUnit4.class) public class AlgorithmParametersTestAES extends AbstractAlgorithmParametersTest { + // android-add: Allow access to deprecated BC algorithms. + private static final byte[] parameterData = new byte[] { (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8, (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5, (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8, diff --git a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java index 4efb757e0..9877c4578 100644 --- a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java +++ b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java @@ -18,8 +18,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,6 +35,8 @@ @RunWith(JUnit4.class) public class AlgorithmParametersTestDESede extends AbstractAlgorithmParametersTest { + // android-add: Allow access to deprecated BC algorithms. + private static final byte[] parameterData = new byte[] {(byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8, (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5}; diff --git a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestEC.java b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestEC.java index d9ff4fbf3..31d1c740a 100644 --- a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestEC.java +++ b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestEC.java @@ -18,8 +18,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -34,6 +37,8 @@ @RunWith(JUnit4.class) public class AlgorithmParametersTestEC extends AbstractAlgorithmParametersTest { + // android-add: Allow access to deprecated BC algorithms. + private static final String CURVE_NAME = "secp384r1"; private static final String CURVE_OID = "1.3.132.0.34"; diff --git a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java index e7a4e664c..1ae5a477a 100644 --- a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java +++ b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java @@ -20,8 +20,11 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -39,6 +42,8 @@ @RunWith(JUnit4.class) public class AlgorithmParametersTestGCM extends AbstractAlgorithmParametersTest { + // android-add: Allow access to deprecated BC algorithms. + private static final byte[] IV = new byte[] { (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8, (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5, (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8}; diff --git a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java index 45c1b15f9..4d2023d72 100644 --- a/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java +++ b/common/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java @@ -18,8 +18,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -35,6 +38,8 @@ @RunWith(JUnit4.class) public class AlgorithmParametersTestOAEP extends AbstractAlgorithmParametersTest { + // android-add: Allow access to deprecated BC algorithms. + // The ASN.1 encoding for OAEP params (specified in RFC 4055 section 4.1) specifies // default values for all parameters, so we need to consider encodings with those // values both explicitly specified and unspecified. When encoding values, it is required diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java index 152b05750..6b7880c9e 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestEC.java @@ -24,6 +24,7 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import java.util.List; @@ -32,6 +33,8 @@ @RunWith(JUnit4.class) public class KeyFactoryTestEC extends AbstractKeyFactoryTest { + // android-add: Allow access to deprecated BC algorithms. + public KeyFactoryTestEC() { super("EC", ECPublicKeySpec.class, ECPrivateKeySpec.class); } diff --git a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java index cc55d4ff5..4a4cd7448 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyFactoryTestRSA.java @@ -42,11 +42,14 @@ import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import java.util.Arrays; import java.util.List; @RunWith(JUnit4.class) public class KeyFactoryTestRSA extends AbstractKeyFactoryTest { + // android-add: Allow access to deprecated BC algorithms. + public KeyFactoryTestRSA() { super("RSA", RSAPublicKeySpec.class, RSAPrivateKeySpec.class); } diff --git a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java index e6eb3d4cd..15f248a8b 100644 --- a/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java +++ b/common/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java @@ -64,7 +64,11 @@ @RunWith(JUnit4.class) public class KeyPairGeneratorTest { + // android-add: Allow access to deprecated BC algorithms. + @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) + // android-add: @NonMts(reason = NonMtsReasons.API_LEVEL_GATING) public void test_getInstance() throws Exception { ServiceTester .test("KeyPairGenerator") diff --git a/common/src/test/java/org/conscrypt/java/security/MessageDigestTest.java b/common/src/test/java/org/conscrypt/java/security/MessageDigestTest.java index de430ea1f..45df784a7 100644 --- a/common/src/test/java/org/conscrypt/java/security/MessageDigestTest.java +++ b/common/src/test/java/org/conscrypt/java/security/MessageDigestTest.java @@ -18,8 +18,11 @@ import static org.junit.Assert.assertEquals; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -34,6 +37,8 @@ @RunWith(JUnit4.class) public final class MessageDigestTest { + // android-add: Allow access to deprecated BC algorithms. + private final byte[] sha_456 = {-24, 9, -59, -47, -50, -92, 123, 69, -29, 71, 1, -46, 63, 96, -118, -102, 88, 3, 77, -55}; diff --git a/common/src/test/java/org/conscrypt/java/security/SignatureTest.java b/common/src/test/java/org/conscrypt/java/security/SignatureTest.java index 287a8388a..4f50d8598 100644 --- a/common/src/test/java/org/conscrypt/java/security/SignatureTest.java +++ b/common/src/test/java/org/conscrypt/java/security/SignatureTest.java @@ -71,10 +71,14 @@ @RunWith(JUnit4.class) public class SignatureTest { + // android-add: Allow access to deprecated BC algorithms. + // 20 bytes for DSA private final byte[] EMPTY_DATA = new byte[20]; @Test + // android-add: @NonCts(reason = NonCtsReasons.INTERNAL_APIS) + // android-add: @NonMts(reason = NonMtsReasons.API_LEVEL_GATING) public void test_getInstance() throws Exception { ServiceTester .test("Signature") diff --git a/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java b/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java index 881912e7b..35ca968cb 100644 --- a/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java +++ b/common/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java @@ -75,6 +75,8 @@ @RunWith(JUnit4.class) public class CertificateFactoryTest { + // android-add: Allow access to deprecated BC algorithms. + private static final String VALID_CERTIFICATE_PEM = "-----BEGIN CERTIFICATE-----\n" + "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM\n" + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg\n" diff --git a/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java b/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java index b0244a724..8362ce84d 100644 --- a/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java +++ b/common/src/test/java/org/conscrypt/java/security/cert/X509CRLTest.java @@ -44,6 +44,8 @@ @RunWith(JUnit4.class) public class X509CRLTest { + // android-add: Allow access to deprecated BC algorithms. + private static final String CA_CERT = "-----BEGIN CERTIFICATE-----\n" + "MIIC0DCCAjmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk\n" + "MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX\n" diff --git a/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java b/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java index 78d1ece96..59988af3f 100644 --- a/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java +++ b/common/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java @@ -38,6 +38,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -56,6 +57,8 @@ @RunWith(JUnit4.class) public class X509CertificateTest { + // android-add: Allow access to deprecated BC algorithms. + private static final String VALID_CERT = "-----BEGIN CERTIFICATE-----\n" + "MIIFMjCCAxqgAwIBAgIJAL0mG5fOeJ7xMA0GCSqGSIb3DQEBCwUAMC0xCzAJBgNV\n" + "BAYTAkdCMQ8wDQYDVQQHDAZMb25kb24xDTALBgNVBAoMBFRlc3QwIBcNMTgwOTE3\n" diff --git a/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java b/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java index cb2cc3bc9..537dba1a4 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/CipherTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -87,6 +88,8 @@ @RunWith(JUnit4.class) public final class CipherTest { + // android-add: Allow access to deprecated BC algorithms. + @BeforeClass public static void setUp() { TestUtils.assumeAllowsUnsignedCrypto(); @@ -4777,73 +4780,6 @@ public void test_AESGCMNoPadding_init_algParams() throws Exception { assertEquals(Arrays.toString(c1.doFinal()), Arrays.toString(c2.doFinal())); } - /* - * http://b/27224566 - * http://b/27994930 - * Check that a PBKDF2WITHHMACSHA1 secret key factory works well with a - * PBEWITHSHAAND128BITAES-CBC-BC cipher. The former is PKCS5 and the latter is PKCS12, and so - * mixing them is not recommended. However, until 1.52 BouncyCastle was accepting this mixture, - * assuming the IV was a 0 vector. Some apps still use this functionality. This - * compatibility is likely to be removed in later versions of Android. - * TODO(27995180): consider whether we keep this compatibility. Consider whether we only allow - * if an IV is passed in the parameters. - */ - @Test - public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_noIV() throws Exception { - Assume.assumeNotNull(Security.getProvider("BC")); - byte[] plaintext = - new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; - byte[] ciphertext = new byte[] {92, -65, -128, 16, -102, -115, -44, 52, 16, 124, -34, - -45, 58, -70, -17, 127, 119, -67, 87, 91, 63, -13, - -40, 9, 97, -17, -71, 97, 10, -61, -19, -73}; - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA1"); - PBEKeySpec pbeks = new PBEKeySpec("password".toCharArray(), - "salt".getBytes(TestUtils.UTF_8), 100, 128); - SecretKey secretKey = skf.generateSecret(pbeks); - - Cipher cipher = Cipher.getInstance("PBEWITHSHAAND128BITAES-CBC-BC"); - PBEParameterSpec paramSpec = new PBEParameterSpec("salt".getBytes(TestUtils.UTF_8), 100); - cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec); - assertEquals(Arrays.toString(ciphertext), Arrays.toString(cipher.doFinal(plaintext))); - - secretKey = skf.generateSecret(pbeks); - cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec); - assertEquals(Arrays.toString(plaintext), Arrays.toString(cipher.doFinal(ciphertext))); - } - - /* - * http://b/27224566 - * http://b/27994930 - * Check that a PBKDF2WITHHMACSHA1 secret key factory works well with a - * PBEWITHSHAAND128BITAES-CBC-BC cipher. The former is PKCS5 and the latter is PKCS12, and so - * mixing them is not recommended. However, until 1.52 BouncyCastle was accepting this mixture, - * assuming the IV was a 0 vector. Some apps still use this functionality. This - * compatibility is likely to be removed in later versions of Android. - * TODO(27995180): consider whether we keep this compatibility. Consider whether we only allow - * if an IV is passed in the parameters. - */ - @Test - public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_withIV() throws Exception { - Assume.assumeNotNull(Security.getProvider("BC")); - byte[] plaintext = - new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; - byte[] ciphertext = {68, -87, 71, -6, 32, -77, 124, 3, 35, -26, 96, - -16, 100, -17, 52, -32, 110, 26, -117, 112, -25, -113, - -58, -30, 19, -46, -21, 59, -126, -8, -70, -89}; - byte[] iv = new byte[] {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA1"); - PBEKeySpec pbeks = new PBEKeySpec("password".toCharArray(), - "salt".getBytes(TestUtils.UTF_8), 100, 128); - SecretKey secretKey = skf.generateSecret(pbeks); - Cipher cipher = Cipher.getInstance("PBEWITHSHAAND128BITAES-CBC-BC"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); - assertEquals(Arrays.toString(ciphertext), Arrays.toString(cipher.doFinal(plaintext))); - - secretKey = skf.generateSecret(pbeks); - cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); - assertEquals(Arrays.toString(plaintext), Arrays.toString(cipher.doFinal(ciphertext))); - } - private static Cipher createAesCipher(int opmode) { try { final Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); diff --git a/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java b/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java index c41162cf7..edbb9875f 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java @@ -65,6 +65,8 @@ */ @RunWith(JUnit4.class) public class ECDHKeyAgreementTest { + // android-add: Allow access to deprecated BC algorithms. + // Two key pairs and the resulting shared secret for the Known Answer Test private static final byte[] KAT_PUBLIC_KEY1_X509 = TestUtils.decodeHex( "3059301306072a8648ce3d020106082a8648ce3d030107034200049fc2f71f85446b1371244491d83" @@ -463,6 +465,19 @@ private static Provider[] getKeyAgreementProviders() { if (providers == null) { return new Provider[0]; } + + // Do not test AndroidKeyStore as KeyAgreement provider. It only handles Android + // Keystore-backed keys. It's OKish not to test AndroidKeyStore here because it's tested by + // cts/tests/test/keystore. + List filteredProvidersList = new ArrayList(providers.length); + for (Provider provider : providers) { + if ("AndroidKeyStore".equals(provider.getName())) { + continue; + } + filteredProvidersList.add(provider); + } + providers = filteredProvidersList.toArray(new Provider[filteredProvidersList.size()]); + // Sort providers by name to guarantee deterministic order in which providers are used in // the tests. return sortByName(providers); diff --git a/common/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java b/common/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java index 414ceb862..c399344de 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java @@ -19,10 +19,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +// android-add: import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule; import org.conscrypt.TestUtils; import org.conscrypt.java.security.StandardNames; import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -40,6 +43,8 @@ @RunWith(JUnit4.class) public class KeyGeneratorTest { + // android-add: Allow access to deprecated BC algorithms. + private static boolean isUnsupported(KeyGenerator kg) { // Don't bother testing "Sun..." KeyGenerators or BC outside of Android return kg.getProvider().getName().startsWith("Sun") diff --git a/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyFactoryTest.java b/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyFactoryTest.java index 5873ea6e7..80b51feb2 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyFactoryTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright (C) 2023 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. diff --git a/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyTest.java b/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyTest.java index c49547596..b756ba121 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright (C) 2023 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. diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java index d436a29de..ae89950bb 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java @@ -597,11 +597,6 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, // should be agreed assertEquals(referenceContext.host.getHostName(), session.getPeerHost()); - - // The negotiated cipher suite should be one of the enabled - // ones, but BoringSSL may have reordered them based on things - // like hardware support, so we don't know which one may have - // been negotiated. String sessionSuite = session.getCipherSuite(); List enabledSuites = Arrays.asList(referenceEngine.getEnabledCipherSuites()); diff --git a/openjdk/build.gradle b/openjdk/build.gradle index b63b4c8de..c9b38d427 100644 --- a/openjdk/build.gradle +++ b/openjdk/build.gradle @@ -350,6 +350,15 @@ def testInterop = tasks.register("testInterop", Test) { } check.dependsOn testInterop +// Added to see results of new ECH tests when running tests from the command line +tasks.withType(Test).configureEach { + testLogging { + exceptionFormat "full" + events "started", "skipped", "passed", "failed" + showStandardStreams true + } +} + jacocoTestReport { additionalSourceDirs.from files("$rootDir/openjdk/src/test/java", "$rootDir/common/src/main/java") executionData tasks.withType(Test) @@ -415,7 +424,6 @@ model { if (toolChain in Clang || toolChain in Gcc) { cppCompiler.args "-Wall", - "-Werror", "-fPIC", "-O3", "-std=c++17", diff --git a/openjdk/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java b/openjdk/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java new file mode 100644 index 000000000..1e516c8fc --- /dev/null +++ b/openjdk/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 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; + +import org.conscrypt.metrics.CertificateTransparencyVerificationReason; + +/** + * A default NetworkSecurityPolicy for OpenJDK. + */ +@Internal +public class ConscryptNetworkSecurityPolicy implements NetworkSecurityPolicy { + public static ConscryptNetworkSecurityPolicy getDefault() { + return new ConscryptNetworkSecurityPolicy(); + } + + @Override + public boolean isCertificateTransparencyVerificationRequired(String hostname) { + return false; + } + + @Override + public CertificateTransparencyVerificationReason getCertificateTransparencyVerificationReason( + String hostname) { + return CertificateTransparencyVerificationReason.UNKNOWN; + } + + @Override + public DomainEncryptionMode getDomainEncryptionMode(String hostname) { + return DomainEncryptionMode.UNKNOWN; + } +} diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java index 58a88d848..4be4c871f 100644 --- a/openjdk/src/main/java/org/conscrypt/Platform.java +++ b/openjdk/src/main/java/org/conscrypt/Platform.java @@ -77,9 +77,11 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.function.Supplier; import javax.crypto.spec.GCMParameterSpec; import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; @@ -648,57 +650,8 @@ static boolean supportsX509ExtendedTrustManager() { return true; } - /** - * Check if SCT verification is required for a given hostname. - * - * SCT Verification is enabled using {@code Security} properties. - * The "conscrypt.ct.enable" property must be true, as well as a per domain property. - * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce." - * is used as the property name. - * Basic globbing is also supported. - * - * For example, for the domain foo.bar.com, the following properties will be - * looked up, in order of precedence. - * - conscrypt.ct.enforce.com.bar.foo - * - conscrypt.ct.enforce.com.bar.* - * - conscrypt.ct.enforce.com.* - * - conscrypt.ct.enforce.* - */ - public static boolean isCTVerificationRequired(String hostname) { - if (hostname == null) { - return false; - } - - String property = Security.getProperty("conscrypt.ct.enable"); - if (property == null || !Boolean.parseBoolean(property.toLowerCase(Locale.ROOT))) { - return false; - } - - List parts = Arrays.asList(hostname.split("\\.")); - Collections.reverse(parts); - - boolean enable = false; - StringBuilder propertyName = new StringBuilder("conscrypt.ct.enforce"); - // The loop keeps going on even once we've found a match - // This allows for finer grained settings on subdomains - for (String part : parts) { - property = Security.getProperty(propertyName + ".*"); - if (property != null) { - enable = Boolean.parseBoolean(property.toLowerCase(Locale.ROOT)); - } - propertyName.append(".").append(part); - } - - property = Security.getProperty(propertyName.toString()); - if (property != null) { - enable = Boolean.parseBoolean(property.toLowerCase(Locale.ROOT)); - } - return enable; - } - - public static CertificateTransparencyVerificationReason reasonCTVerificationRequired( - String hostname) { - return CertificateTransparencyVerificationReason.UNKNOWN; + static SSLException wrapInvalidEchDataException(SSLException e) { + return e; } static boolean supportsConscryptCertStore() { @@ -765,7 +718,8 @@ static CertBlocklist newDefaultBlocklist() { return null; } - static CertificateTransparency newDefaultCertificateTransparency() { + static CertificateTransparency newDefaultCertificateTransparency( + Supplier policySupplier) { return null; } diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java index 22c292d8c..16a1bbe47 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java @@ -87,6 +87,7 @@ MlDsaTest.class, NativeCryptoArgTest.class, NativeCryptoTest.class, + NativeSslTest.class, NativeRefTest.class, NativeSslSessionTest.class, OpenSSLKeyTest.class, diff --git a/platform/src/main/ct_log_store.fbs b/platform/src/main/ct_log_store.fbs new file mode 100644 index 000000000..9a15f998a --- /dev/null +++ b/platform/src/main/ct_log_store.fbs @@ -0,0 +1,38 @@ +namespace com.android.org.conscrypt.ct.fbs; + +file_identifier "CTFB"; +file_extension "ctfb"; + +enum LogType:byte { + Unknown = 0, + Rfc6962 = 1, + Static = 2 +} + +enum LogState:byte { + Unknown = 0, + Pending = 1, + Qualified = 2, + Usable = 3, + Readonly = 4, + Retired = 5, + Rejected = 6 +} + +table Log { + log_id:string (key); + public_key:[byte]; + operator:string; + type:LogType; + state:LogState; + state_timestamp:long; +} + +table LogList { + version_major:long; + version_minor:long; + timestamp:long; + logs:[Log]; +} + +root_type LogList; diff --git a/platform/src/main/java/org/conscrypt/AndroidHpkeSpi.java b/platform/src/main/java/org/conscrypt/AndroidHpkeSpi.java index 24b4bfa6b..8e3df8641 100644 --- a/platform/src/main/java/org/conscrypt/AndroidHpkeSpi.java +++ b/platform/src/main/java/org/conscrypt/AndroidHpkeSpi.java @@ -104,4 +104,22 @@ public X25519_CHACHA20() { super(new HpkeImpl.X25519_CHACHA20()); } } + + public static class XwingHkdfSha256Aes128Gcm extends AndroidHpkeSpi { + public XwingHkdfSha256Aes128Gcm() { + super(new HpkeImpl.XwingHkdfSha256Aes128Gcm()); + } + } + + public static class XwingHkdfSha256Aes256Gcm extends AndroidHpkeSpi { + public XwingHkdfSha256Aes256Gcm() { + super(new HpkeImpl.XwingHkdfSha256Aes256Gcm()); + } + } + + public static class XwingHkdfSha256ChaCha20Poly1305 extends AndroidHpkeSpi { + public XwingHkdfSha256ChaCha20Poly1305() { + super(new HpkeImpl.XwingHkdfSha256ChaCha20Poly1305()); + } + } } diff --git a/platform/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java b/platform/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java new file mode 100644 index 000000000..ff0f9a625 --- /dev/null +++ b/platform/src/main/java/org/conscrypt/ConscryptNetworkSecurityPolicy.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2025 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; + +import org.conscrypt.metrics.CertificateTransparencyVerificationReason; + +/** + * ConscryptNetworkSecurityPolicy for the platform (mainline). + * + * The Conscrypt-internal interface NetworkSecurityPolicy is ignored when exporting the API. + */ +@SuppressWarnings("HiddenSuperclass") +public class ConscryptNetworkSecurityPolicy implements NetworkSecurityPolicy { + private final libcore.net.NetworkSecurityPolicy policy; + + public static ConscryptNetworkSecurityPolicy getDefault() { + return new ConscryptNetworkSecurityPolicy(libcore.net.NetworkSecurityPolicy.getInstance()); + } + + public ConscryptNetworkSecurityPolicy(libcore.net.NetworkSecurityPolicy policy) { + this.policy = policy; + } + + @Override + public boolean isCertificateTransparencyVerificationRequired(String hostname) { + return policy.isCertificateTransparencyVerificationRequired(hostname); + } + + @Override + public CertificateTransparencyVerificationReason getCertificateTransparencyVerificationReason( + String hostname) { + if (Platform.isSdkGreater(33) + && com.android.libcore.Flags.networkSecurityPolicyReasonCtEnabledApi()) { + CertificateTransparencyVerificationReason reason = plaformCtReasonToConscryptReason( + policy.getCertificateTransparencyVerificationReason(hostname)); + if (reason != CertificateTransparencyVerificationReason.UNKNOWN) { + return reason; + } + } + if (policy.isCertificateTransparencyVerificationRequired("")) { + return CertificateTransparencyVerificationReason.APP_OPT_IN; + } else if (policy.isCertificateTransparencyVerificationRequired(hostname)) { + return CertificateTransparencyVerificationReason.DOMAIN_OPT_IN; + } + return CertificateTransparencyVerificationReason.UNKNOWN; + } + + private static CertificateTransparencyVerificationReason plaformCtReasonToConscryptReason( + int platformReason) { + switch (platformReason) { + case libcore.net.NetworkSecurityPolicy.CERTIFICATE_TRANSPARENCY_REASON_APP_OPT_IN: + return CertificateTransparencyVerificationReason.APP_OPT_IN; + case libcore.net.NetworkSecurityPolicy.CERTIFICATE_TRANSPARENCY_REASON_DOMAIN_OPT_IN: + return CertificateTransparencyVerificationReason.DOMAIN_OPT_IN; + case libcore.net.NetworkSecurityPolicy + .CERTIFICATE_TRANSPARENCY_REASON_SDK_TARGET_DEFAULT_ENABLED: + return CertificateTransparencyVerificationReason.SDK_TARGET_DEFAULT_ENABLED; + default: + return CertificateTransparencyVerificationReason.UNKNOWN; + } + } + + @Override + public DomainEncryptionMode getDomainEncryptionMode(String hostname) { + // Domain encryption is enabled if it is supported by the platform AND + // the API is available in libcore. + if (org.conscrypt.net.flags.Flags.encryptedClientHelloPlatform() + && com.android.libcore.Flags.networkSecurityPolicyEchApi()) { + return platformToConscryptEncryptionMode(policy.getDomainEncryptionMode(hostname)); + } + return DomainEncryptionMode.UNKNOWN; + } + + private static DomainEncryptionMode platformToConscryptEncryptionMode(int platformMode) { + switch (platformMode) { + case libcore.net.NetworkSecurityPolicy.DOMAIN_ENCRYPTION_MODE_DISABLED: + return DomainEncryptionMode.DISABLED; + case libcore.net.NetworkSecurityPolicy.DOMAIN_ENCRYPTION_MODE_OPPORTUNISTIC: + return DomainEncryptionMode.OPPORTUNISTIC; + case libcore.net.NetworkSecurityPolicy.DOMAIN_ENCRYPTION_MODE_ENABLED: + return DomainEncryptionMode.ENABLED; + case libcore.net.NetworkSecurityPolicy.DOMAIN_ENCRYPTION_MODE_REQUIRED: + return DomainEncryptionMode.REQUIRED; + default: + return DomainEncryptionMode.UNKNOWN; + } + } +} diff --git a/platform/src/main/java/org/conscrypt/Hex.java b/platform/src/main/java/org/conscrypt/Hex.java index d88205e49..7cdfe0479 100644 --- a/platform/src/main/java/org/conscrypt/Hex.java +++ b/platform/src/main/java/org/conscrypt/Hex.java @@ -20,24 +20,12 @@ * Helper class for dealing with hexadecimal strings. */ @Internal -// public for testing by TrustedCertificateStoreTest -// TODO(nathanmittler): Move to InternalUtil? public final class Hex { private Hex() {} private final static char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - public static String bytesToHexString(byte[] bytes) { - char[] buf = new char[bytes.length * 2]; - int c = 0; - for (byte b : bytes) { - buf[c++] = DIGITS[(b >> 4) & 0xf]; - buf[c++] = DIGITS[b & 0xf]; - } - return new String(buf); - } - public static String intToHexString(int i, int minWidth) { int bufLen = 8; // Max number of hex digits in an int char[] buf = new char[bufLen]; @@ -49,4 +37,33 @@ public static String intToHexString(int i, int minWidth) { return new String(buf, cursor, bufLen - cursor); } + + public static byte[] decodeHex(String encoded) throws IllegalArgumentException { + if ((encoded.length() % 2) != 0) { + throw new IllegalArgumentException("Invalid input length: " + encoded.length()); + } + + int resultLengthBytes = encoded.length() / 2; + byte[] result = new byte[resultLengthBytes]; + + int resultOffset = 0; + int i = 0; + for (int len = encoded.length(); i < len; i += 2) { + result[resultOffset++] = + (byte) ((toDigit(encoded.charAt(i)) << 4) | toDigit(encoded.charAt(i + 1))); + } + + return result; + } + + private static int toDigit(char pseudoCodePoint) throws IllegalArgumentException { + if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { + return pseudoCodePoint - '0'; + } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { + return 10 + (pseudoCodePoint - 'a'); + } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { + return 10 + (pseudoCodePoint - 'A'); + } + throw new IllegalArgumentException("Illegal char: " + pseudoCodePoint); + } } diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java index ad7fb182a..33a7aa485 100644 --- a/platform/src/main/java/org/conscrypt/Platform.java +++ b/platform/src/main/java/org/conscrypt/Platform.java @@ -26,8 +26,7 @@ import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; import dalvik.system.VMRuntime; - -import libcore.net.NetworkSecurityPolicy; +import dalvik.system.ZygoteHooks; import org.conscrypt.NativeCrypto; import org.conscrypt.ct.CertificateTransparency; @@ -37,12 +36,14 @@ import org.conscrypt.ct.PolicyImpl; import org.conscrypt.flags.Flags; import org.conscrypt.metrics.CertificateTransparencyVerificationReason; +import org.conscrypt.metrics.NoopStatsLog; import org.conscrypt.metrics.OptionalMethod; import org.conscrypt.metrics.Source; import org.conscrypt.metrics.StatsLog; import org.conscrypt.metrics.StatsLogImpl; import java.io.FileDescriptor; +import java.io.FileReader; import java.io.IOException; import java.lang.System; import java.lang.reflect.Field; @@ -66,6 +67,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import javax.crypto.spec.GCMParameterSpec; import javax.net.ssl.HttpsURLConnection; @@ -73,6 +75,7 @@ import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; @@ -90,8 +93,13 @@ private static class NoPreloadHolder { private static boolean DEPRECATED_TLS_V1 = true; private static boolean ENABLED_TLS_V1 = false; private static boolean FILTERED_TLS_V1 = true; + private static boolean RUNNING_IN_ZYGOTE = true; + private static final boolean canProbeZygote; + private static final boolean canCallZygoteMethod; static { + canProbeZygote = isSdkGreater(32); + canCallZygoteMethod = isSdkGreater(36); NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); } @@ -104,6 +112,7 @@ public static synchronized void setup(boolean deprecatedTlsV1, boolean enabledTl FILTERED_TLS_V1 = !enabledTlsV1; NoPreloadHolder.MAPPER.ping(); NativeCrypto.setTlsV1DeprecationStatus(DEPRECATED_TLS_V1, ENABLED_TLS_V1); + RUNNING_IN_ZYGOTE = inZygote(); } /** @@ -172,7 +181,8 @@ static void setSSLParameters(SSLParameters params, SSLParametersImpl impl, try { Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); - } catch (NoSuchMethodException | IllegalArgumentException e) { + } catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { // Do nothing. } @@ -216,9 +226,11 @@ static void setSSLParameters(SSLParameters params, SSLParametersImpl impl, try { Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); - } catch (NoSuchMethodException | IllegalArgumentException e) { + } catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { // Do nothing. } + List serverNames = params.getServerNames(); if (serverNames != null) { for (SNIServerName serverName : serverNames) { @@ -243,6 +255,7 @@ static void getSSLParameters(SSLParameters params, SSLParametersImpl impl, } catch (NoSuchMethodException | IllegalArgumentException e) { // Do nothing. } + if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) { params.setServerNames(Collections.singletonList( new SNIHostName(engine.getHostname()))); @@ -533,23 +546,8 @@ static boolean supportsX509ExtendedTrustManager() { return true; } - public static boolean isCTVerificationRequired(String hostname) { - if (Flags.certificateTransparencyPlatform()) { - return NetworkSecurityPolicy.getInstance() - .isCertificateTransparencyVerificationRequired(hostname); - } - return false; - } - - public static CertificateTransparencyVerificationReason reasonCTVerificationRequired( - String hostname) { - if (NetworkSecurityPolicy.getInstance().isCertificateTransparencyVerificationRequired("")) { - return CertificateTransparencyVerificationReason.APP_OPT_IN; - } else if (NetworkSecurityPolicy.getInstance() - .isCertificateTransparencyVerificationRequired(hostname)) { - return CertificateTransparencyVerificationReason.DOMAIN_OPT_IN; - } - return CertificateTransparencyVerificationReason.UNKNOWN; + static SSLException wrapInvalidEchDataException(SSLException e) { + return new android.net.ssl.InvalidEchDataException(e.getMessage()); } static boolean supportsConscryptCertStore() { @@ -574,11 +572,13 @@ static CertBlocklist newDefaultBlocklist() { return CertBlocklistImpl.getDefault(); } - static CertificateTransparency newDefaultCertificateTransparency() { + static CertificateTransparency newDefaultCertificateTransparency( + Supplier policySupplier) { org.conscrypt.ct.Policy policy = new org.conscrypt.ct.PolicyImpl(); org.conscrypt.ct.LogStore logStore = new org.conscrypt.ct.LogStoreImpl(policy); org.conscrypt.ct.Verifier verifier = new org.conscrypt.ct.Verifier(logStore); - return new CertificateTransparency(logStore, policy, verifier, getStatsLog()); + return new CertificateTransparency(logStore, policy, verifier, getStatsLog(), + policySupplier); } static boolean serverNamePermitted(SSLParametersImpl parameters, String serverName) { @@ -610,7 +610,14 @@ static long getMillisSinceBoot() { } public static StatsLog getStatsLog() { - return StatsLogImpl.getInstance(); + if (!RUNNING_IN_ZYGOTE) { + return StatsLogImpl.getInstance(); + } + if (!inZygote()) { + RUNNING_IN_ZYGOTE = false; + return StatsLogImpl.getInstance(); + } + return NoopStatsLog.getInstance(); } public static Source getStatsSource() { @@ -645,6 +652,31 @@ public static boolean isPakeSupported() { return true; } + private static boolean inZygote() { + if (canCallZygoteMethod) { + return ZygoteHooks.isInZygote(); + } + if (canProbeZygote) { + try { + Class zygoteHooksClass = Class.forName("dalvik.system.ZygoteHooks"); + Method inZygoteMethod = zygoteHooksClass.getDeclaredMethod("inZygote"); + Object inZygote = inZygoteMethod.invoke(null); + if (inZygote == null) { + return true; + } + return (boolean) inZygote; + } catch (IllegalAccessException | NullPointerException | InvocationTargetException + | ClassNotFoundException | NoSuchMethodException e) { + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + // For previous releases, we have no mechanism to test if we are in Zygote. + // Assume we are not, to conserve the existing behaviour. + return false; + } + static Object getTargetSdkVersion() { try { Class vmRuntimeClass = Class.forName("dalvik.system.VMRuntime"); diff --git a/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java b/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java index fc875e907..e18c3ef3b 100644 --- a/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java +++ b/platform/src/main/java/org/conscrypt/ct/LogStoreImpl.java @@ -37,32 +37,29 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @Internal public class LogStoreImpl implements LogStore { private static final Logger logger = Logger.getLogger(LogStoreImpl.class.getName()); - private static final String BASE_PATH = "misc/keychain/ct"; - private static final int COMPAT_VERSION = 1; - private static final String CURRENT = "current"; - private static final String LOG_LIST_FILENAME = "log_list.json"; - private static final Path DEFAULT_LOG_LIST; + private static final int COMPAT_VERSION = 2; + private static final Path logListPrefix; + private static final Path logListSuffix; + private static final long LOG_LIST_CHECK_INTERVAL_IN_MS = 10L * 60 * 1_000; // 10 minutes static { String androidData = System.getenv("ANDROID_DATA"); - String compatVersion = String.format("v%d", COMPAT_VERSION); - DEFAULT_LOG_LIST = - Paths.get(androidData, BASE_PATH, compatVersion, CURRENT, LOG_LIST_FILENAME); + // /data/misc/keychain/ct/v1/current/log_list.json + logListPrefix = Paths.get(androidData, "misc", "keychain", "ct"); + logListSuffix = Paths.get("current", "log_list.json"); } private final Path logList; @@ -73,20 +70,34 @@ public class LogStoreImpl implements LogStore { private int minorVersion; private long timestamp; private Map logs; + private long logListLastModified; + private Supplier clock; + private long logListLastChecked; - public LogStoreImpl(Policy policy) { - this(policy, DEFAULT_LOG_LIST); + /* We do not have access to InstantSource. Implement a similar pattern using Supplier. */ + static class SystemTimeSupplier implements Supplier { + @Override + public Long get() { + return System.currentTimeMillis(); + } } - public LogStoreImpl(Policy policy, Path logList) { - this(policy, logList, Platform.getStatsLog()); + private static Path getPathForCompatVersion(int compatVersion) { + String version = String.format("v%d", compatVersion); + return logListPrefix.resolve(version).resolve(logListSuffix); + } + + public LogStoreImpl(Policy policy) { + this(policy, getPathForCompatVersion(COMPAT_VERSION), Platform.getStatsLog(), + new SystemTimeSupplier()); } - public LogStoreImpl(Policy policy, Path logList, StatsLog metrics) { + public LogStoreImpl(Policy policy, Path logList, StatsLog metrics, Supplier clock) { this.state = State.UNINITIALIZED; this.policy = policy; this.logList = logList; this.metrics = metrics; + this.clock = clock; } @Override @@ -112,8 +123,7 @@ public int getMinorVersion() { @Override public int getCompatVersion() { - // Currently, there is only one compatibility version supported. If we - // are loaded or initialized, it means the expected compatibility + // If we are loaded or initialized, it means the expected compatibility // version was found. if (state == State.LOADED || state == State.COMPLIANT || state == State.NON_COMPLIANT) { return COMPAT_VERSION; @@ -123,6 +133,9 @@ public int getCompatVersion() { @Override public int getMinCompatVersionAvailable() { + if (Files.exists(getPathForCompatVersion(1))) { + return 1; + } return getCompatVersion(); } @@ -145,26 +158,55 @@ public LogInfo getKnownLog(byte[] logId) { /* Ensures the log list is loaded. * Returns true if the log list is usable. */ - private boolean ensureLogListIsLoaded() { - synchronized (this) { - State previousState = state; - if (state == State.UNINITIALIZED) { - state = loadLogList(); - } - if (state == State.LOADED && policy != null) { - state = policy.isLogStoreCompliant(this) ? State.COMPLIANT : State.NON_COMPLIANT; + private synchronized boolean ensureLogListIsLoaded() { + resetLogListIfRequired(); + State previousState = state; + if (state == State.UNINITIALIZED) { + state = loadLogList(); + } + if (state == State.LOADED && policy != null) { + state = policy.isLogStoreCompliant(this) ? State.COMPLIANT : State.NON_COMPLIANT; + } + if (state != previousState) { + metrics.updateCTLogListStatusChanged(this); + } + return state == State.COMPLIANT; + } + + private synchronized void resetLogListIfRequired() { + long now = clock.get(); + if (now >= this.logListLastChecked + && now < this.logListLastChecked + LOG_LIST_CHECK_INTERVAL_IN_MS) { + return; + } + this.logListLastChecked = now; + try { + long lastModified = Files.getLastModifiedTime(logList).toMillis(); + if (this.logListLastModified == lastModified) { + // The log list has the same last modified timestamp. Keep our + // current cached value. + return; } - if (state != previousState) { - metrics.updateCTLogListStatusChanged(this); + } catch (IOException e) { + if (this.logListLastModified == 0) { + // The log list is not accessible now and it has never been + // previously, there is nothing to do. + return; } - return state == State.COMPLIANT; } + this.state = State.UNINITIALIZED; + this.logs = null; + this.timestamp = 0; + this.majorVersion = 0; + this.minorVersion = 0; } private State loadLogList() { byte[] content; + long lastModified; try { content = Files.readAllBytes(logList); + lastModified = Files.getLastModifiedTime(logList).toMillis(); } catch (IOException e) { return State.NOT_FOUND; } @@ -182,39 +224,18 @@ private State loadLogList() { try { majorVersion = parseMajorVersion(json.getString("version")); minorVersion = parseMinorVersion(json.getString("version")); - timestamp = parseTimestamp(json.getString("log_list_timestamp")); + timestamp = json.getLong("log_list_timestamp"); JSONArray operators = json.getJSONArray("operators"); for (int i = 0; i < operators.length(); i++) { JSONObject operator = operators.getJSONObject(i); String operatorName = operator.getString("name"); - JSONArray logs = operator.getJSONArray("logs"); - for (int j = 0; j < logs.length(); j++) { - JSONObject log = logs.getJSONObject(j); - - LogInfo.Builder builder = - new LogInfo.Builder() - .setDescription(log.getString("description")) - .setPublicKey(parsePubKey(log.getString("key"))) - .setUrl(log.getString("url")) - .setOperator(operatorName); - JSONObject stateObject = log.optJSONObject("state"); - if (stateObject != null) { - String state = stateObject.keys().next(); - String stateTimestamp = - stateObject.getJSONObject(state).getString("timestamp"); - builder.setState(parseState(state), parseTimestamp(stateTimestamp)); - } - - LogInfo logInfo = builder.build(); - byte[] logId = Base64.getDecoder().decode(log.getString("log_id")); - - // The logId computed using the public key should match the log_id field. - if (!Arrays.equals(logInfo.getID(), logId)) { - throw new IllegalArgumentException("logId does not match publicKey"); - } + JSONArray logs = operator.getJSONArray("logs"); + addLogsToMap(logs, operatorName, LogInfo.TYPE_RFC6962, logsMap); - logsMap.put(new ByteArray(logId), logInfo); + JSONArray tiledLogs = operator.optJSONArray("tiled_logs"); + if (tiledLogs != null) { + addLogsToMap(tiledLogs, operatorName, LogInfo.TYPE_STATIC_CT_API, logsMap); } } } catch (JSONException | IllegalArgumentException e) { @@ -222,9 +243,45 @@ private State loadLogList() { return State.MALFORMED; } this.logs = Collections.unmodifiableMap(logsMap); + this.logListLastModified = lastModified; return State.LOADED; } + private void addLogsToMap(JSONArray logs, String operatorName, int logType, + Map logsMap) throws JSONException { + for (int j = 0; j < logs.length(); j++) { + JSONObject log = logs.getJSONObject(j); + LogInfo.Builder builder = new LogInfo.Builder() + .setPublicKey(parsePubKey(log.getString("key"))) + .setType(logType) + .setOperator(operatorName); + JSONObject stateObject = log.optJSONObject("state"); + if (stateObject != null) { + String state = stateObject.keys().next(); + long stateTimestamp = stateObject.getJSONObject(state).getLong("timestamp"); + builder.setState(parseState(state), stateTimestamp); + } + LogInfo logInfo = builder.build(); + + String logIdFromList = log.getString("log_id"); + // The logId computed using the public key should match the log_id field. + byte[] logId = Base64.getDecoder().decode(logIdFromList); + if (!Arrays.equals(logInfo.getID(), logId)) { + throw new IllegalArgumentException("logId does not match publicKey"); + } + + // Verify that the log is in a known state now. This might fail if + // there is an issue with the device's clock which can cause false + // positives when validating SCTs. + if (logInfo.getStateAt(clock.get()) == LogInfo.STATE_UNKNOWN) { + throw new IllegalArgumentException("Log current state is " + + "unknown, logId: " + logIdFromList); + } + + logsMap.put(new ByteArray(logId), logInfo); + } + } + private static int parseMajorVersion(String version) { int pos = version.indexOf("."); if (pos == -1) { @@ -268,19 +325,6 @@ private static int parseState(String state) { } } - // ISO 8601 - private static DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); - - @SuppressWarnings("JavaUtilDate") - private static long parseTimestamp(String timestamp) { - try { - Date date = dateFormatter.parse(timestamp); - return date.getTime(); - } catch (ParseException e) { - throw new IllegalArgumentException(e); - } - } - private static PublicKey parsePubKey(String key) { byte[] pem = ("-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----") .getBytes(US_ASCII); diff --git a/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java b/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java index a606557fa..dd48cf971 100644 --- a/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java +++ b/platform/src/main/java/org/conscrypt/ct/PolicyImpl.java @@ -178,7 +178,7 @@ private PolicyCompliance conformEmbeddedSCTs(Set embeddedValidSCTs, return PolicyCompliance.NOT_ENOUGH_SCTS; } - /* 3. Among the SCTs satisfying requirements 1 and 2, at least two SCTs + /* 3. Among the SCTs satisfying requirements 2, at least two SCTs * must be issued from distinct CT Log Operators as recognized by * Chrome. */ @@ -190,6 +190,20 @@ private PolicyCompliance conformEmbeddedSCTs(Set embeddedValidSCTs, return PolicyCompliance.NOT_ENOUGH_DIVERSE_SCTS; } + /* 4. Among the SCTs satisfying requirement 2, at least one SCT must be + * issued from a log recognized by Chrome as being RFC6962-compliant. + */ + boolean foundRfc6962Log = false; + for (LogInfo logInfo : validLogs) { + if (logInfo.getType() == LogInfo.TYPE_RFC6962) { + foundRfc6962Log = true; + break; + } + } + if (!foundRfc6962Log) { + return PolicyCompliance.NO_RFC6962_LOG; + } + return PolicyCompliance.COMPLY; } @@ -223,6 +237,20 @@ private PolicyCompliance conformOCSPorTLSSCTs(Set ocspOrTLSValidSCT return PolicyCompliance.NOT_ENOUGH_DIVERSE_SCTS; } + /* 3. Among the SCTs satisfying requirement 1, at least one SCT must be + * issued from a log recognized by Chrome as being RFC6962-compliant. + */ + boolean foundRfc6962Log = false; + for (LogInfo logInfo : validLogs) { + if (logInfo.getType() == LogInfo.TYPE_RFC6962) { + foundRfc6962Log = true; + break; + } + } + if (!foundRfc6962Log) { + return PolicyCompliance.NO_RFC6962_LOG; + } + return PolicyCompliance.COMPLY; } } diff --git a/platform/src/test/java/org/conscrypt/AndroidHpkeSpiTest.java b/platform/src/test/java/org/conscrypt/AndroidHpkeSpiTest.java index 4348b472e..8901b4077 100644 --- a/platform/src/test/java/org/conscrypt/AndroidHpkeSpiTest.java +++ b/platform/src/test/java/org/conscrypt/AndroidHpkeSpiTest.java @@ -30,7 +30,10 @@ public class AndroidHpkeSpiTest { private static final String[] HPKE_NAMES = new String[] {"DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/AES_128_GCM", "DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/AES_256_GCM", - "DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/CHACHA20POLY1305"}; + "DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/CHACHA20POLY1305", + "XWING/HKDF_SHA256/AES_128_GCM", + "XWING/HKDF_SHA256/AES_256_GCM", + "XWING/HKDF_SHA256/CHACHA20POLY1305"}; // This only needs to test the wrapper functionality as the implementation and client // APIs are tested elsewhere. What we're looking for is that HPKE SPI instances returned diff --git a/platform/src/test/java/org/conscrypt/CertBlocklistTest.java b/platform/src/test/java/org/conscrypt/CertBlocklistTest.java index 31341ae9a..e37730b3b 100644 --- a/platform/src/test/java/org/conscrypt/CertBlocklistTest.java +++ b/platform/src/test/java/org/conscrypt/CertBlocklistTest.java @@ -16,9 +16,13 @@ package org.conscrypt; +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.conscrypt.metrics.NoopStatsLog; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -29,6 +33,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Collection; import javax.net.ssl.X509TrustManager; @@ -102,6 +107,44 @@ public void testBlocklistedIntermediateFallback() throws Exception { assertUntrusted(chain, getTrustManager(blocklistedCa)); } + static class FakeStatsLog extends NoopStatsLog { + public ArrayList entries = new ArrayList<>(); + + @Override + public void reportBlocklistHit(CertBlocklistEntry entry) { + entries.add(entry); + } + } + + @Test + public void isPublicKeyBlockListed_hitBlocklist_hitIsReported() throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + FakeStatsLog fakeMetrics = new FakeStatsLog(); + CertBlocklist blocklist = + new CertBlocklistImpl.Builder().loadAllDefaults().setMetrics(fakeMetrics).build(); + + blocklist.isPublicKeyBlockListed(blocklistedCa.getPublicKey()); + + assertThat(fakeMetrics.entries).hasSize(1); + CertBlocklistEntry entry = fakeMetrics.entries.get(0); + assertThat(entry.getOrigin()).isEqualTo(CertBlocklistEntry.Origin.SHA1_TEST); + assertThat(entry.getIndex()).isEqualTo(0); + } + + @Test + public void isPublicKeyBlockListed_hitBlocklistTwiceWithSameKey_hitIsReportedTwice() + throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + FakeStatsLog fakeMetrics = new FakeStatsLog(); + CertBlocklist blocklist = + new CertBlocklistImpl.Builder().loadAllDefaults().setMetrics(fakeMetrics).build(); + + blocklist.isPublicKeyBlockListed(blocklistedCa.getPublicKey()); + blocklist.isPublicKeyBlockListed(blocklistedCa.getPublicKey()); + + assertThat(fakeMetrics.entries).hasSize(2); + } + private static X509Certificate loadCertificate(String file) throws Exception { return loadCertificates(file)[0]; } diff --git a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java index 1a05c21a8..0ebb329ce 100644 --- a/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java +++ b/platform/src/test/java/org/conscrypt/TlsDeprecationTest.java @@ -39,6 +39,8 @@ @RunWith(JUnit4.class) public class TlsDeprecationTest { + // android-add: test rule for switching target SDK version to 36. + @Test @TargetSdkVersion(36) public void test_SSLSocket_SSLv3Unsupported_36() throws Exception { diff --git a/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java b/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java index 1af49b4ef..a27812a2e 100644 --- a/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java +++ b/platform/src/test/java/org/conscrypt/ct/LogStoreImplTest.java @@ -24,25 +24,24 @@ import org.conscrypt.OpenSSLKey; import org.conscrypt.metrics.NoopStatsLog; +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.security.PublicKey; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; +import java.util.function.Supplier; @RunWith(JUnit4.class) public class LogStoreImplTest { + /** FakeStatsLog captures the events being reported */ static class FakeStatsLog extends NoopStatsLog { public ArrayList states = new ArrayList(); @@ -76,13 +75,31 @@ public PolicyCompliance doesResultConformToPolicy(VerificationResult result, } }; - @Test - public void test_loadValidLogList() throws Exception { - // clang-format off - String content = "" + + /* Time supplier that can be set to any arbitrary time */ + static class TimeSupplier implements Supplier { + private long currentTimeInMs; + + TimeSupplier(long currentTimeInMs) { + this.currentTimeInMs = currentTimeInMs; + } + + @Override + public Long get() { + return currentTimeInMs; + } + + public void setCurrentTimeInMs(long currentTimeInMs) { + this.currentTimeInMs = currentTimeInMs; + } + } + + private static final long JAN2024 = 1704103200000L; + private static final long JAN2022 = 1641031200000L; + // clang-format off + static final String validLogList = "" + "{" + " \"version\": \"1.1\"," + -" \"log_list_timestamp\": \"2024-01-01T11:55:12Z\"," + +" \"log_list_timestamp\": 1704070861000," + " \"operators\": [" + " {" + " \"name\": \"Operator 1\"," + @@ -96,12 +113,12 @@ public void test_loadValidLogList() throws Exception { " \"mmd\": 86400," + " \"state\": {" + " \"usable\": {" + -" \"timestamp\": \"2022-11-01T18:54:00Z\"" + +" \"timestamp\": 1667328840000" + " }" + " }," + " \"temporal_interval\": {" + -" \"start_inclusive\": \"2024-01-01T00:00:00Z\"," + -" \"end_exclusive\": \"2025-01-01T00:00:00Z\"" + +" \"start_inclusive\": 1704070861000," + +" \"end_exclusive\": 1735693261000" + " }" + " }," + " {" + @@ -112,12 +129,12 @@ public void test_loadValidLogList() throws Exception { " \"mmd\": 86400," + " \"state\": {" + " \"usable\": {" + -" \"timestamp\": \"2023-11-26T12:00:00Z\"" + +" \"timestamp\": 1700960461000" + " }" + " }," + " \"temporal_interval\": {" + -" \"start_inclusive\": \"2025-01-01T00:00:00Z\"," + -" \"end_exclusive\": \"2025-07-01T00:00:00Z\"" + +" \"start_inclusive\": 1735693261000," + +" \"end_exclusive\": 1751331661000" + " }" + " }" + " ]" + @@ -134,26 +151,58 @@ public void test_loadValidLogList() throws Exception { " \"mmd\": 86400," + " \"state\": {" + " \"usable\": {" + -" \"timestamp\": \"2022-11-30T17:00:00Z\"" + +" \"timestamp\": 1669770061000" + " }" + " }," + " \"temporal_interval\": {" + -" \"start_inclusive\": \"2024-01-01T00:00:00Z\"," + -" \"end_exclusive\": \"2025-01-01T00:00:00Z\"" + +" \"start_inclusive\": 1704070861000," + +" \"end_exclusive\": 1735693261000" + +" }" + +" }" + +" ]," + +" \"tiled_logs\": [" + +" {" + +" \"description\": \"Operator 2 'Test2025' log\"," + +" \"log_id\": \"DleUvPOuqT4zGyyZB7P3kN+bwj1xMiXdIaklrGHFTiE=\"," + +" \"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB/we6GOO/xwxivy4HhkrYFAAPo6e2nc346Wo2o2U+GvoPWSPJz91s/xrEvA3Bk9kWHUUXVZS5morFEzsgdHqPg==\"," + +" \"submission_url\": \"https://operator2.example.com/tiled/test2025\"," + +" \"monitoring_url\": \"https://operator2.exmaple.com/tiled_monitor/test2025\"," + +" \"mmd\": 86400," + +" \"state\": {" + +" \"usable\": {" + +" \"timestamp\": 1667328840000" + +" }" + +" }," + +" \"temporal_interval\": {" + +" \"start_inclusive\": 1767225600000," + +" \"end_exclusive\": 1782864000000" + " }" + " }" + " ]" + " }" + " ]" + "}"; - // clang-format on + // clang-format on - FakeStatsLog metrics = new FakeStatsLog(); - File logList = writeFile(content); - LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList.toPath(), metrics); + Path grandparentDir; + Path parentDir; + Path logList; - assertNull("A null logId should return null", store.getKnownLog(null)); + @After + public void tearDown() throws Exception { + if (logList != null) { + Files.deleteIfExists(logList); + Files.deleteIfExists(parentDir); + Files.deleteIfExists(grandparentDir); + } + } + @Test + public void loadValidLogList_returnsCompliantState() throws Exception { + FakeStatsLog metrics = new FakeStatsLog(); + logList = writeLogList(validLogList); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); byte[] pem = ("-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHblsqctplMVc5ramA7vSuNxUQxcomQwGAVAdnW" + "TAWUYr" @@ -161,55 +210,163 @@ public void test_loadValidLogList() throws Exception { + "\n-----END PUBLIC KEY-----\n") .getBytes(US_ASCII); ByteArrayInputStream is = new ByteArrayInputStream(pem); - LogInfo log1 = new LogInfo.Builder() .setPublicKey(OpenSSLKey.fromPublicKeyPemInputStream(is).getPublicKey()) - .setDescription("Operator 1 'Test2024' log") - .setUrl("https://operator1.example.com/logs/test2024/") + .setType(LogInfo.TYPE_RFC6962) .setState(LogInfo.STATE_USABLE, 1667328840000L) .setOperator("Operator 1") .build(); byte[] log1Id = Base64.getDecoder().decode("7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZs="); + + assertNull("A null logId should return null", store.getKnownLog(/* logId= */ null)); assertEquals("An existing logId should be returned", log1, store.getKnownLog(log1Id)); - assertEquals("One metric update should be emitted", metrics.states.size(), 1); + assertEquals("One metric update should be emitted", 1, metrics.states.size()); assertEquals("The metric update for log list state should be compliant", - metrics.states.get(0), LogStore.State.COMPLIANT); + LogStore.State.COMPLIANT, metrics.states.get(0)); } @Test - public void test_loadMalformedLogList() throws Exception { + public void loadMalformedLogList_returnsMalformedState() throws Exception { FakeStatsLog metrics = new FakeStatsLog(); String content = "}}"; - File logList = writeFile(content); - LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList.toPath(), metrics); + logList = writeLogList(content); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); + + assertEquals("The log state should be malformed", LogStore.State.MALFORMED, + store.getState()); + assertEquals("One metric update should be emitted", 1, metrics.states.size()); + assertEquals("The metric update for log list state should be malformed", + LogStore.State.MALFORMED, metrics.states.get(0)); + } - assertEquals("The log state should be malformed", store.getState(), - LogStore.State.MALFORMED); - assertEquals("One metric update should be emitted", metrics.states.size(), 1); + @Test + public void loadFutureLogList_returnsMalformedState() throws Exception { + FakeStatsLog metrics = new FakeStatsLog(); + logList = writeLogList(validLogList); // The logs are usable from 2024 onwards. + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2022); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); + + assertEquals("The log state should be malformed", LogStore.State.MALFORMED, + store.getState()); + assertEquals("One metric update should be emitted", 1, metrics.states.size()); assertEquals("The metric update for log list state should be malformed", - metrics.states.get(0), LogStore.State.MALFORMED); + LogStore.State.MALFORMED, metrics.states.get(0)); } @Test - public void test_loadMissingLogList() throws Exception { + public void loadMissingLogList_returnsNotFoundState() throws Exception { FakeStatsLog metrics = new FakeStatsLog(); - File logList = new File("does_not_exist"); - LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList.toPath(), metrics); + Path missingLogList = Paths.get("missing_dir", "missing_subdir", "does_not_exist_log_list"); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024); + LogStore store = + new LogStoreImpl(alwaysCompliantStorePolicy, missingLogList, metrics, fakeTime); - assertEquals("The log state should be not found", store.getState(), - LogStore.State.NOT_FOUND); - assertEquals("One metric update should be emitted", metrics.states.size(), 1); + assertEquals("The log state should be not found", LogStore.State.NOT_FOUND, + store.getState()); + assertEquals("One metric update should be emitted", 1, metrics.states.size()); assertEquals("The metric update for log list state should be not found", - metrics.states.get(0), LogStore.State.NOT_FOUND); + LogStore.State.NOT_FOUND, metrics.states.get(0)); } - private File writeFile(String content) throws IOException { - File file = File.createTempFile("test", null); - file.deleteOnExit(); - try (FileWriter fw = new FileWriter(file)) { - fw.write(content); - } + @Test + public void loadMissingAndThenFoundLogList_logListIsLoaded() throws Exception { + // Arrange + FakeStatsLog metrics = new FakeStatsLog(); + // Allocate a temporary file path and delete it. We keep the temporary + // path so that we can add a valid log list later on. + logList = writeLogList(""); + Files.deleteIfExists(logList); + Files.deleteIfExists(parentDir); + Files.deleteIfExists(grandparentDir); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); + assertEquals("The log state should be not found", LogStore.State.NOT_FOUND, + store.getState()); + + // Act + Files.createDirectory(grandparentDir); + Files.createDirectory(parentDir); + Files.write(logList, validLogList.getBytes()); + + // Assert + // 5min < 10min, we should not check the log list yet. + fakeTime.setCurrentTimeInMs(JAN2024 + 5L * 60 * 1000); + assertEquals("The log state should be not found", LogStore.State.NOT_FOUND, + store.getState()); + + // 12min, the log list should be reloadable. + fakeTime.setCurrentTimeInMs(JAN2024 + 12L * 60 * 1000); + assertEquals("The log state should be compliant", LogStore.State.COMPLIANT, + store.getState()); + } + + @Test + public void loadMissingThenTimeTravelBackwardsAndThenFoundLogList_logListIsLoaded() + throws Exception { + FakeStatsLog metrics = new FakeStatsLog(); + // Allocate a temporary file path and delete it. We keep the temporary + // path so that we can add a valid log list later on. + logList = writeLogList(""); + Files.deleteIfExists(logList); + Files.deleteIfExists(parentDir); + Files.deleteIfExists(grandparentDir); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024 + 100L * 60 * 1000); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); + assertEquals("The log state should be not found", LogStore.State.NOT_FOUND, + store.getState()); + + Files.createDirectory(grandparentDir); + Files.createDirectory(parentDir); + Files.write(logList, validLogList.getBytes()); + // Move back in time. + fakeTime.setCurrentTimeInMs(JAN2024); + + assertEquals("The log state should be compliant", LogStore.State.COMPLIANT, + store.getState()); + } + + @Test + public void loadExistingAndThenRemovedLogList_logListIsNotFound() throws Exception { + FakeStatsLog metrics = new FakeStatsLog(); + logList = writeLogList(validLogList); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); + assertEquals("The log should be loaded", LogStore.State.COMPLIANT, store.getState()); + + Files.delete(logList); + // 12min, the log list should be reloadable. + fakeTime.setCurrentTimeInMs(JAN2024 + 12L * 60 * 1000); + + assertEquals("The log should have been refreshed", LogStore.State.NOT_FOUND, + store.getState()); + } + + @Test + public void loadExistingLogListAndThenMoveDirectory_logListIsNotFound() throws Exception { + FakeStatsLog metrics = new FakeStatsLog(); + logList = writeLogList(validLogList); + TimeSupplier fakeTime = new TimeSupplier(/* currentTimeInMs= */ JAN2024); + LogStore store = new LogStoreImpl(alwaysCompliantStorePolicy, logList, metrics, fakeTime); + assertEquals("The log should be loaded", LogStore.State.COMPLIANT, store.getState()); + + Path oldParentDir = parentDir; + parentDir = grandparentDir.resolve("more_current"); + Files.move(oldParentDir, parentDir); + logList = parentDir.resolve("log_list.json"); + // 12min, the log list should be reloadable. + fakeTime.setCurrentTimeInMs(JAN2024 + 12L * 60 * 1000); + + assertEquals("The log should have been refreshed", LogStore.State.NOT_FOUND, + store.getState()); + } + + private Path writeLogList(String content) throws IOException { + grandparentDir = Files.createTempDirectory("v1"); + parentDir = Files.createDirectory(grandparentDir.resolve("current")); + Path file = Files.createFile(parentDir.resolve("log_list.json")); + Files.write(file, content.getBytes()); return file; } } diff --git a/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java b/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java index cc3b40b42..7a1a13712 100644 --- a/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java +++ b/platform/src/test/java/org/conscrypt/ct/PolicyImplTest.java @@ -36,9 +36,11 @@ public class PolicyImplTest { private static final String OPERATOR2 = "operator 2"; private static LogInfo usableOp1Log1; private static LogInfo usableOp1Log2; + private static LogInfo usableStaticOp1Log; private static LogInfo retiredOp1LogOld; private static LogInfo retiredOp1LogNew; private static LogInfo usableOp2Log; + private static LogInfo usableStaticOp2Log; private static LogInfo retiredOp2Log; private static SignedCertificateTimestamp embeddedSCT; private static SignedCertificateTimestamp ocspSCT; @@ -89,37 +91,49 @@ public static void setUp() { */ usableOp1Log1 = new LogInfo.Builder() .setPublicKey(new FakePublicKey(new byte[] {0x01})) - .setUrl("") + .setType(LogInfo.TYPE_RFC6962) .setOperator(OPERATOR1) .setState(LogInfo.STATE_USABLE, JAN2022) .build(); usableOp1Log2 = new LogInfo.Builder() .setPublicKey(new FakePublicKey(new byte[] {0x02})) - .setUrl("") + .setType(LogInfo.TYPE_RFC6962) .setOperator(OPERATOR1) .setState(LogInfo.STATE_USABLE, JAN2022) .build(); + usableStaticOp1Log = new LogInfo.Builder() + .setPublicKey(new FakePublicKey(new byte[] {0x07})) + .setType(LogInfo.TYPE_STATIC_CT_API) + .setOperator(OPERATOR1) + .setState(LogInfo.STATE_USABLE, JAN2022) + .build(); retiredOp1LogOld = new LogInfo.Builder() .setPublicKey(new FakePublicKey(new byte[] {0x03})) - .setUrl("") + .setType(LogInfo.TYPE_RFC6962) .setOperator(OPERATOR1) .setState(LogInfo.STATE_RETIRED, JAN2022) .build(); retiredOp1LogNew = new LogInfo.Builder() .setPublicKey(new FakePublicKey(new byte[] {0x06})) - .setUrl("") + .setType(LogInfo.TYPE_RFC6962) .setOperator(OPERATOR1) .setState(LogInfo.STATE_RETIRED, JUN2023) .build(); usableOp2Log = new LogInfo.Builder() .setPublicKey(new FakePublicKey(new byte[] {0x04})) - .setUrl("") + .setType(LogInfo.TYPE_RFC6962) .setOperator(OPERATOR2) .setState(LogInfo.STATE_USABLE, JAN2022) .build(); + usableStaticOp2Log = new LogInfo.Builder() + .setPublicKey(new FakePublicKey(new byte[] {0x08})) + .setType(LogInfo.TYPE_STATIC_CT_API) + .setOperator(OPERATOR2) + .setState(LogInfo.STATE_USABLE, JAN2022) + .build(); retiredOp2Log = new LogInfo.Builder() .setPublicKey(new FakePublicKey(new byte[] {0x05})) - .setUrl("") + .setType(LogInfo.TYPE_RFC6962) .setOperator(OPERATOR2) .setState(LogInfo.STATE_RETIRED, JAN2022) .build(); @@ -373,6 +387,71 @@ public void invalidOneEmbeddedOneOCSPVerificationResult() throws Exception { p.doesResultConformToPolicyAt(result, leaf, JAN2024)); } + public void validVerificationResultPartialStatic(SignedCertificateTimestamp sct) + throws Exception { + PolicyImpl p = new PolicyImpl(); + + VerifiedSCT vsct1 = new VerifiedSCT.Builder(sct) + .setStatus(VerifiedSCT.Status.VALID) + .setLogInfo(usableOp1Log1) + .build(); + + VerifiedSCT vsct2 = new VerifiedSCT.Builder(sct) + .setStatus(VerifiedSCT.Status.VALID) + .setLogInfo(usableStaticOp2Log) + .build(); + + VerificationResult result = new VerificationResult(); + result.add(vsct1); + result.add(vsct2); + + X509Certificate leaf = new FakeX509Certificate(); + assertEquals("Two valid SCTs from different operators", PolicyCompliance.COMPLY, + p.doesResultConformToPolicyAt(result, leaf, JAN2024)); + } + + @Test + public void validEmbeddedVerificationResultPartialStatic() throws Exception { + validVerificationResultPartialStatic(embeddedSCT); + } + + @Test + public void validOCSPVerificationResultPartialStatic() throws Exception { + validVerificationResultPartialStatic(ocspSCT); + } + + public void invalidTwoSctsAllStatic(SignedCertificateTimestamp sct) throws Exception { + PolicyImpl p = new PolicyImpl(); + + VerifiedSCT vsct1 = new VerifiedSCT.Builder(sct) + .setStatus(VerifiedSCT.Status.VALID) + .setLogInfo(usableStaticOp1Log) + .build(); + + VerifiedSCT vsct2 = new VerifiedSCT.Builder(sct) + .setStatus(VerifiedSCT.Status.VALID) + .setLogInfo(usableStaticOp2Log) + .build(); + + VerificationResult result = new VerificationResult(); + result.add(vsct1); + result.add(vsct2); + + X509Certificate leaf = new FakeX509Certificate(); + assertEquals("Two static SCTs", PolicyCompliance.NO_RFC6962_LOG, + p.doesResultConformToPolicyAt(result, leaf, JAN2024)); + } + + @Test + public void invalidEmbeddedTwoSctsAllStaticsVerificationResult() throws Exception { + invalidTwoSctsAllStatic(embeddedSCT); + } + + @Test + public void invalidOCSPTwoSctsAllStaticsVerificationResult() throws Exception { + invalidTwoSctsAllStatic(ocspSCT); + } + @Test public void validRecentLogStore() throws Exception { PolicyImpl p = new PolicyImpl(); diff --git a/testing/src/main/java/org/conscrypt/java/security/StandardNames.java b/testing/src/main/java/org/conscrypt/java/security/StandardNames.java index e9bf0437c..b7504ef55 100644 --- a/testing/src/main/java/org/conscrypt/java/security/StandardNames.java +++ b/testing/src/main/java/org/conscrypt/java/security/StandardNames.java @@ -23,6 +23,7 @@ import org.conscrypt.TestUtils; import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -155,17 +156,24 @@ private static void provideSslContextEnabledProtocols(String algorithm, TLSVersi provideCipherPaddings("AES", new String[] {"PKCS7Padding"}); } - provideSslContextEnabledProtocols("TLS", TLSVersion.TLSv1, TLSVersion.TLSv13); - provideSslContextEnabledProtocols("TLSv1", TLSVersion.TLSv1, TLSVersion.TLSv12); - provideSslContextEnabledProtocols("TLSv1.1", TLSVersion.TLSv1, TLSVersion.TLSv12); - provideSslContextEnabledProtocols("TLSv1.2", TLSVersion.TLSv1, TLSVersion.TLSv12); - provideSslContextEnabledProtocols("TLSv1.3", TLSVersion.TLSv1, TLSVersion.TLSv13); - provideSslContextEnabledProtocols("Default", TLSVersion.TLSv1, TLSVersion.TLSv13); + if (TestUtils.isTlsV1Supported()) { + provideSslContextEnabledProtocols("TLS", TLSVersion.TLSv1, TLSVersion.TLSv13); + provideSslContextEnabledProtocols("TLSv1", TLSVersion.TLSv1, TLSVersion.TLSv12); + provideSslContextEnabledProtocols("TLSv1.1", TLSVersion.TLSv1, TLSVersion.TLSv12); + provideSslContextEnabledProtocols("TLSv1.2", TLSVersion.TLSv1, TLSVersion.TLSv12); + provideSslContextEnabledProtocols("TLSv1.3", TLSVersion.TLSv1, TLSVersion.TLSv13); + provideSslContextEnabledProtocols("Default", TLSVersion.TLSv1, TLSVersion.TLSv13); + } else { + provideSslContextEnabledProtocols("TLS", TLSVersion.TLSv12, TLSVersion.TLSv13); + provideSslContextEnabledProtocols("TLSv1.2", TLSVersion.TLSv12, TLSVersion.TLSv12); + provideSslContextEnabledProtocols("TLSv1.3", TLSVersion.TLSv12, TLSVersion.TLSv13); + provideSslContextEnabledProtocols("Default", TLSVersion.TLSv12, TLSVersion.TLSv13); + } } public static final String SSL_CONTEXT_PROTOCOLS_DEFAULT = "Default"; - public static final Set SSL_CONTEXT_PROTOCOLS = new HashSet(Arrays.asList( - SSL_CONTEXT_PROTOCOLS_DEFAULT, "TLS", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3")); + public static final Set SSL_CONTEXT_PROTOCOLS = new HashSet( + Arrays.asList(SSL_CONTEXT_PROTOCOLS_DEFAULT, "TLS", "TLSv1.2", "TLSv1.3")); public static final Set SSL_CONTEXT_PROTOCOLS_WITH_DEFAULT_CONFIG = new HashSet(Arrays.asList(SSL_CONTEXT_PROTOCOLS_DEFAULT, "TLS", "TLSv1.3")); // Deprecated TLS protocols... May or may not be present or enabled. @@ -191,8 +199,15 @@ private static void provideSslContextEnabledProtocols(String algorithm, TLSVersi } } - public static final Set SSL_SOCKET_PROTOCOLS = - new HashSet(Arrays.asList("TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3")); + public static final Set SSL_SOCKET_PROTOCOLS = new HashSet<>(); + static { + SSL_SOCKET_PROTOCOLS.add("TLSv1.2"); + SSL_SOCKET_PROTOCOLS.add("TLSv1.3"); + if (TestUtils.isTlsV1Supported()) { + SSL_SOCKET_PROTOCOLS.add("TLSv1"); + SSL_SOCKET_PROTOCOLS.add("TLSv1.1"); + } + } private enum TLSVersion { SSLv3("SSLv3"), @@ -421,11 +436,23 @@ public static void assertValidCipherSuites(String[] cipherSuites) { assertValidCipherSuites(CIPHER_SUITES, cipherSuites); } + private static final List OPTIONAL_CIPHER_SUITES = + Arrays.asList("SSL_RSA_WITH_3DES_EDE_CBC_SHA"); + /** * Assert that the provided list of cipher suites matches the supported list. */ public static void assertSupportedCipherSuites(String[] cipherSuites) { - assertSupportedCipherSuites(CIPHER_SUITES, cipherSuites); + List filteredCipherSuites = new ArrayList<>(); + for (String cipherSuite : cipherSuites) { + if (OPTIONAL_CIPHER_SUITES.contains(cipherSuite)) { + continue; + } + filteredCipherSuites.add(cipherSuite); + } + String[] filteredCipherSuitesArray = new String[filteredCipherSuites.size()]; + filteredCipherSuites.toArray(filteredCipherSuitesArray); + assertSupportedCipherSuites(CIPHER_SUITES, filteredCipherSuitesArray); } /** diff --git a/testing/src/main/java/org/conscrypt/testing/OpaqueProvider.java b/testing/src/main/java/org/conscrypt/testing/OpaqueProvider.java index b614f0087..b88bb2875 100644 --- a/testing/src/main/java/org/conscrypt/testing/OpaqueProvider.java +++ b/testing/src/main/java/org/conscrypt/testing/OpaqueProvider.java @@ -305,6 +305,7 @@ private OpaqueDelegatingRSAPrivateKey(RSAPrivateKey delegate) { this.delegate = delegate; } + // g3-add: getParams() @Override public AlgorithmParameterSpec getParams() { return delegate.getParams();