diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro deleted file mode 100644 index 8f7b25c61..000000000 --- a/android/proguard-rules.pro +++ /dev/null @@ -1,29 +0,0 @@ -# Add project specific ProGuard rules here. -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Many of the Conscrypt classes are referenced indirectly via JNI or -# reflection. -# This could probably be tightened up, but this will get it building for now. -# TODO(kroot): Need anything special to prevent obfuscation? --keep class org.conscrypt.** { *; } - -# Backward compatibility code. --dontnote libcore.io.Libcore --dontnote org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey --dontnote org.apache.harmony.security.utils.AlgNameMapper --dontnote sun.security.x509.AlgorithmId - --dontwarn android.util.StatsEvent --dontwarn dalvik.system.BlockGuard --dontwarn dalvik.system.BlockGuard$Policy --dontwarn dalvik.system.CloseGuard --dontwarn com.android.org.conscrypt.AbstractConscryptSocket --dontwarn com.android.org.conscrypt.ConscryptFileDescriptorSocket --dontwarn com.android.org.conscrypt.OpenSSLSocketImpl --dontwarn com.android.org.conscrypt.SSLParametersImpl --dontwarn org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl --dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl diff --git a/android/src/main/java/org/conscrypt/ConscryptStatsLog.java b/android/src/main/java/org/conscrypt/ConscryptStatsLog.java new file mode 100644 index 000000000..19ab61631 --- /dev/null +++ b/android/src/main/java/org/conscrypt/ConscryptStatsLog.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.conscrypt; + +/** + * Stub class for logging statistics events. + */ +public class ConscryptStatsLog { + public static final int TLS_HANDSHAKE_REPORTED = 0; + + public static void write(int code, boolean arg1, int arg2, int arg3, int arg4) {} +} diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java index d60977bc2..00636f6e1 100644 --- a/android/src/main/java/org/conscrypt/Platform.java +++ b/android/src/main/java/org/conscrypt/Platform.java @@ -255,8 +255,12 @@ private static void setSSLParametersOnImpl(SSLParameters params, SSLParametersIm Method m_getUseCipherSuitesOrder = params.getClass().getMethod("getUseCipherSuitesOrder"); impl.setUseCipherSuitesOrder((boolean) m_getUseCipherSuitesOrder.invoke(params)); - Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); - impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); + try { + Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); + impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); + } catch (NoSuchMethodException | IllegalArgumentException e) { + // Do nothing. + } } public static void setSSLParameters( @@ -327,8 +331,12 @@ private static void getSSLParametersFromImpl(SSLParameters params, SSLParameters params.getClass().getMethod("setUseCipherSuitesOrder", boolean.class); m_setUseCipherSuitesOrder.invoke(params, impl.getUseCipherSuitesOrder()); - Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); - setNamedGroupsMethod.invoke(params, (Object[]) impl.getNamedGroups()); + try { + Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); + setNamedGroupsMethod.invoke(params, (Object) impl.getNamedGroups()); + } catch (NoSuchMethodException | IllegalArgumentException e) { + // Do nothing. + } } public static void getSSLParameters( 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..4595435f7 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java @@ -511,8 +511,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 +542,9 @@ public final void close() throws IOException { if (in != null) { in.release(); } + if (out != null) { + out.release(); + } } } } @@ -625,7 +626,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 +662,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 +682,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 +787,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 +801,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 +823,9 @@ void release() { if (allocatedBuffer != null) { allocatedBuffer.release(); } + if (allocatedSocketBuffer != null) { + allocatedSocketBuffer.release(); + } } } 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..7e3c69691 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/google3/src/google3_jniload.cc b/google3/src/google3_jniload.cc new file mode 100644 index 000000000..75777e69d --- /dev/null +++ b/google3/src/google3_jniload.cc @@ -0,0 +1,39 @@ + +// JNI_OnLoad for Google3. + +#include +#include +#include +#include +#include + +using ::conscrypt::CompatibilityCloseMonitor; +using ::conscrypt::NativeCrypto; + +// This replicates functionality in jniload.cc for a couple of reasons: +// * This needs to be declared 'extern "C"', and jniload's isn't. +// * We can't include jniload.cc in our .so target at all due to +// symbol collisions in some targets that also compile in jniload.cc +// in a separate compilation unit(!). +// +// // TODO(b/408158702): Some refactoring of jniload.cc in upstream conscrypt +// could remove the need for all or some of this. +extern "C" JNIEXPORT jint JNI_OnLoad_conscrypt_google3(JavaVM* vm, + void* reserved) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) != JNI_OK) { + CONSCRYPT_LOG_ERROR("Could not get JNIEnv"); + return JNI_ERR; + } + + // Initialize the JNI constants. + conscrypt::jniutil::init(vm, env); + + // Register all of the native JNI methods. + NativeCrypto::registerNativeMethods(env); + + // Perform static initialization of the close monitor (if required on this + // platform). + CompatibilityCloseMonitor::init(); + return JNI_VERSION_1_8; +} diff --git a/google3/src/java7/java/org/conscrypt/Java8EngineSocket.java b/google3/src/java7/java/org/conscrypt/Java8EngineSocket.java new file mode 100644 index 000000000..70267f009 --- /dev/null +++ b/google3/src/java7/java/org/conscrypt/Java8EngineSocket.java @@ -0,0 +1,39 @@ +package org.conscrypt; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +/** + * Shim for Java 7-only google3 builds that does nothing. + */ +final class Java8EngineSocket extends ConscryptEngineSocket { + Java8EngineSocket(SSLParametersImpl sslParameters) throws IOException { + super(sslParameters); + } + + Java8EngineSocket(String hostname, int port, SSLParametersImpl sslParameters) + throws IOException { + super(hostname, port, sslParameters); + } + + Java8EngineSocket(InetAddress address, int port, SSLParametersImpl sslParameters) + throws IOException { + super(address, port, sslParameters); + } + + Java8EngineSocket(String hostname, int port, InetAddress clientAddress, int clientPort, + SSLParametersImpl sslParameters) throws IOException { + super(hostname, port, clientAddress, clientPort, sslParameters); + } + + Java8EngineSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort, + SSLParametersImpl sslParameters) throws IOException { + super(address, port, clientAddress, clientPort, sslParameters); + } + + Java8EngineSocket(Socket socket, String hostname, int port, boolean autoClose, + SSLParametersImpl sslParameters) throws IOException { + super(socket, hostname, port, autoClose, sslParameters); + } +} diff --git a/google3/src/java7/java/org/conscrypt/Java8FileDescriptorSocket.java b/google3/src/java7/java/org/conscrypt/Java8FileDescriptorSocket.java new file mode 100644 index 000000000..6e5eb1678 --- /dev/null +++ b/google3/src/java7/java/org/conscrypt/Java8FileDescriptorSocket.java @@ -0,0 +1,39 @@ +package org.conscrypt; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +/** + * Shim for Java 7-only google3 builds that does nothing. + */ +final class Java8FileDescriptorSocket extends ConscryptFileDescriptorSocket { + Java8FileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException { + super(sslParameters); + } + + Java8FileDescriptorSocket(String hostname, int port, SSLParametersImpl sslParameters) + throws IOException { + super(hostname, port, sslParameters); + } + + Java8FileDescriptorSocket(InetAddress address, int port, SSLParametersImpl sslParameters) + throws IOException { + super(address, port, sslParameters); + } + + Java8FileDescriptorSocket(String hostname, int port, InetAddress clientAddress, int clientPort, + SSLParametersImpl sslParameters) throws IOException { + super(hostname, port, clientAddress, clientPort, sslParameters); + } + + Java8FileDescriptorSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort, + SSLParametersImpl sslParameters) throws IOException { + super(address, port, clientAddress, clientPort, sslParameters); + } + + Java8FileDescriptorSocket(Socket socket, String hostname, int port, boolean autoClose, + SSLParametersImpl sslParameters) throws IOException { + super(socket, hostname, port, autoClose, sslParameters); + } +} diff --git a/google3/src/java7/java/org/conscrypt/Java8PlatformUtil.java b/google3/src/java7/java/org/conscrypt/Java8PlatformUtil.java new file mode 100644 index 000000000..936544f2f --- /dev/null +++ b/google3/src/java7/java/org/conscrypt/Java8PlatformUtil.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 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 javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +/** + * Shim for Java 7-only google3 builds that does nothing. + */ +final class Java8PlatformUtil { + static void setSSLParameters( + SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + static void getSSLParameters( + SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + static void setSSLParameters( + SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { + throw new AssertionError("Java 7 builds should never call this class."); + } + static void getSSLParameters( + SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + static SSLEngine wrapEngine(ConscryptEngine engine) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + static SSLEngine unwrapEngine(SSLEngine engine) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + static SSLSocket unwrapSocket(SSLSocket socket) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + static SSLSession wrapSSLSession(ConscryptSession sslSession) { + throw new AssertionError("Java 7 builds should never call this class."); + } + + private Java8PlatformUtil() {} +} diff --git a/google3/src/java7/java/org/conscrypt/TrustManagerFactoryImpl.java b/google3/src/java7/java/org/conscrypt/TrustManagerFactoryImpl.java new file mode 100644 index 000000000..fe73f32a5 --- /dev/null +++ b/google3/src/java7/java/org/conscrypt/TrustManagerFactoryImpl.java @@ -0,0 +1,27 @@ +package org.conscrypt; + +import java.security.KeyStore; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactorySpi; + +/** + * Shim for Java 7-only google3 builds that does nothing. + */ +public final class TrustManagerFactoryImpl extends TrustManagerFactorySpi { + public TrustManagerFactoryImpl() { + throw new UnsupportedOperationException("Cannot use TrustManagerFactoryImpl on Java 7."); + } + + protected TrustManager[] engineGetTrustManagers() { + throw new UnsupportedOperationException("Cannot use TrustManagerFactoryImpl on Java 7."); + } + + protected void engineInit(KeyStore ks) { + throw new UnsupportedOperationException("Cannot use TrustManagerFactoryImpl on Java 7."); + } + + protected void engineInit(ManagerFactoryParameters spec) { + throw new UnsupportedOperationException("Cannot use TrustManagerFactoryImpl on Java 7."); + } +} diff --git a/google3/src/main/java/org/conscrypt/NativeCryptoJni.java b/google3/src/main/java/org/conscrypt/NativeCryptoJni.java new file mode 100644 index 000000000..f8e23f1fd --- /dev/null +++ b/google3/src/main/java/org/conscrypt/NativeCryptoJni.java @@ -0,0 +1,30 @@ +package org.conscrypt; + +import com.google.common.flogger.GoogleLogger; +import com.google.common.jni.JniLoader; +import com.google.devtools.java.launcher.LauncherExport; +import com.google.wrappers.base.GoogleInit; + +class NativeCryptoJni { + private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); + + public static void init() { + logger.atFine().log("Initializing Conscrypt"); + if (GoogleInit.isNativeDepsInLauncher()) { + // This is the typical case. The Google3 launcher will allow us to load like this. + // Note: it is important that we do not try `JniLoader.loadLibrary` if we are using the + // Google3 launcher as this might lead to C++ undefined behavior. + LauncherExport.loadFromLauncher("conscrypt_google3"); + logger.atInfo().log("Successfully loaded Conscrypt"); + } else { + // This is the uncommon case. Since we are not using NativeDepsInLauncher we need to call + // `JniLoader.loadLibrary`. + logger.atInfo().log("Attempting to load conscrypt via Shared Library JNI loading."); + JniLoader.loadLibrary("third_party/java_src/conscrypt/libconscrypt_openjdk_jni.so"); + GoogleInit.initializeLibraryWithSystemPropertyFlags(NativeCrypto.class.getName()); + logger.atInfo().log("Successfully loaded conscrypt via Shared Library JNI loading."); + } + } + + private NativeCryptoJni() {} +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c02..000000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ 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/platform/proguard-rules.pro b/platform/proguard-rules.pro deleted file mode 100644 index 3bc75b2df..000000000 --- a/platform/proguard-rules.pro +++ /dev/null @@ -1,26 +0,0 @@ -# Add project specific ProGuard rules here. -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Many of the Conscrypt classes are referenced indirectly via JNI or -# reflection. -# This could probably be tightened up, but this will get it building for now. -# TODO(kroot): Need anything special to prevent obfuscation? --keep class org.conscrypt.** { *; } - -# Backward compatibility code. --dontnote libcore.io.Libcore --dontnote org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey --dontnote org.apache.harmony.security.utils.AlgNameMapper --dontnote sun.security.x509.AlgorithmId - --dontwarn dalvik.system.BlockGuard --dontwarn dalvik.system.BlockGuard$Policy --dontwarn dalvik.system.CloseGuard --dontwarn com.android.org.conscrypt.AbstractConscryptSocket --dontwarn com.android.org.conscrypt.ConscryptFileDescriptorSocket --dontwarn com.android.org.conscrypt.OpenSSLSocketImpl --dontwarn org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java index ec208cde6..f9a799449 100644 --- a/platform/src/main/java/org/conscrypt/Platform.java +++ b/platform/src/main/java/org/conscrypt/Platform.java @@ -167,8 +167,12 @@ static void setSSLParameters( impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm()); impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder()); - Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); - impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); + try { + Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); + impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); + } catch (NoSuchMethodException | IllegalArgumentException e) { + // Do nothing. + } List serverNames = params.getServerNames(); if (serverNames != null) { @@ -187,8 +191,12 @@ static void getSSLParameters( params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm()); params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder()); - Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); - setNamedGroupsMethod.invoke(params, (Object[]) impl.getNamedGroups()); + try { + 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(socket.getHostname())) { params.setServerNames(Collections.singletonList( @@ -202,9 +210,12 @@ static void setSSLParameters( impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm()); impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder()); - Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); - impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); - + try { + Method getNamedGroupsMethod = params.getClass().getMethod("getNamedGroups"); + impl.setNamedGroups((String[]) getNamedGroupsMethod.invoke(params)); + } catch (NoSuchMethodException | IllegalArgumentException e) { + // Do nothing. + } List serverNames = params.getServerNames(); if (serverNames != null) { for (SNIServerName serverName : serverNames) { @@ -222,9 +233,12 @@ static void getSSLParameters( params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm()); params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder()); - Method setNamedGroupsMethod = params.getClass().getMethod("setNamedGroups", String[].class); - setNamedGroupsMethod.invoke(params, (Object[]) impl.getNamedGroups()); - + try { + 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())));