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/platform/src/main/java/org/conscrypt/NativeCryptoJni.java b/android/src/main/java/org/conscrypt/ConscryptStatsLog.java similarity index 67% rename from platform/src/main/java/org/conscrypt/NativeCryptoJni.java rename to android/src/main/java/org/conscrypt/ConscryptStatsLog.java index ce28c8039..19ab61631 100644 --- a/platform/src/main/java/org/conscrypt/NativeCryptoJni.java +++ b/android/src/main/java/org/conscrypt/ConscryptStatsLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 The Android Open Source Project + * 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. @@ -17,14 +17,10 @@ package org.conscrypt; /** - * Helper to initialize the JNI libraries. This version runs when compiled - * as part of the platform. + * Stub class for logging statistics events. */ -class NativeCryptoJni { - public static void init() { - System.loadLibrary("javacrypto"); - } +public class ConscryptStatsLog { + public static final int TLS_HANDSHAKE_REPORTED = 0; - private NativeCryptoJni() { - } + public static void write(int code, boolean arg1, int arg2, int arg3, int arg4) {} } diff --git a/android/src/main/java/org/conscrypt/NativeCryptoJni.java b/android/src/main/java/org/conscrypt/NativeCryptoJni.java deleted file mode 100644 index a4bf20776..000000000 --- a/android/src/main/java/org/conscrypt/NativeCryptoJni.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2015 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; - -/** - * Helper to initialize the JNI libraries. This version runs when compiled - * as part of an app distribution (or GmsCore). - */ -class NativeCryptoJni { - public static void init() { - if ("com.google.android.gms.org.conscrypt".equals(NativeCrypto.class.getPackage().getName())) { - System.loadLibrary("conscrypt_gmscore_jni"); - } else { - System.loadLibrary("conscrypt_jni"); - } - } - - private NativeCryptoJni() { - } -} 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/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/main/java/org/conscrypt/NativeCryptoJni.java b/openjdk/src/main/java/org/conscrypt/NativeCryptoJni.java deleted file mode 100644 index f0c9d8d1f..000000000 --- a/openjdk/src/main/java/org/conscrypt/NativeCryptoJni.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2014 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.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import org.conscrypt.NativeLibraryLoader.LoadResult; - -/** - * Helper to initialize the JNI libraries. This version runs when compiled as part of a host OpenJDK - * build. - */ -final class NativeCryptoJni { - private static final String STATIC_LIB_NAME = "conscrypt"; - private static final String DYNAMIC_LIB_NAME_PREFIX = "conscrypt_openjdk_jni"; - - /** - * Attempts to load the shared JNI library. First try loading the platform-specific library - * name (e.g. conscrypt_openjdk_jni-linux-x86_64). If that doesn't work, try to load the - * library via just the prefix (e.g. conscrypt_openjdk_jni). If not found, try the static - * library name. - * - * The non-suffixed dynamic library name is used by the Android build system, which builds - * the appropriate library for the system it's being run on under that name. - * - * The static library name is needed in order to support Java 8 static linking - * (http://openjdk.java.net/jeps/178), where the library name is used to invoke a - * library-specific load method (i.e. {@code JNI_OnLoad_conscrypt}). - * - * @throws UnsatisfiedLinkError if the library failed to load. - */ - static void init() throws UnsatisfiedLinkError { - List results = new ArrayList(); - if (!NativeLibraryLoader.loadFirstAvailable(classLoader(), results, - platformLibName(), DYNAMIC_LIB_NAME_PREFIX, STATIC_LIB_NAME)) { - logResults(results); - throwBestError(results); - } - } - - private NativeCryptoJni() {} - - private static void logResults(List results) { - for (LoadResult result : results) { - result.log(); - } - } - - private static void throwBestError(List results) { - Collections.sort(results, ErrorComparator.INSTANCE); - - Throwable bestError = results.get(0).error; - for (LoadResult result : results.subList(1, results.size())) { - // Suppress all of the other errors, so that they're available to the caller if - // desired. - bestError.addSuppressed(result.error); - } - - if (bestError instanceof Error) { - throw (Error) bestError; - } - - throw (Error) new UnsatisfiedLinkError(bestError.getMessage()).initCause(bestError); - } - - private static ClassLoader classLoader() { - return NativeCrypto.class.getClassLoader(); - } - - private static String platformLibName() { - return DYNAMIC_LIB_NAME_PREFIX + "-" + osName() + '-' + archName(); - } - - private static String osName() { - return HostProperties.OS.getFileComponent(); - } - - private static String archName() { - return HostProperties.ARCH.getFileComponent(); - } - - /** - * Sorts the errors in a list in descending order of value. After a list is sorted, - * the first element is the most important error. - */ - private static final class ErrorComparator implements Comparator { - static final ErrorComparator INSTANCE = new ErrorComparator(); - - @Override - public int compare(LoadResult o1, LoadResult o2) { - Throwable e1 = o1.error; - Throwable e2 = o2.error; - - // First, sort by error type. - int value1 = e1 instanceof UnsatisfiedLinkError ? 1 : 0; - int value2 = e2 instanceof UnsatisfiedLinkError ? 1 : 0; - if (value1 != value2) { - // Order so that the UnsatisfiedLinkError is first. - return value2 - value1; - } - - // Both are either link errors or not. Compare the message. Treat messages in - // the form "no in java.library.path" as lower value, since there may be - // a more interesting message for a library that was found. - String m1 = e1.getMessage(); - String m2 = e2.getMessage(); - value1 = m1 != null && m1.contains("java.library.path") ? 0 : 1; - value2 = m2 != null && m2.contains("java.library.path") ? 0 : 1; - return value2 - value1; - } - } -} 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())));