From b285e3bbe2c9cdb8d9d35f4113ba92dd51db6018 Mon Sep 17 00:00:00 2001 From: Conscrypt Team Date: Wed, 14 Jan 2026 11:52:49 +0000 Subject: [PATCH] Project import generated by Copybara. PiperOrigin-RevId: 856143029 --- android/src/main/AndroidManifest.xml | 8 +- .../src/main/java/org/conscrypt/Platform.java | 29 +- .../java/org/conscrypt/ConscryptEngine.java | 361 +++++++++++------- .../org/conscrypt/ConscryptEngineSocket.java | 61 ++- .../java/org/conscrypt/TrustManagerImpl.java | 12 +- .../org/conscrypt/HostnameVerifierTest.java | 2 +- .../java/security/MessageDigestTest.java | 16 +- .../javax/crypto/XdhKeyFactoryTest.java | 2 +- .../javax/net/ssl/HttpsURLConnectionTest.java | 15 +- .../SSLSocketVersionCompatibilityTest.java | 2 +- .../org/conscrypt/ConscryptAndroidSuite.java | 156 ++++++++ .../org/conscrypt/ConscryptSocketTest.java | 2 +- .../conscrypt/OpenSSLX509CertificateTest.java | 2 +- .../conscrypt/ServerSessionContextTest.java | 2 +- .../src/main/java/org/conscrypt/Platform.java | 8 +- .../conscrypt/java/security/CpuFeatures.java | 3 +- 16 files changed, 472 insertions(+), 209 deletions(-) create mode 100644 openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index bbc40563e..4907eeaa6 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,7 @@ - - + + + + + diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java index c1bcd7828..a778f8f99 100644 --- a/android/src/main/java/org/conscrypt/Platform.java +++ b/android/src/main/java/org/conscrypt/Platform.java @@ -23,18 +23,8 @@ import android.os.SystemClock; import android.system.Os; import android.util.Log; - import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; - -import org.conscrypt.NativeCrypto; -import org.conscrypt.ct.CertificateTransparency; -import org.conscrypt.metrics.CertificateTransparencyVerificationReason; -import org.conscrypt.metrics.NoopStatsLog; -import org.conscrypt.metrics.Source; -import org.conscrypt.metrics.StatsLog; -import org.conscrypt.metrics.StatsLogImpl; - import java.io.FileDescriptor; import java.io.IOException; import java.lang.reflect.Constructor; @@ -50,7 +40,6 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; import java.security.Security; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -61,7 +50,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; - import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; @@ -71,11 +59,16 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.StandardConstants; import javax.net.ssl.X509TrustManager; +import org.conscrypt.ct.CertificateTransparency; +import org.conscrypt.metrics.CertificateTransparencyVerificationReason; +import org.conscrypt.metrics.NoopStatsLog; +import org.conscrypt.metrics.Source; +import org.conscrypt.metrics.StatsLog; +import org.conscrypt.metrics.StatsLogImpl; -/** - * Platform-specific methods for unbundled Android. - */ +/** Platform-specific methods for unbundled Android. */ @Internal +@SuppressLint("DiscouragedPrivateApi") final public class Platform { private static final String TAG = "Conscrypt"; private static boolean DEPRECATED_TLS_V1 = true; @@ -332,8 +325,7 @@ private static void getSSLParametersFromImpl(SSLParameters params, SSLParameters m_setUseCipherSuitesOrder.invoke(params, impl.getUseCipherSuitesOrder()); try { - Method setNamedGroupsMethod = - params.getClass().getMethod("setNamedGroups", String[].class); + Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); setNamedGroupsMethod.invoke(params, (Object) impl.getNamedGroups()); } catch (NoSuchMethodException | IllegalArgumentException e) { // Do nothing. @@ -787,7 +779,8 @@ public static SSLSession wrapSSLSession(ExternalSession sslSession) { return sslSession; } - public static String getOriginalHostNameFromInetAddress(InetAddress addr) { + @SuppressWarnings("SoonBlockedPrivateApi") + public static String getOriginalHostNameFromInetAddress(InetAddress addr) { if (Build.VERSION.SDK_INT > 27) { try { Method getHolder = InetAddress.class.getDeclaredMethod("holder"); diff --git a/common/src/main/java/org/conscrypt/ConscryptEngine.java b/common/src/main/java/org/conscrypt/ConscryptEngine.java index 70c39fea6..208612509 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngine.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngine.java @@ -55,7 +55,6 @@ import static org.conscrypt.SSLUtils.calculateOutNetBufSize; import static org.conscrypt.SSLUtils.toSSLHandshakeException; -import static java.lang.Math.max; import static java.lang.Math.min; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED; @@ -83,6 +82,8 @@ import java.security.interfaces.ECKey; import java.security.spec.ECParameterSpec; import java.util.Arrays; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; import javax.crypto.SecretKey; import javax.net.ssl.SSLEngine; @@ -115,16 +116,11 @@ final class ConscryptEngine extends AbstractConscryptEngine new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0); private static BufferAllocator defaultBufferAllocator = null; + private static final BufferAllocator DEFAULT_POOL = new PoolingBufferAllocator(); private final SSLParametersImpl sslParameters; - private BufferAllocator bufferAllocator = defaultBufferAllocator; - - /** - * A lazy-created direct buffer used as a bridge between heap buffers provided by the - * application and JNI. This avoids the overhead of calling JNI with heap buffers. - * Used only when no {@link #bufferAllocator} has been provided. - */ - private ByteBuffer lazyDirectBuffer; + private BufferAllocator bufferAllocator = + defaultBufferAllocator != null ? defaultBufferAllocator : DEFAULT_POOL; /** * Hostname used with the TLS extension SNI hostname. @@ -229,7 +225,7 @@ static void setDefaultBufferAllocator(BufferAllocator bufferAllocator) { * has been explicitly set. */ static BufferAllocator getDefaultBufferAllocator() { - return defaultBufferAllocator; + return defaultBufferAllocator != null ? defaultBufferAllocator : DEFAULT_POOL; } @Override @@ -1037,16 +1033,8 @@ private int writePlaintextDataDirect(ByteBuffer src, int pos, int len) throws IO private int writePlaintextDataHeap(ByteBuffer src, int pos, int len) throws IOException { AllocatedBuffer allocatedBuffer = null; try { - final ByteBuffer buffer; - if (bufferAllocator != null) { - allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); - buffer = allocatedBuffer.nioBuffer(); - } else { - // We don't have a buffer allocator, but we don't want to send a heap - // buffer to JNI. So lazy-create a direct buffer that we will use from now - // on to copy plaintext data. - buffer = getOrCreateLazyDirectBuffer(); - } + allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); + final ByteBuffer buffer = allocatedBuffer.nioBuffer(); // Copy the data to the direct buffer. int limit = src.limit(); @@ -1099,16 +1087,8 @@ private int readPlaintextDataHeap(ByteBuffer dst, int len) throws IOException, CertificateException { AllocatedBuffer allocatedBuffer = null; try { - final ByteBuffer buffer; - if (bufferAllocator != null) { - allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); - buffer = allocatedBuffer.nioBuffer(); - } else { - // We don't have a buffer allocator, but we don't want to send a heap - // buffer to JNI. So lazy-create a direct buffer that we will use from now - // on to copy plaintext data. - buffer = getOrCreateLazyDirectBuffer(); - } + allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); + final ByteBuffer buffer = allocatedBuffer.nioBuffer(); // Read the data to the direct buffer. int bytesToRead = min(len, buffer.remaining()); @@ -1168,15 +1148,8 @@ private int writeEncryptedDataHeap(ByteBuffer src, int pos, int len) throws IOEx AllocatedBuffer allocatedBuffer = null; try { final ByteBuffer buffer; - if (bufferAllocator != null) { - allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); - buffer = allocatedBuffer.nioBuffer(); - } else { - // We don't have a buffer allocator, but we don't want to send a heap - // buffer to JNI. So lazy-create a direct buffer that we will use from now - // on to copy encrypted packets. - buffer = getOrCreateLazyDirectBuffer(); - } + allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); + buffer = allocatedBuffer.nioBuffer(); int limit = src.limit(); int bytesToCopy = min(min(limit - pos, len), buffer.remaining()); @@ -1202,15 +1175,6 @@ private int writeEncryptedDataHeap(ByteBuffer src, int pos, int len) throws IOEx } } - private ByteBuffer getOrCreateLazyDirectBuffer() { - if (lazyDirectBuffer == null) { - lazyDirectBuffer = ByteBuffer.allocateDirect( - max(SSL3_RT_MAX_PLAIN_LENGTH, SSL3_RT_MAX_PACKET_SIZE)); - } - lazyDirectBuffer.clear(); - return lazyDirectBuffer; - } - private long directByteBufferAddress(ByteBuffer directBuffer, int pos) { return NativeCrypto.getDirectBufferAddress(directBuffer) + pos; } @@ -1289,16 +1253,8 @@ private int readEncryptedDataDirect(ByteBuffer dst, int pos, int len) throws IOE private int readEncryptedDataHeap(ByteBuffer dst, int len) throws IOException { AllocatedBuffer allocatedBuffer = null; try { - final ByteBuffer buffer; - if (bufferAllocator != null) { - allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); - buffer = allocatedBuffer.nioBuffer(); - } else { - // We don't have a buffer allocator, but we don't want to send a heap - // buffer to JNI. So lazy-create a direct buffer that we will use from now - // on to copy encrypted packets. - buffer = getOrCreateLazyDirectBuffer(); - } + allocatedBuffer = bufferAllocator.allocateDirectBuffer(len); + final ByteBuffer buffer = allocatedBuffer.nioBuffer(); int bytesToRead = min(len, buffer.remaining()); int bytesRead = readEncryptedDataDirect(buffer, 0, bytesToRead); @@ -1442,95 +1398,95 @@ public SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, B int bytesProduced = 0; int bytesConsumed = 0; if (dataLength > 0) { - // Try and find a single buffer to send, e.g. the first non-empty buffer has - // more than enough data remaining to fill a TLS record. Otherwise copy as much - // data as possible from the source buffers to fill a record. Note the we can't - // mark the data as consumed until we see how much the TLS layer actually consumes. - boolean isCopy = false; - ByteBuffer outputBuffer = - BufferUtils.getBufferLargerThan(srcs, SSL3_RT_MAX_PLAIN_LENGTH); - if (outputBuffer == null) { - // The buffer by getOrCreateLazyDirectBuffer() is also used by - // writePlainTextDataHeap(), but by filling it here the write path will go via - // writePlainTextDataDirect() and the cost will be approximately the same, - // especially if compacting multiple non-direct buffers into a single - // direct one. - // TODO(): use bufferAllocator if set. - // https://github.com/google/conscrypt/issues/974 - outputBuffer = BufferUtils.copyNoConsume( - srcs, getOrCreateLazyDirectBuffer(), SSL3_RT_MAX_PLAIN_LENGTH); - isCopy = true; - } - final SSLEngineResult pendingNetResult; - // Write plaintext application data to the SSL engine - int result = writePlaintextData( - outputBuffer, min(SSL3_RT_MAX_PLAIN_LENGTH, outputBuffer.remaining())); - if (result > 0) { - bytesConsumed = result; - if (isCopy) { - // Data was a copy, so mark it as consumed in the original buffers. - BufferUtils.consume(srcs, bytesConsumed); + AllocatedBuffer allocatedBuffer = null; + try { + boolean isCopy = false; + ByteBuffer outputBuffer + = BufferUtils.getBufferLargerThan(srcs, SSL3_RT_MAX_PLAIN_LENGTH); + if (outputBuffer == null) { + allocatedBuffer = bufferAllocator.allocateDirectBuffer(dataLength); + outputBuffer = BufferUtils.copyNoConsume( + srcs, allocatedBuffer.nioBuffer(), dataLength); + isCopy = true; } - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - if (pendingNetResult != null) { - if (pendingNetResult.getStatus() != OK) { - return pendingNetResult; + final SSLEngineResult pendingNetResult; + // Write plaintext application data to the SSL engine + int result = writePlaintextData(outputBuffer, + min(SSL3_RT_MAX_PLAIN_LENGTH, outputBuffer.remaining())); + + if (result > 0) { + bytesConsumed = result; + if (isCopy) { + // Data was a copy, so mark it as consumed in the original buffers. + BufferUtils.consume(srcs, bytesConsumed); + } + + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + if (pendingNetResult != null) { + if (pendingNetResult.getStatus() != OK) { + return pendingNetResult; + } + bytesProduced = pendingNetResult.bytesProduced(); + } + } else { + int sslError = ssl.getError(result); + switch (sslError) { + case SSL_ERROR_ZERO_RETURN: + // This means the connection was shutdown correctly, close inbound + // and outbound + closeAll(); + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null ? pendingNetResult + : CLOSED_NOT_HANDSHAKING; + case SSL_ERROR_WANT_READ: + // If there is no pending data to read from BIO we should go back to + // event loop and try + // to read more data [1]. It is also possible that event loop will + // detect the socket + // has been closed. [1] + // https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null + ? pendingNetResult + : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, + bytesConsumed, bytesProduced); + case SSL_ERROR_WANT_WRITE: + // SSL_ERROR_WANT_WRITE typically means that the underlying + // transport is not writable + // and we should set the "want write" flag on the selector and try + // again when the + // underlying transport is writable [1]. However we are not directly + // writing to the + // underlying transport and instead writing to a BIO buffer. The + // OpenSsl documentation + // says we should do the following [1]: + // + // "When using a buffering BIO, like a BIO pair, data must be + // written into or retrieved + // out of the BIO before being able to continue." + // + // So we attempt to drain the BIO buffer below, but if there is no + // data this condition + // is undefined and we assume their is a fatal error with the + // openssl engine and close. + // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pendingNetResult = readPendingBytesFromBIO( + dst, bytesConsumed, bytesProduced, handshakeStatus); + return pendingNetResult != null ? pendingNetResult + : NEED_WRAP_CLOSED; + default: + // Everything else is considered as error + closeAll(); + throw newSslExceptionWithMessage("SSL_write: error " + sslError); } - bytesProduced = pendingNetResult.bytesProduced(); } - } else { - int sslError = ssl.getError(result); - switch (sslError) { - case SSL_ERROR_ZERO_RETURN: - // This means the connection was shutdown correctly, close inbound - // and outbound - closeAll(); - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null ? pendingNetResult - : CLOSED_NOT_HANDSHAKING; - case SSL_ERROR_WANT_READ: - // If there is no pending data to read from BIO we should go back to - // event loop and try - // to read more data [1]. It is also possible that event loop will - // detect the socket - // has been closed. [1] - // https://www.openssl.org/docs/manmaster/man3/SSL_write.html - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null - ? pendingNetResult - : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, - bytesConsumed, bytesProduced); - case SSL_ERROR_WANT_WRITE: - // SSL_ERROR_WANT_WRITE typically means that the underlying - // transport is not writable - // and we should set the "want write" flag on the selector and try - // again when the - // underlying transport is writable [1]. However we are not directly - // writing to the - // underlying transport and instead writing to a BIO buffer. The - // OpenSsl documentation - // says we should do the following [1]: - // - // "When using a buffering BIO, like a BIO pair, data must be - // written into or retrieved - // out of the BIO before being able to continue." - // - // So we attempt to drain the BIO buffer below, but if there is no - // data this condition - // is undefined and we assume their is a fatal error with the - // openssl engine and close. - // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html - pendingNetResult = readPendingBytesFromBIO( - dst, bytesConsumed, bytesProduced, handshakeStatus); - return pendingNetResult != null ? pendingNetResult : NEED_WRAP_CLOSED; - default: - // Everything else is considered as error - closeAll(); - throw newSslExceptionWithMessage("SSL_write: error " + sslError); + } finally { + if (allocatedBuffer != null) { + allocatedBuffer.release(); } } } @@ -1840,4 +1796,123 @@ private void transitionTo(int newState) { // Update the state this.state = newState; } + + /** + * A BufferAllocator that pools and reuses direct ByteBuffers using + * size-segregated buckets and a global pool size limit. + */ + public static final class PoolingBufferAllocator extends BufferAllocator { + private static final int SMALL_BUFFER_SIZE = 2048; + private static final int MEDIUM_BUFFER_SIZE = 4096; + private static final int LARGE_BUFFER_SIZE = 16709; + + private static final int MAX_TOTAL_POOL_SIZE = 64; + + private final AtomicInteger totalPooledBuffers = new AtomicInteger(0); + + private final ConcurrentLinkedQueue poolSmall = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue poolMedium = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue poolLarge = new ConcurrentLinkedQueue<>(); + + @Override + public AllocatedBuffer allocateDirectBuffer(int capacity) { + final ConcurrentLinkedQueue pool; + final int bufferSize; + ByteBuffer buffer; + + if (capacity <= SMALL_BUFFER_SIZE) { + pool = poolSmall; + bufferSize = SMALL_BUFFER_SIZE; + } else if (capacity <= MEDIUM_BUFFER_SIZE) { + pool = poolMedium; + bufferSize = MEDIUM_BUFFER_SIZE; + } else if (capacity <= LARGE_BUFFER_SIZE) { + pool = poolLarge; + bufferSize = LARGE_BUFFER_SIZE; + } else { + buffer = ByteBuffer.allocateDirect(capacity); + return new PooledAllocatedBuffer(buffer); + } + + buffer = pool.poll(); + + if (buffer != null) { + totalPooledBuffers.decrementAndGet(); + buffer.clear(); + } else { + buffer = ByteBuffer.allocateDirect(bufferSize); + } + + return new PooledAllocatedBuffer(buffer); + } + + @Override + public AllocatedBuffer allocateHeapBuffer(int capacity) { + return new AllocatedBuffer() { + final ByteBuffer buffer = ByteBuffer.allocate(capacity); + + @Override + public ByteBuffer nioBuffer() { + return buffer; + } + + @Override + public AllocatedBuffer release() { + return this; + } + }; + } + + /** + * Returns a buffer to the correct pool, only if the global limit isn't reached. + */ + private void releaseBuffer(ByteBuffer buffer) { + int capacity = buffer.capacity(); + final ConcurrentLinkedQueue pool; + + if (capacity == SMALL_BUFFER_SIZE) { + pool = poolSmall; + } else if (capacity == MEDIUM_BUFFER_SIZE) { + pool = poolMedium; + } else if (capacity == LARGE_BUFFER_SIZE) { + pool = poolLarge; + } else { + return; + } + + if (totalPooledBuffers.getAndUpdate((i) -> i >= MAX_TOTAL_POOL_SIZE ? i : i + 1) + >= MAX_TOTAL_POOL_SIZE) { + // Pool is full, let the buffer be garbage collected. + return; + } + buffer.clear(); + pool.offer(buffer); + } + + /** + * An AllocatedBuffer that returns itself to the pool on release. + */ + private final class PooledAllocatedBuffer extends AllocatedBuffer { + private final ByteBuffer buffer; + private boolean released; + + PooledAllocatedBuffer(ByteBuffer buffer) { + this.buffer = buffer; + } + + @Override + public ByteBuffer nioBuffer() { + return buffer; + } + + @Override + public AllocatedBuffer release() { + if (!released) { + released = true; + releaseBuffer(buffer); + } + return this; + } + } + } } diff --git a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java index db1376d68..0f3996417 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java @@ -16,6 +16,8 @@ package org.conscrypt; +import static javax.net.ssl.SSLEngineResult.Status.CLOSED; +import static javax.net.ssl.SSLEngineResult.Status.OK; import static org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED; import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED; import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED; @@ -23,11 +25,6 @@ import static org.conscrypt.SSLUtils.EngineStates.STATE_READY; import static org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH; -import static javax.net.ssl.SSLEngineResult.Status.CLOSED; -import static javax.net.ssl.SSLEngineResult.Status.OK; - -import org.conscrypt.metrics.StatsLog; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -39,7 +36,6 @@ import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; - import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -50,6 +46,7 @@ import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; +import org.conscrypt.metrics.StatsLog; /** * Implements crypto handling by delegating to {@link ConscryptEngine}. @@ -138,12 +135,13 @@ public void onHandshakeFinished() { return engine; } - // Returns a trust manager that delegates to the given trust manager, but maps SSLEngine - // references to the given ConscryptEngineSocket. Our internal engine will call - // the SSLEngine-receiving methods, but our callers expect the SSLSocket-receiving - // methods to get called. - private static X509TrustManager getDelegatingTrustManager(final X509TrustManager delegate, - final ConscryptEngineSocket socket) { + // Returns a trust manager that delegates to the given trust manager, but maps SSLEngine + // references to the given ConscryptEngineSocket. Our internal engine will call + // the SSLEngine-receiving methods, but our callers expect the SSLSocket-receiving + // methods to get called. + @SuppressWarnings("CustomX509TrustManager") + private static X509TrustManager getDelegatingTrustManager( + final X509TrustManager delegate, final ConscryptEngineSocket socket) { if (delegate instanceof X509ExtendedTrustManager) { final X509ExtendedTrustManager extendedDelegate = (X509ExtendedTrustManager) delegate; return new X509ExtendedTrustManager() { @@ -511,8 +509,6 @@ public final void setWantClientAuth(boolean want) { @Override @SuppressWarnings("UnsynchronizedOverridesSynchronized") public final void close() throws IOException { - // TODO: Close SSL sockets using a background thread so they close gracefully. - if (stateLock == null) { // Constructor failed, e.g. superclass constructor called close() return; @@ -544,6 +540,9 @@ public final void close() throws IOException { if (in != null) { in.release(); } + if (out != null) { + out.release(); + } } } } @@ -625,7 +624,7 @@ private void waitForHandshake() throws IOException { private void drainOutgoingQueue() { try { - while (engine.pendingOutboundEncryptedBytes() > 0) { + while (engine.pendingOutboundEncryptedBytes() > 0 && out != null) { out.writeInternal(EMPTY_BUFFER); // Always flush handshake frames immediately. out.flushInternal(); @@ -661,10 +660,18 @@ private final class SSLOutputStream extends OutputStream { private final Object writeLock = new Object(); private final ByteBuffer target; private final int targetArrayOffset; + private final AllocatedBuffer allocatedTargetBuffer; private OutputStream socketOutputStream; SSLOutputStream() { - target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); + if (bufferAllocator != null) { + allocatedTargetBuffer = bufferAllocator.allocateHeapBuffer( + engine.getSession().getPacketBufferSize()); + target = allocatedTargetBuffer.nioBuffer(); + } else { + allocatedTargetBuffer = null; + target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); + } targetArrayOffset = target.arrayOffset(); } @@ -673,6 +680,14 @@ public void close() throws IOException { ConscryptEngineSocket.this.close(); } + void release() { + synchronized (writeLock) { + if (allocatedTargetBuffer != null) { + allocatedTargetBuffer.release(); + } + } + } + @Override public void write(int b) throws IOException { waitForHandshake(); @@ -770,6 +785,7 @@ private final class SSLInputStream extends InputStream { private final ByteBuffer fromSocket; private final int fromSocketArrayOffset; private final AllocatedBuffer allocatedBuffer; + private final AllocatedBuffer allocatedSocketBuffer; private InputStream socketInputStream; SSLInputStream() { @@ -783,7 +799,15 @@ private final class SSLInputStream extends InputStream { } // Initially fromEngine.remaining() == 0. fromEngine.flip(); - fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); + + if (bufferAllocator != null) { + allocatedSocketBuffer = bufferAllocator.allocateHeapBuffer( + engine.getSession().getPacketBufferSize()); + fromSocket = allocatedSocketBuffer.nioBuffer(); + } else { + allocatedSocketBuffer = null; + fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); + } fromSocketArrayOffset = fromSocket.arrayOffset(); } @@ -797,6 +821,9 @@ void release() { if (allocatedBuffer != null) { allocatedBuffer.release(); } + if (allocatedSocketBuffer != null) { + allocatedSocketBuffer.release(); + } } } diff --git a/common/src/main/java/org/conscrypt/TrustManagerImpl.java b/common/src/main/java/org/conscrypt/TrustManagerImpl.java index 732c88dd0..2899dbc71 100644 --- a/common/src/main/java/org/conscrypt/TrustManagerImpl.java +++ b/common/src/main/java/org/conscrypt/TrustManagerImpl.java @@ -62,7 +62,6 @@ import java.util.List; import java.util.Set; import java.util.logging.Logger; - import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; @@ -71,14 +70,14 @@ import javax.net.ssl.X509ExtendedTrustManager; /** - * - * TrustManager implementation. The implementation is based on CertPathValidator - * PKIX and CertificateFactory X509 implementations. This implementations should - * be provided by some certification provider. + * TrustManager implementation. The implementation is based on CertPathValidator PKIX and + * CertificateFactory X509 implementations. This implementations should be provided by some + * certification provider. * * @see javax.net.ssl.X509ExtendedTrustManager */ @Internal +@SuppressWarnings("CustomX509TrustManager") public final class TrustManagerImpl extends X509ExtendedTrustManager { private static final Logger logger = Logger.getLogger(TrustManagerImpl.class.getName()); @@ -431,7 +430,8 @@ private byte[] getTlsSctDataFromSession(SSLSession session) { byte[] data = null; try { - Method m_getTlsSctData = session.getClass().getDeclaredMethod("getPeerSignedCertificateTimestamp"); + Method m_getTlsSctData = + session.getClass().getDeclaredMethod("getPeerSignedCertificateTimestamp"); m_getTlsSctData.setAccessible(true); Object rawData = m_getTlsSctData.invoke(session); if (rawData instanceof byte[]) { diff --git a/common/src/test/java/org/conscrypt/HostnameVerifierTest.java b/common/src/test/java/org/conscrypt/HostnameVerifierTest.java index a369ecd1e..15b259ae6 100644 --- a/common/src/test/java/org/conscrypt/HostnameVerifierTest.java +++ b/common/src/test/java/org/conscrypt/HostnameVerifierTest.java @@ -31,7 +31,7 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; -import org.junit.Ignore; +// g3-add: import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; 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 7e533840c..40cf95694 100644 --- a/common/src/test/java/org/conscrypt/java/security/MessageDigestTest.java +++ b/common/src/test/java/org/conscrypt/java/security/MessageDigestTest.java @@ -169,13 +169,15 @@ private static Map getExpectations(String algorithm) throws Exce TestUtils.decodeHex( "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6" + "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26")); - putExpectation("SHAKE128-256", INPUT_EMPTY, - TestUtils.decodeHex( - "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26")); - putExpectation("SHAKE256-512", INPUT_EMPTY, - TestUtils.decodeHex( - "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f" - + "d75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be")); + putExpectation("SHAKE128-256", + INPUT_EMPTY, + TestUtils.decodeHex( + "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26")); + putExpectation("SHAKE256-512", + INPUT_EMPTY, + TestUtils.decodeHex( + "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f" + + "d75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be")); // Regression test for a SHA-1 problem with inputs larger than 256 MiB. http://b/4501620 // In mid-2013 this takes 3 minutes even on the host, so let's not run it on devices. 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 465557f5f..98f8ff3bc 100644 --- a/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyFactoryTest.java +++ b/common/src/test/java/org/conscrypt/javax/crypto/XdhKeyFactoryTest.java @@ -30,7 +30,7 @@ import org.conscrypt.OpenSSLX25519PublicKey; import org.conscrypt.TestUtils; import org.conscrypt.XdhKeySpec; -import org.junit.Ignore; +// g3-add: import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java index 2dbb056ce..eabf7c641 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java @@ -122,7 +122,8 @@ public void failedUrlConnect() throws Exception { Future future = executor.submit(server.run(op)); HttpsURLConnection connection = server.tlsConnection("/file"); - // g3-add: broken HTTPS hostname verification + // google3-added: broken HTTPS hostname verification: b/266061083 + connection.setHostnameVerifier(new FakeHostnameVerifier()); int response = connection.getResponseCode(); assertEquals(404, response); @@ -138,7 +139,8 @@ public void successfulUrlConnect() throws Exception { Future future = executor.submit(server.run(op)); HttpsURLConnection connection = server.tlsConnection("/file"); - // g3-add: broken HTTPS hostname verification + // google3-added: broken HTTPS hostname verification: b/266061083 + connection.setHostnameVerifier(new FakeHostnameVerifier()); int response = connection.getResponseCode(); assertEquals(200, response); @@ -193,7 +195,14 @@ public void urlConnectTimeout() throws Exception { } return null; }); - future.get(2 * timeoutMillis, TimeUnit.MILLISECONDS); + try { + future.get(2 * timeoutMillis, TimeUnit.MILLISECONDS); + } catch (ExecutionException e) { + // google3 changed, DO NOT UPSTREAM: Currently no way to reliably generate a connection + // timeout on Forge, so just skip this test if we get a SocketException for now. + assumeFalse(e.getCause() instanceof SocketException); + throw e.getCause(); + } } @Test diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java index b7775905b..dfe139974 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java @@ -52,7 +52,7 @@ import org.conscrypt.tlswire.record.TlsRecord; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; +// g3-add: import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java new file mode 100644 index 000000000..5701ff937 --- /dev/null +++ b/openjdk/src/test/java/org/conscrypt/ConscryptAndroidSuite.java @@ -0,0 +1,156 @@ +/* + * 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. + * 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 static org.conscrypt.TestUtils.installConscryptAsDefaultProvider; + +import org.conscrypt.ct.SerializationTest; +import org.conscrypt.ct.VerifierTest; +import org.conscrypt.java.security.AlgorithmParameterGeneratorTestDH; +import org.conscrypt.java.security.AlgorithmParameterGeneratorTestDSA; +import org.conscrypt.java.security.AlgorithmParametersPSSTest; +import org.conscrypt.java.security.AlgorithmParametersTestAES; +import org.conscrypt.java.security.AlgorithmParametersTestDES; +import org.conscrypt.java.security.AlgorithmParametersTestDESede; +import org.conscrypt.java.security.AlgorithmParametersTestDH; +import org.conscrypt.java.security.AlgorithmParametersTestDSA; +import org.conscrypt.java.security.AlgorithmParametersTestEC; +import org.conscrypt.java.security.AlgorithmParametersTestGCM; +import org.conscrypt.java.security.AlgorithmParametersTestOAEP; +import org.conscrypt.java.security.KeyFactoryTestDH; +import org.conscrypt.java.security.KeyFactoryTestDSA; +import org.conscrypt.java.security.KeyFactoryTestEC; +import org.conscrypt.java.security.KeyFactoryTestRSACrt; +import org.conscrypt.java.security.KeyPairGeneratorTest; +import org.conscrypt.java.security.KeyPairGeneratorTestDH; +import org.conscrypt.java.security.KeyPairGeneratorTestDSA; +import org.conscrypt.java.security.KeyPairGeneratorTestRSA; +import org.conscrypt.java.security.KeyPairGeneratorTestXDH; +import org.conscrypt.java.security.MessageDigestTest; +import org.conscrypt.java.security.SignatureTest; +import org.conscrypt.java.security.cert.CertificateFactoryTest; +import org.conscrypt.java.security.cert.X509CRLTest; +import org.conscrypt.java.security.cert.X509CertificateTest; +import org.conscrypt.javax.crypto.AeadCipherTest; +import org.conscrypt.javax.crypto.CipherBasicsTest; +import org.conscrypt.javax.crypto.ECDHKeyAgreementTest; +import org.conscrypt.javax.crypto.KeyGeneratorTest; +import org.conscrypt.javax.crypto.ScryptTest; +import org.conscrypt.javax.crypto.XDHKeyAgreementTest; +import org.conscrypt.javax.crypto.XdhKeyFactoryTest; +import org.conscrypt.javax.crypto.XdhKeyTest; +import org.conscrypt.javax.net.ssl.KeyManagerFactoryTest; +import org.conscrypt.javax.net.ssl.KeyStoreBuilderParametersTest; +import org.conscrypt.javax.net.ssl.SNIHostNameTest; +import org.conscrypt.javax.net.ssl.SSLParametersTest; +import org.conscrypt.javax.net.ssl.X509KeyManagerTest; +import org.conscrypt.metrics.CipherSuiteTest; +import org.conscrypt.metrics.OptionalMethodTest; +import org.conscrypt.metrics.ProtocolTest; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + // org.conscrypt tests + AddressUtilsTest.class, + ApplicationProtocolSelectorAdapterTest.class, + ArrayUtilsTest.class, + CertPinManagerTest.class, + ChainStrengthAnalyzerTest.class, + DuckTypedHpkeSpiTest.class, + EdDsaTest.class, + ExposedByteArrayOutputStreamTest.class, + FileClientSessionCacheTest.class, + HostnameVerifierTest.class, + HpkeContextTest.class, + HpkeContextRecipientTest.class, + HpkeContextSenderTest.class, + HpkeSuiteTest.class, + HpkeTestVectorsTest.class, + KeySpecUtilTest.class, + MlDsaTest.class, + NativeCryptoArgTest.class, + NativeCryptoTest.class, + NativeRefTest.class, + NativeSslSessionTest.class, + OpenSSLKeyTest.class, + OpenSSLX509CertificateTest.class, + SSLUtilsTest.class, + SlhDsaTest.class, + TestSessionBuilderTest.class, + TrustManagerImplTest.class, + X25519Test.class, + XwingTest.class, + // org.conscrypt.ct tests + VerifierTest.class, + SerializationTest.class, + // java.security tests + CertificateFactoryTest.class, + X509CertificateTest.class, + X509CRLTest.class, + AlgorithmParameterGeneratorTestDH.class, + AlgorithmParameterGeneratorTestDSA.class, + AlgorithmParametersPSSTest.class, + AlgorithmParametersTestAES.class, + AlgorithmParametersTestDES.class, + AlgorithmParametersTestDESede.class, + AlgorithmParametersTestDH.class, + AlgorithmParametersTestDSA.class, + AlgorithmParametersTestEC.class, + AlgorithmParametersTestGCM.class, + AlgorithmParametersTestOAEP.class, + BufferUtilsTest.class, + CipherSuiteTest.class, + KeyFactoryTestDH.class, + KeyFactoryTestDSA.class, + KeyFactoryTestEC.class, + KeyFactoryTestRSACrt.class, + KeyPairGeneratorTest.class, + KeyPairGeneratorTestDH.class, + KeyPairGeneratorTestDSA.class, + KeyPairGeneratorTestRSA.class, + KeyPairGeneratorTestXDH.class, + MessageDigestTest.class, + SignatureTest.class, + // javax.crypto tests + AeadCipherTest.class, + CipherBasicsTest.class, + MacTest.class, + ECDHKeyAgreementTest.class, + KeyGeneratorTest.class, + XDHKeyAgreementTest.class, + XdhKeyFactoryTest.class, + XdhKeyTest.class, + // javax.net.ssl tests + KeyManagerFactoryTest.class, + KeyStoreBuilderParametersTest.class, + OptionalMethodTest.class, + ProtocolTest.class, + ScryptTest.class, + SNIHostNameTest.class, + SSLParametersTest.class, + VeryBasicHttpServerTest.class, + X509KeyManagerTest.class, +}) +public class ConscryptAndroidSuite { + @BeforeClass + public static void setupStatic() { + installConscryptAsDefaultProvider(); + } +} diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java index aefbed621..2254cc3ee 100644 --- a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java +++ b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java @@ -61,7 +61,7 @@ import javax.net.ssl.TrustManagerFactory; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; +// g3-add: import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java index 546bb7f30..43ae10222 100644 --- a/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java +++ b/openjdk/src/test/java/org/conscrypt/OpenSSLX509CertificateTest.java @@ -37,7 +37,7 @@ import java.util.Arrays; import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException; import org.junit.Assume; -import org.junit.Ignore; +// g3-add: import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java index 9941d7ff0..ae3419d1d 100644 --- a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java +++ b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java @@ -39,7 +39,7 @@ int size(ServerSessionContext context) { int count = 0; Enumeration ids = context.getIds(); while (ids.hasMoreElements()) { - ids.nextElement(); + Object unused = ids.nextElement(); count++; } return count; diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java index 716999899..f9a799449 100644 --- a/platform/src/main/java/org/conscrypt/Platform.java +++ b/platform/src/main/java/org/conscrypt/Platform.java @@ -192,8 +192,7 @@ static void getSSLParameters( params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder()); try { - Method setNamedGroupsMethod = - params.getClass().getMethod("setNamedGroups", String[].class); + Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); setNamedGroupsMethod.invoke(params, (Object) impl.getNamedGroups()); } catch (NoSuchMethodException | IllegalArgumentException e) { // Do nothing. @@ -217,7 +216,6 @@ static void setSSLParameters( } catch (NoSuchMethodException | IllegalArgumentException e) { // Do nothing. } - List serverNames = params.getServerNames(); if (serverNames != null) { for (SNIServerName serverName : serverNames) { @@ -236,13 +234,11 @@ static void getSSLParameters( params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder()); try { - Method setNamedGroupsMethod = - params.getClass().getMethod("setNamedGroups", String[].class); + Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); setNamedGroupsMethod.invoke(params, (Object) impl.getNamedGroups()); } catch (NoSuchMethodException | IllegalArgumentException e) { // Do nothing. } - if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) { params.setServerNames(Collections.singletonList( new SNIHostName(engine.getHostname()))); diff --git a/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java b/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java index 4e238da92..60779593d 100644 --- a/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java +++ b/testing/src/main/java/org/conscrypt/java/security/CpuFeatures.java @@ -111,7 +111,8 @@ private static List getListFromCpuinfo(String fieldName) { return Arrays.asList(features.split("\\s")); } - private static List getCpuFeaturesMac() { + @SuppressWarnings("RuntimeExec") + private static List getCpuFeaturesMac() { try { StringBuilder output = new StringBuilder(); Process proc = Runtime.getRuntime().exec("sysctl -a");