Merge upstream master
Includes a lot of refactorings of the core SSL classes (especially
session code), some bug fixes, and AlgorithmParameters.GCM. There
shouldn't be any large changes in behavior.
A number of classes have been renamed, but shims are left in place to
handle cases where users are using reflection to access unexposed
APIs (see, eg, OpenSSLSocketImpl.java).
Bug: 62852271
Bug: 62369410
Test: cts -m CtsLibcoreTestCases
Test: cts -m CtsLibcoreOkHttpTestCases
Test: cts -m CtsLibcoreWycheproofConscryptTestCases
Change-Id: Ib6eb0819f2b38d1b8e49b49a0eba769eb8afc4cf
diff --git a/.travis.yml b/.travis.yml
index 60d7d81..4789ae0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,7 +38,7 @@
- $ANDROID_HOME/tools/bin/sdkmanager 'platforms;android-25'
- $ANDROID_HOME/tools/bin/sdkmanager 'extras;android;m2repository'
- $ANDROID_HOME/tools/bin/sdkmanager --channel=1 ndk-bundle
- - $ANDROID_HOME/tools/bin/sdkmanager 'cmake;3.6.3155560'
+ - $ANDROID_HOME/tools/bin/sdkmanager 'cmake;3.6.4111459'
addons:
apt:
diff --git a/Dockerfile b/Dockerfile
index e5501ef..87a3308 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,9 +21,9 @@
# Install Java 8
RUN wget -q --no-cookies --no-check-certificate \
--header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" \
- "http://download.oracle.com/otn-pub/java/jdk/8u121-b13/e9e7ea248e2c4826b92b3f075a80e441/jdk-8u121-linux-x64.tar.gz" \
+ "http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.tar.gz" \
-O - | tar xz -C /var/local
-ENV JAVA_HOME /var/local/jdk1.8.0_121
+ENV JAVA_HOME /var/local/jdk1.8.0_131
ENV PATH $JAVA_HOME/bin:$PATH
# Install GCC 4.8
diff --git a/README.md b/README.md
index d377821..2dc45c4 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
Conscrypt is a Java Security Provider (JSP) that implements parts of the
Java Cryptography Extension (JCE) and Java Secure Socket Extension (JSSE).
-It uses BoringSSL to provide cryptograhpic primitives and Transport Layer
+It uses BoringSSL to provide cryptographical primitives and Transport Layer
Security (TLS) for Java applications on Android and OpenJDK.
The core SSL engine has borrowed liberally from the [Netty](http://netty.io/) project and their
@@ -32,14 +32,14 @@
#### Download JARs
You can download
-[the JARs](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.conscrypt%22%20AND%20v%3A%221.1.0%22)
+[the JARs](http://search.maven.org/#search%7Cga%7C1%7Cg:%22org.conscrypt%22%20AND%20v:%221.1.0%22)
directly from the Maven repositories.
#### OpenJDK (i.e. non-Android)
##### Native Classifiers
-The OpenJDK artifacts are platform-dependent, since each embeds a native library for a particular
+The OpenJDK artifacts are platform-dependent since each embeds a native library for a particular
platform. We publish artifacts to Maven Central for the following platforms:
Classifier | Description
@@ -143,10 +143,10 @@
### OpenJDK
-This modules provides the `Platform` class for non-Android (OpenJDK-based) systems. It also provides
+These modules provide the `Platform` class for non-Android (OpenJDK-based) systems. It also provides
a native library loader supports bundling the shared library with the JAR.
### Platform
This is not an actual module and is not part of the default build. This is used for building
- Conscrypt as an embedded component of the Android platform.
\ No newline at end of file
+ Conscrypt as an embedded component of the Android platform.
diff --git a/RELEASING.md b/RELEASING.md
index 67ede26..19c4b8c 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -188,10 +188,18 @@
The following command will build the whole project and upload it to Maven
Central. Parallel building [is not safe during
uploadArchives](https://issues.gradle.org/browse/GRADLE-3420).
+
+**Linux/Mac:**
```bash
conscrypt$ ./gradlew build && ./gradlew -Dorg.gradle.parallel=false uploadArchives
```
+**Windows:**
+```bat
+C:\conscrypt>gradlew build && gradlew -Dorg.gradle.parallel=false uploadArchives
+
+```
+
If the version has the `-SNAPSHOT` suffix, the artifacts will automatically
go to the snapshot repository. Otherwise it's a release deployment and the
artifacts will go to a freshly created staging repository.
@@ -201,18 +209,23 @@
it and the architecture of your JVM. For a fully fledged deployment, you will
need to deploy for each supported OS/architecture.
-To deploy the codegen for an OS and architecture, you must run the following
-commands on that OS and specify the architecture by the flag `-PtargetArch=<arch>`.
-
When deploying a Release, the first deployment will create
[a new staging repository](https://oss.sonatype.org/#stagingRepositories). You'll need
to look up the ID in the OSSRH UI (usually in the form of `orgconscrypt-*`). Codegen
deployment commands should include `-PrepositoryId=<repository-id>` in order to
ensure that the artifacts are pushed to the same staging repository.
+**Linux/Mac:**
```bash
-conscrypt$ ./gradlew build conscrypt-openjdk:uploadArchives \
- -Dorg.gradle.parallel=false -PrepositoryId=<repository-id>
+conscrypt$ ./gradlew build && ./gradlew -Dorg.gradle.parallel=false \
+ conscrypt-openjdk:uploadArchives -PrepositoryId=<repository-id>
+```
+
+**Windows:**
+```bat
+C:\conscrypt>gradlew build && gradlew -Dorg.gradle.parallel=false ^
+ conscrypt-openjdk:uploadArchives -PrepositoryId=<repository-id>
+
```
Now finish [Releasing on Maven Central](#releasing-on-maven-central).
@@ -221,6 +234,7 @@
Once all of the native JARs appear on Maven Central, you can build and deploy
the Uber JAR that contains all of them.
+**Linux/Mac:**
```bash
conscrypt$ ./gradlew conscrypt-openjdk-uber:build \
-Dorg.conscrypt.openjdk.buildUberJar=true
@@ -230,6 +244,17 @@
-Dorg.conscrypt.openjdk.buildUberJar=true
```
+**Windows:**
+```bat
+C:\conscrypt>gradlew conscrypt-openjdk-uber:build ^
+ -Dorg.conscrypt.openjdk.buildUberJar=true
+
+C:\conscrypt>gradlew conscrypt-openjdk-uber:uploadArchives ^
+ -Dorg.gradle.parallel=false ^
+ -Dorg.conscrypt.openjdk.buildUberJar=true
+
+```
+
This will create
[a new staging repository](https://oss.sonatype.org/#stagingRepositories),
so you'll need to [close and release](#releasing-on-maven-central) the
diff --git a/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java b/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java
index 831dd17..851e27b 100644
--- a/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java
+++ b/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java
@@ -20,6 +20,7 @@
import java.security.cert.CertificateException;
import javax.net.ssl.SSLException;
+@SuppressWarnings("unused")
final class NativeCrypto {
public interface SSLHandshakeCallbacks {
/**
diff --git a/android-stub/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java b/android-stub/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java
index 079f89f..651d431 100644
--- a/android-stub/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java
+++ b/android-stub/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java
@@ -16,7 +16,7 @@
package com.android.org.conscrypt;
-class SSLParametersImpl {
+final class SSLParametersImpl {
public static SSLParametersImpl getDefault() {
throw new RuntimeException("Stub!");
}
diff --git a/android/build.gradle b/android/build.gradle
index d764845..de33f02 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -100,7 +100,12 @@
compile fileTree(dir: 'libs', include: ['*.jar'])
publicApiDocs project(':conscrypt-api-doclet')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
- exclude group: 'com.android.support', module: 'support-annotations'
+ exclude module: 'support-annotations'
+ exclude module: 'support-v4'
+ exclude module: 'support-v13'
+ exclude module: 'recyclerview-v7'
+ exclude module: 'appcompat-v7'
+ exclude module: 'design'
})
provided project(':conscrypt-android-stub')
diff --git a/android/lint.xml b/android/lint.xml
index d899843..0f057a9 100644
--- a/android/lint.xml
+++ b/android/lint.xml
@@ -2,7 +2,7 @@
<lint>
<!-- ExtendedSSLSession only gets instantiated in new APIs on Android. -->
<issue id="NewApi">
- <ignore path="**/org/conscrypt/OpenSSLExtendedSessionImpl.java" />
+ <ignore path="**/org/conscrypt/DelegatingExtendedSSLSession.java" />
</issue>
<!-- Android SparseArrays can't be used in common directory. -->
diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro
index c3bdd2c..3bc75b2 100644
--- a/android/proguard-rules.pro
+++ b/android/proguard-rules.pro
@@ -20,5 +20,7 @@
-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/android/src/main/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java b/android/src/main/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java
index 556b815..db7dc85 100644
--- a/android/src/main/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java
+++ b/android/src/main/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java
@@ -45,9 +45,9 @@
extends com.android.org.conscrypt.OpenSSLSocketImpl {
- private final org.conscrypt.OpenSSLSocketImpl delegate;
+ private final AbstractConscryptSocket delegate;
- public KitKatPlatformOpenSSLSocketImplAdapter(org.conscrypt.OpenSSLSocketImpl delegate)
+ public KitKatPlatformOpenSSLSocketImplAdapter(AbstractConscryptSocket delegate)
throws IOException {
super(null);
this.delegate = delegate;
diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java
index fa4a83a..32b4b6d 100644
--- a/android/src/main/java/org/conscrypt/Platform.java
+++ b/android/src/main/java/org/conscrypt/Platform.java
@@ -82,9 +82,8 @@
}
}
- public static FileDescriptor getFileDescriptorFromSSLSocket(
- OpenSSLSocketImpl openSSLSocketImpl) {
- return getFileDescriptor(openSSLSocketImpl);
+ public static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
+ return getFileDescriptor(socket);
}
public static String getCurveName(ECParameterSpec spec) {
@@ -195,7 +194,7 @@
}
public static void setSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLSocketImpl socket) {
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
try {
setSSLParametersOnImpl(params, impl);
@@ -213,14 +212,14 @@
}
public static void setSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
try {
setSSLParametersOnImpl(params, impl);
if (Build.VERSION.SDK_INT >= 24) {
String sniHostname = getSniHostnameFromParams(params);
if (sniHostname != null) {
- engine.setSniHostname(sniHostname);
+ engine.setHostname(sniHostname);
}
}
} catch (NoSuchMethodException ignored) {
@@ -260,7 +259,7 @@
}
public static void getSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLSocketImpl socket) {
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
try {
getSSLParametersFromImpl(params, impl);
@@ -276,17 +275,18 @@
@TargetApi(24)
private static void setParametersSniHostname(
- SSLParameters params, SSLParametersImpl impl, OpenSSLSocketImpl socket)
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
Method m_setServerNames = params.getClass().getMethod("setServerNames", List.class);
- m_setServerNames.invoke(params, Collections.<SNIServerName>singletonList(
- new SNIHostName(socket.getHostname())));
+ m_setServerNames.invoke(params,
+ Collections.<SNIServerName>singletonList(
+ new SNIHostName(socket.getHostname())));
}
}
public static void getSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
try {
getSSLParametersFromImpl(params, impl);
@@ -302,13 +302,13 @@
@TargetApi(24)
private static void setParametersSniHostname(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine)
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
- if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) {
+ if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
Method m_setServerNames = params.getClass().getMethod("setServerNames", List.class);
m_setServerNames.invoke(params,
Collections.<SNIServerName>singletonList(
- new SNIHostName(engine.getSniHostname())));
+ new SNIHostName(engine.getHostname())));
}
}
@@ -359,9 +359,9 @@
return false;
}
- @SuppressLint("NewApi") // OpenSSLSocketImpl defines getHandshakeSession()
+ @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLSocketImpl socket) throws CertificateException {
+ String authType, AbstractConscryptSocket socket) throws CertificateException {
if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
socket.getHandshakeSession().getPeerHost())) {
@@ -369,9 +369,9 @@
}
}
- @SuppressLint("NewApi") // OpenSSLSocketImpl defines getHandshakeSession()
+ @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLSocketImpl socket) throws CertificateException {
+ String authType, AbstractConscryptSocket socket) throws CertificateException {
if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
socket.getHandshakeSession().getPeerHost())) {
@@ -379,9 +379,9 @@
}
}
- @SuppressLint("NewApi") // OpenSSLSocketImpl defines getHandshakeSession()
+ @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLEngineImpl engine) throws CertificateException {
+ String authType, ConscryptEngine engine) throws CertificateException {
if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
engine.getHandshakeSession().getPeerHost())) {
@@ -389,9 +389,9 @@
}
}
- @SuppressLint("NewApi") // OpenSSLSocketImpl defines getHandshakeSession()
+ @SuppressLint("NewApi") // AbstractConscryptSocket defines getHandshakeSession()
public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLEngineImpl engine) throws CertificateException {
+ String authType, ConscryptEngine engine) throws CertificateException {
if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
engine.getHandshakeSession().getPeerHost())) {
@@ -430,7 +430,8 @@
// provider, which should be the default. That could happen if an
// OEM decided
// to implement a different default provider. Also highly unlikely.
- Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:"
+ Log.e(TAG,
+ "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:"
+ javaKey.getClass().getCanonicalName());
return null;
}
@@ -695,12 +696,12 @@
* Pre-Java 8 backward compatibility.
*/
- public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
+ public static SSLSession wrapSSLSession(ActiveSession sslSession) {
if (Build.VERSION.SDK_INT <= 23) {
return sslSession;
}
- return new OpenSSLExtendedSessionImpl(sslSession);
+ return new DelegatingExtendedSSLSession(sslSession);
}
public static SSLSession unwrapSSLSession(SSLSession sslSession) {
@@ -708,9 +709,10 @@
return sslSession;
}
- if (sslSession instanceof OpenSSLExtendedSessionImpl) {
- return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
+ if (sslSession instanceof DelegatingExtendedSSLSession) {
+ return ((DelegatingExtendedSSLSession) sslSession).getDelegate();
}
+
return sslSession;
}
diff --git a/android/src/main/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java b/android/src/main/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java
index 1a89208..9353a91 100644
--- a/android/src/main/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java
+++ b/android/src/main/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java
@@ -45,9 +45,9 @@
extends org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl {
- private final org.conscrypt.OpenSSLSocketImpl delegate;
+ private final AbstractConscryptSocket delegate;
- public PreKitKatPlatformOpenSSLSocketImplAdapter(org.conscrypt.OpenSSLSocketImpl delegate)
+ public PreKitKatPlatformOpenSSLSocketImplAdapter(AbstractConscryptSocket delegate)
throws IOException {
super(null);
this.delegate = delegate;
diff --git a/api-doclet/build.gradle b/api-doclet/build.gradle
index 235040b..74f015a 100644
--- a/api-doclet/build.gradle
+++ b/api-doclet/build.gradle
@@ -3,3 +3,7 @@
dependencies {
compile fileTree(dir: "${System.properties['java.home']}/../lib", include: '*tools.jar')
}
+
+// Don't include this artifact in the distribution.
+tasks.install.enabled = false
+tasks.uploadArchives.enabled = false;
diff --git a/appveyor.yml b/appveyor.yml
index 3f8cbf5..7cb3de1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -45,6 +45,9 @@
# Install yasm
- choco install -y yasm
+ # Install Go for BoringSSL compile (embedding test data)
+ - choco install -y golang
+
# Clone BoringSSL
- git clone --depth 1 https://boringssl.googlesource.com/boringssl.git "%BORINGSSL_HOME%"
@@ -86,7 +89,7 @@
- '%ANDROID_HOME%\tools\bin\sdkmanager.bat platforms;android-25'
- '%ANDROID_HOME%\tools\bin\sdkmanager.bat extras;android;m2repository'
- '%ANDROID_HOME%\tools\bin\sdkmanager.bat --channel=1 ndk-bundle'
- - '%ANDROID_HOME%\tools\bin\sdkmanager.bat cmake;3.6.3155560'
+ - '%ANDROID_HOME%\tools\bin\sdkmanager.bat cmake;3.6.4111459'
build_script:
- gradlew.bat assemble
diff --git a/benchmark-base/build.gradle b/benchmark-base/build.gradle
new file mode 100644
index 0000000..57eb24f
--- /dev/null
+++ b/benchmark-base/build.gradle
@@ -0,0 +1,32 @@
+description = 'Conscrypt: Benchmarks Base'
+
+evaluationDependsOn(':conscrypt-openjdk')
+
+ext {
+ preferredNativeConfiguration = project(':conscrypt-openjdk').preferredNativeConfiguration
+ preferredNativeFileDir = project(':conscrypt-openjdk').preferredNativeFileDir
+}
+
+sourceSets {
+ main {
+ resources {
+ // This shouldn't be needed but seems to help IntelliJ locate the native artifact.
+ srcDirs += preferredNativeFileDir
+ }
+ }
+}
+
+dependencies {
+ compile project(':conscrypt-openjdk'),
+ project(':conscrypt-testing'),
+ libraries.junit,
+ libraries.netty_handler,
+ libraries.netty_tcnative
+
+ // Add the preferred native openjdk configuration for this platform.
+ compile project(path: ':conscrypt-openjdk', configuration: "$preferredNativeConfiguration")
+}
+
+// Don't include this artifact in the distribution.
+tasks.install.enabled = false
+tasks.uploadArchives.enabled = false;
diff --git a/benchmark-base/src/main/java/org/conscrypt/BufferType.java b/benchmark-base/src/main/java/org/conscrypt/BufferType.java
new file mode 100644
index 0000000..a20bd01
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/BufferType.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Enumeration that provides allocation of direct or heap buffers.
+ */
+@SuppressWarnings("unused")
+public enum BufferType {
+ HEAP {
+ @Override
+ ByteBuffer newBuffer(int size) {
+ return ByteBuffer.allocate(size);
+ }
+ },
+ DIRECT {
+ @Override
+ ByteBuffer newBuffer(int size) {
+ return ByteBuffer.allocateDirect(size);
+ }
+ };
+
+ abstract ByteBuffer newBuffer(int size);
+
+ ByteBuffer newApplicationBuffer(SSLEngine engine) {
+ return newBuffer(engine.getSession().getApplicationBufferSize());
+ }
+
+ ByteBuffer newPacketBuffer(SSLEngine engine) {
+ return newBuffer(engine.getSession().getPacketBufferSize());
+ }
+}
diff --git a/testing/src/main/java/org/conscrypt/TestClient.java b/benchmark-base/src/main/java/org/conscrypt/ClientEndpoint.java
similarity index 76%
rename from testing/src/main/java/org/conscrypt/TestClient.java
rename to benchmark-base/src/main/java/org/conscrypt/ClientEndpoint.java
index 6ef37f9..3b75908 100644
--- a/testing/src/main/java/org/conscrypt/TestClient.java
+++ b/benchmark-base/src/main/java/org/conscrypt/ClientEndpoint.java
@@ -19,32 +19,39 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.InetAddress;
import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
/**
* Client-side endpoint. Provides basic services for sending/receiving messages from the client
* socket.
*/
-public final class TestClient {
+final class ClientEndpoint {
private final SSLSocket socket;
private InputStream input;
private OutputStream output;
- public TestClient(SSLSocket socket) {
- this.socket = socket;
+ ClientEndpoint(SSLSocketFactory socketFactory, ChannelType channelType, int port,
+ String[] protocols, String[] ciphers) throws IOException {
+ socket = channelType.newClientSocket(
+ socketFactory, InetAddress.getLoopbackAddress(), port);
+ socket.setEnabledProtocols(protocols);
+ socket.setEnabledCipherSuites(ciphers);
}
- public void start() {
+ void start() {
try {
socket.startHandshake();
input = socket.getInputStream();
output = socket.getOutputStream();
} catch (IOException e) {
+ e.printStackTrace();
throw new RuntimeException(e);
}
}
- public void stop() {
+ void stop() {
try {
socket.close();
} catch (IOException e) {
@@ -52,7 +59,7 @@
}
}
- public int readMessage(byte[] buffer) {
+ int readMessage(byte[] buffer) {
try {
int totalBytesRead = 0;
while (totalBytesRead < buffer.length) {
@@ -69,7 +76,7 @@
}
}
- public void sendMessage(byte[] data) {
+ void sendMessage(byte[] data) {
try {
output.write(data);
} catch (IOException e) {
@@ -77,7 +84,7 @@
}
}
- public void flush() {
+ void flush() {
try {
output.flush();
} catch (IOException e) {
diff --git a/benchmark-base/src/main/java/org/conscrypt/ClientSocketBenchmark.java b/benchmark-base/src/main/java/org/conscrypt/ClientSocketBenchmark.java
new file mode 100644
index 0000000..14bf98a
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/ClientSocketBenchmark.java
@@ -0,0 +1,155 @@
+/*
+ * 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 static org.conscrypt.TestUtils.getProtocols;
+import static org.conscrypt.TestUtils.newTextMessage;
+
+import java.io.OutputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Benchmark for comparing performance of client socket implementations.
+ */
+public final class ClientSocketBenchmark {
+ /**
+ * Provider for the benchmark configuration
+ */
+ interface Config {
+ SocketType socketType();
+ int messageSize();
+ String cipher();
+ ChannelType channelType();
+ }
+
+ private ClientEndpoint client;
+ private ServerEndpoint server;
+ private byte[] message;
+ private ExecutorService executor;
+ private Future<?> sendingFuture;
+ private volatile boolean stopping;
+
+ private static final AtomicLong bytesCounter = new AtomicLong();
+ private AtomicBoolean recording = new AtomicBoolean();
+
+ ClientSocketBenchmark(Config config) throws Exception {
+ recording.set(false);
+
+ message = newTextMessage(config.messageSize());
+
+ // Always use the same server for consistency across the benchmarks.
+ server = SocketType.CONSCRYPT_ENGINE.newServer(
+ ChannelType.CHANNEL, config.messageSize(), getProtocols(), ciphers(config));
+
+ server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
+ @Override
+ public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+ if (recording.get()) {
+ // Server received a message, increment the count.
+ bytesCounter.addAndGet(numBytes);
+ }
+ }
+ });
+ Future<?> connectedFuture = server.start();
+
+ client = config.socketType().newClient(
+ config.channelType(), server.port(), getProtocols(), ciphers(config));
+ client.start();
+
+ // Wait for the initial connection to complete.
+ connectedFuture.get(5, TimeUnit.SECONDS);
+
+ executor = Executors.newSingleThreadExecutor();
+ sendingFuture = executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Thread thread = Thread.currentThread();
+ while (!stopping && !thread.isInterrupted()) {
+ client.sendMessage(message);
+ }
+ }
+ });
+ }
+
+ void close() throws Exception {
+ stopping = true;
+ client.stop();
+ server.stop();
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ sendingFuture.get(5, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Simple benchmark for throughput.
+ */
+ void throughput() throws Exception {
+ recording.set(true);
+ // Send as many messages as we can in a second.
+ Thread.sleep(1001);
+ recording.set(false);
+ }
+
+ static void reset() {
+ bytesCounter.set(0);
+ }
+
+ static long bytesPerSecond() {
+ return bytesCounter.get();
+ }
+
+ private String[] ciphers(Config config) {
+ return new String[] {config.cipher()};
+ }
+
+ /**
+ * A simple main for profiling.
+ */
+ public static void main(String[] args) throws Exception {
+ ClientSocketBenchmark bm = new ClientSocketBenchmark(new Config() {
+ @Override
+ public SocketType socketType() {
+ return SocketType.CONSCRYPT_ENGINE;
+ }
+
+ @Override
+ public int messageSize() {
+ return 512;
+ }
+
+ @Override
+ public String cipher() {
+ return TestUtils.TEST_CIPHER;
+ }
+
+ @Override
+ public ChannelType channelType() {
+ return ChannelType.CHANNEL;
+ }
+ });
+
+ // Just run forever for profiling.
+ while (true) {
+ bm.throughput();
+ }
+ }
+}
diff --git a/benchmark-base/src/main/java/org/conscrypt/EngineHandshakeBenchmark.java b/benchmark-base/src/main/java/org/conscrypt/EngineHandshakeBenchmark.java
new file mode 100644
index 0000000..c5c566e
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/EngineHandshakeBenchmark.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.conscrypt;
+
+import static org.conscrypt.TestUtils.doEngineHandshake;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+/**
+ * Benchmark comparing handshake performance of various engine implementations to conscrypt.
+ */
+public final class EngineHandshakeBenchmark {
+ /**
+ * Provider for the benchmark configuration
+ */
+ interface Config {
+ BufferType bufferType();
+ EngineType engineType();
+ String cipher();
+ }
+
+ private final EngineType engineType;
+ private final String cipher;
+
+ private final ByteBuffer clientApplicationBuffer;
+ private final ByteBuffer clientPacketBuffer;
+ private final ByteBuffer serverApplicationBuffer;
+ private final ByteBuffer serverPacketBuffer;
+
+ EngineHandshakeBenchmark(Config config) throws Exception {
+ engineType = config.engineType();
+ cipher = config.cipher();
+ BufferType bufferType = config.bufferType();
+
+ SSLEngine clientEngine = engineType.newClientEngine(cipher);
+ SSLEngine serverEngine = engineType.newServerEngine(cipher);
+
+ // Create the application and packet buffers for both endpoints.
+ clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+ serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+ clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+ serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+ engineType.dispose(clientEngine);
+ engineType.dispose(serverEngine);
+ }
+
+ void handshake() throws SSLException {
+ SSLEngine client = engineType.newClientEngine(cipher);
+ SSLEngine server = engineType.newServerEngine(cipher);
+ clientApplicationBuffer.clear();
+ clientPacketBuffer.clear();
+ serverApplicationBuffer.clear();
+ serverPacketBuffer.clear();
+
+ doEngineHandshake(client, server, clientApplicationBuffer, clientPacketBuffer,
+ serverApplicationBuffer, serverPacketBuffer);
+ engineType.dispose(client);
+ engineType.dispose(server);
+ }
+
+ /**
+ * A simple main for profiling.
+ */
+ public static void main(String[] args) throws Exception {
+ EngineHandshakeBenchmark bm = new EngineHandshakeBenchmark(new Config() {
+ @Override
+ public BufferType bufferType() {
+ return BufferType.HEAP;
+ }
+
+ @Override
+ public EngineType engineType() {
+ return EngineType.NETTY;
+ }
+
+ @Override
+ public String cipher() {
+ return TestUtils.TEST_CIPHER;
+ }
+ });
+
+ // Just run forever for profiling.
+ while (true) {
+ bm.handshake();
+ }
+ }
+}
diff --git a/benchmark-base/src/main/java/org/conscrypt/EngineType.java b/benchmark-base/src/main/java/org/conscrypt/EngineType.java
new file mode 100644
index 0000000..8d3698f
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/EngineType.java
@@ -0,0 +1,185 @@
+/*
+ * 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 static org.conscrypt.TestUtils.PROTOCOL_TLS_V1_2;
+import static org.conscrypt.TestUtils.initClientSslContext;
+import static org.conscrypt.TestUtils.initEngine;
+import static org.conscrypt.TestUtils.initServerSslContext;
+
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.util.ReferenceCountUtil;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import libcore.java.security.TestKeyStore;
+
+/**
+ * Enumeration of various types of engines for use with engine-based benchmarks.
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "unused"})
+public enum EngineType {
+ JDK {
+ private final SSLContext clientContext = initClientSslContext(newContext());
+ private final SSLContext serverContext = initServerSslContext(newContext());
+
+ @Override
+ SSLEngine newClientEngine(String cipher) {
+ return initEngine(clientContext.createSSLEngine(), cipher, true);
+ }
+
+ @Override
+ SSLEngine newServerEngine(String cipher) {
+ return initEngine(serverContext.createSSLEngine(), cipher, false);
+ }
+
+ private SSLContext newContext() {
+ try {
+ return SSLContext.getInstance(PROTOCOL_TLS_V1_2);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ },
+ CONSCRYPT_UNPOOLED {
+ private final SSLContext clientContext = initClientSslContext(newContext());
+ private final SSLContext serverContext = initServerSslContext(newContext());
+
+ @Override
+ SSLEngine newClientEngine(String cipher) {
+ return initEngine(clientContext.createSSLEngine(), cipher, true);
+ }
+
+ @Override
+ SSLEngine newServerEngine(String cipher) {
+ return initEngine(serverContext.createSSLEngine(), cipher, false);
+ }
+
+ private SSLContext newContext() {
+ try {
+ return SSLContext.getInstance(PROTOCOL_TLS_V1_2, new OpenSSLProvider());
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ },
+ CONSCRYPT_POOLED {
+ private final SSLContext clientContext = initClientSslContext(newContext());
+ private final SSLContext serverContext = initServerSslContext(newContext());
+
+ @Override
+ SSLEngine newClientEngine(String cipher) {
+ SSLEngine engine = initEngine(clientContext.createSSLEngine(), cipher, true);
+ Conscrypt.Engines.setBufferAllocator(engine, PooledAllocator.getInstance());
+ return engine;
+ }
+
+ @Override
+ SSLEngine newServerEngine(String cipher) {
+ SSLEngine engine = initEngine(serverContext.createSSLEngine(), cipher, false);
+ Conscrypt.Engines.setBufferAllocator(engine, PooledAllocator.getInstance());
+ return engine;
+ }
+
+ private SSLContext newContext() {
+ try {
+ return SSLContext.getInstance(PROTOCOL_TLS_V1_2, new OpenSSLProvider());
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ },
+ NETTY {
+ private final SslContext clientContext =
+ newNettyClientContext(io.netty.handler.ssl.SslProvider.OPENSSL);
+ private final SslContext serverContext =
+ newNettyServerContext(io.netty.handler.ssl.SslProvider.OPENSSL);
+
+ @Override
+ SSLEngine newClientEngine(String cipher) {
+ return initEngine(
+ clientContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher, true);
+ }
+
+ @Override
+ SSLEngine newServerEngine(String cipher) {
+ return initEngine(
+ serverContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher, false);
+ }
+ },
+ NETTY_REF_CNT {
+ private final SslContext clientContext =
+ newNettyClientContext(io.netty.handler.ssl.SslProvider.OPENSSL_REFCNT);
+ private final SslContext serverContext =
+ newNettyServerContext(io.netty.handler.ssl.SslProvider.OPENSSL_REFCNT);
+
+ @Override
+ SSLEngine newClientEngine(String cipher) {
+ return initEngine(
+ clientContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher, true);
+ }
+
+ @Override
+ SSLEngine newServerEngine(String cipher) {
+ return initEngine(
+ serverContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher, false);
+ }
+
+ @Override
+ void dispose(SSLEngine engine) {
+ ReferenceCountUtil.release(engine);
+ }
+ };
+
+ abstract SSLEngine newClientEngine(String cipher);
+
+ abstract SSLEngine newServerEngine(String cipher);
+
+ void dispose(SSLEngine engine) {}
+
+ private static SslContext newNettyClientContext(io.netty.handler.ssl.SslProvider sslProvider) {
+ try {
+ TestKeyStore server = TestKeyStore.getServer();
+ SslContextBuilder ctx =
+ SslContextBuilder.forClient()
+ .sslProvider(sslProvider)
+ .trustManager((X509Certificate[]) server.getPrivateKey("RSA", "RSA")
+ .getCertificateChain());
+ return ctx.build();
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static SslContext newNettyServerContext(io.netty.handler.ssl.SslProvider sslProvider) {
+ try {
+ PrivateKeyEntry server = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+ SslContextBuilder ctx =
+ SslContextBuilder
+ .forServer(server.getPrivateKey(),
+ (X509Certificate[]) server.getCertificateChain())
+ .sslProvider(sslProvider);
+ return ctx.build();
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/benchmark-base/src/main/java/org/conscrypt/EngineWrapBenchmark.java b/benchmark-base/src/main/java/org/conscrypt/EngineWrapBenchmark.java
new file mode 100644
index 0000000..f77bc25
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/EngineWrapBenchmark.java
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.conscrypt;
+
+import static org.conscrypt.TestUtils.doEngineHandshake;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ */
+public final class EngineWrapBenchmark {
+ /**
+ * Provider for the benchmark configuration
+ */
+ interface Config {
+ BufferType bufferType();
+ EngineType engineType();
+ int messageSize();
+ String cipher();
+ }
+
+ private final EngineType engineType;
+ private final String cipher;
+ private final SSLEngine clientEngine;
+ private final SSLEngine serverEngine;
+
+ private final ByteBuffer messageBuffer;
+ private final ByteBuffer clientApplicationBuffer;
+ private final ByteBuffer clientPacketBuffer;
+ private final ByteBuffer serverApplicationBuffer;
+ private final ByteBuffer serverPacketBuffer;
+ private final ByteBuffer preEncryptedBuffer;
+
+ EngineWrapBenchmark(Config config) throws Exception {
+ engineType = config.engineType();
+ cipher = config.cipher();
+ BufferType bufferType = config.bufferType();
+
+ clientEngine = engineType.newClientEngine(cipher);
+ serverEngine = engineType.newServerEngine(cipher);
+
+ // Create the application and packet buffers for both endpoints.
+ clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+ serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+ clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+ serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+ // Generate the message to be sent from the client.
+ int messageSize = config.messageSize();
+ messageBuffer = bufferType.newBuffer(messageSize);
+ messageBuffer.put(newTextMessage(messageSize));
+ messageBuffer.flip();
+
+ // Complete the initial TLS handshake.
+ doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer, clientPacketBuffer,
+ serverApplicationBuffer, serverPacketBuffer);
+
+ // Populate the pre-encrypted buffer for use with the unwrap benchmark.
+ preEncryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+ doWrap(messageBuffer, preEncryptedBuffer);
+ doUnwrap(preEncryptedBuffer, serverApplicationBuffer);
+ }
+
+ void teardown() {
+ engineType.dispose(clientEngine);
+ engineType.dispose(serverEngine);
+ }
+
+ void wrap() throws SSLException {
+ // Reset the buffers.
+ messageBuffer.position(0);
+ clientPacketBuffer.clear();
+
+ // Wrap the original message and create the encrypted data.
+ doWrap(messageBuffer, clientPacketBuffer);
+
+ // Lightweight comparison - just make sure the data length is correct.
+ assertEquals(preEncryptedBuffer.limit(), clientPacketBuffer.limit());
+ }
+
+ /**
+ * Simple benchmark that sends a single message from client to server.
+ */
+ void wrapAndUnwrap() throws SSLException {
+ // Reset the buffers.
+ messageBuffer.position(0);
+ clientPacketBuffer.clear();
+ serverApplicationBuffer.clear();
+
+ // Wrap the original message and create the encrypted data.
+ doWrap(messageBuffer, clientPacketBuffer);
+
+ // Unwrap the encrypted data and get back the original result.
+ doUnwrap(clientPacketBuffer, serverApplicationBuffer);
+
+ // Lightweight comparison - just make sure the unencrypted data length is correct.
+ assertEquals(messageBuffer.limit(), serverApplicationBuffer.limit());
+ }
+
+ private void doWrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+ // Wrap the original message and create the encrypted data.
+ verifyResult(src, clientEngine.wrap(src, dst));
+ dst.flip();
+ }
+
+ private void doUnwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+ verifyResult(src, serverEngine.unwrap(src, dst));
+ dst.flip();
+ }
+
+ private void verifyResult(ByteBuffer src, SSLEngineResult result) {
+ if (result.getStatus() != SSLEngineResult.Status.OK) {
+ throw new RuntimeException("Operation returned unexpected result " + result);
+ }
+ if (result.bytesConsumed() != src.limit()) {
+ throw new RuntimeException(
+ String.format("Operation didn't consume all bytes. Expected %d, consumed %d.",
+ src.limit(), result.bytesConsumed()));
+ }
+ }
+
+ /**
+ * A simple main for profiling.
+ */
+ public static void main(String[] args) throws Exception {
+ EngineWrapBenchmark bm = new EngineWrapBenchmark(new Config() {
+ @Override
+ public BufferType bufferType() {
+ return BufferType.HEAP;
+ }
+
+ @Override
+ public EngineType engineType() {
+ return EngineType.CONSCRYPT_POOLED;
+ }
+
+ @Override
+ public int messageSize() {
+ return 512;
+ }
+
+ @Override
+ public String cipher() {
+ return TestUtils.TEST_CIPHER;
+ }
+ });
+
+ // Just run forever for profiling.
+ while (true) {
+ bm.wrapAndUnwrap();
+ }
+ }
+}
diff --git a/benchmark-base/src/main/java/org/conscrypt/PooledAllocator.java b/benchmark-base/src/main/java/org/conscrypt/PooledAllocator.java
new file mode 100644
index 0000000..ed84d7d
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/PooledAllocator.java
@@ -0,0 +1,68 @@
+/*
+ * 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.PooledByteBufAllocator;
+import java.nio.ByteBuffer;
+
+/**
+ * A {@link BufferAllocator} that is backed by a Netty buffer pool.
+ */
+final class PooledAllocator extends BufferAllocator {
+ private static final ByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;
+ private static final PooledAllocator instance = new PooledAllocator();
+
+ static PooledAllocator getInstance() {
+ return instance;
+ }
+
+ private PooledAllocator() {}
+
+ @Override
+ public AllocatedBuffer allocateDirectBuffer(int capacity) {
+ return new ByteBufAdapter(alloc.directBuffer(capacity));
+ }
+
+ private static final class ByteBufAdapter extends AllocatedBuffer {
+ private final ByteBuf nettyBuffer;
+ private final ByteBuffer buffer;
+
+ private ByteBufAdapter(ByteBuf nettyBuffer) {
+ this.nettyBuffer = nettyBuffer;
+ nettyBuffer.writerIndex(nettyBuffer.capacity());
+ this.buffer = nettyBuffer.nioBuffer();
+ }
+
+ @Override
+ public ByteBuffer nioBuffer() {
+ return buffer;
+ }
+
+ @Override
+ public AllocatedBuffer retain() {
+ nettyBuffer.retain();
+ return this;
+ }
+
+ @Override
+ public AllocatedBuffer release() {
+ nettyBuffer.release();
+ return this;
+ }
+ }
+}
diff --git a/testing/src/main/java/org/conscrypt/TestServer.java b/benchmark-base/src/main/java/org/conscrypt/ServerEndpoint.java
similarity index 75%
rename from testing/src/main/java/org/conscrypt/TestServer.java
rename to benchmark-base/src/main/java/org/conscrypt/ServerEndpoint.java
index c706ba9..29cf10e 100644
--- a/testing/src/main/java/org/conscrypt/TestServer.java
+++ b/benchmark-base/src/main/java/org/conscrypt/ServerEndpoint.java
@@ -19,21 +19,25 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.ServerSocket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
/**
* A simple socket-based test server.
*/
-public final class TestServer {
+final class ServerEndpoint {
/**
* A processor for receipt of a single message.
*/
- public interface MessageProcessor { void processMessage(byte[] message, int numBytes, OutputStream os); }
+ public interface MessageProcessor {
+ void processMessage(byte[] message, int numBytes, OutputStream os);
+ }
/**
* A {@link MessageProcessor} that simply echos back the received message to the client.
@@ -50,8 +54,12 @@
}
}
- private final SSLServerSocket serverSocket;
+ private final ServerSocket serverSocket;
+ private final ChannelType channelType;
+ private final SSLSocketFactory socketFactory;
private final int messageSize;
+ private final String[] protocols;
+ private final String[] cipherSuites;
private final byte[] buffer;
private SSLSocket socket;
private ExecutorService executor;
@@ -60,22 +68,28 @@
private volatile boolean stopping;
private volatile MessageProcessor messageProcessor = new EchoProcessor();
- public TestServer(SSLServerSocket serverSocket, int messageSize) {
- this.serverSocket = serverSocket;
+ ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
+ ChannelType channelType, int messageSize, String[] protocols,
+ String[] cipherSuites) throws IOException {
+ this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+ this.socketFactory = socketFactory;
+ this.channelType = channelType;
this.messageSize = messageSize;
+ this.protocols = protocols;
+ this.cipherSuites = cipherSuites;
buffer = new byte[messageSize];
}
- public void setMessageProcessor(MessageProcessor messageProcessor) {
+ void setMessageProcessor(MessageProcessor messageProcessor) {
this.messageProcessor = messageProcessor;
}
- public Future<?> start() {
+ Future<?> start() throws IOException {
executor = Executors.newSingleThreadExecutor();
return executor.submit(new AcceptTask());
}
- public void stop() {
+ void stop() {
try {
stopping = true;
@@ -106,7 +120,12 @@
if (stopping) {
return;
}
- socket = (SSLSocket) serverSocket.accept();
+ socket = channelType.accept(serverSocket, socketFactory);
+ socket.setEnabledProtocols(protocols);
+ socket.setEnabledCipherSuites(cipherSuites);
+
+ socket.startHandshake();
+
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
@@ -115,6 +134,7 @@
}
executor.execute(new ProcessTask());
} catch (IOException e) {
+ e.printStackTrace();
throw new RuntimeException(e);
}
}
diff --git a/benchmark-base/src/main/java/org/conscrypt/ServerSocketBenchmark.java b/benchmark-base/src/main/java/org/conscrypt/ServerSocketBenchmark.java
new file mode 100644
index 0000000..9059b75
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/ServerSocketBenchmark.java
@@ -0,0 +1,169 @@
+/*
+ * 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 static org.conscrypt.TestUtils.getProtocols;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import org.conscrypt.ServerEndpoint.MessageProcessor;
+
+/**
+ * Benchmark for comparing performance of server socket implementations.
+ */
+public final class ServerSocketBenchmark {
+ /**
+ * Provider for the benchmark configuration
+ */
+ interface Config {
+ SocketType socketType();
+ int messageSize();
+ String cipher();
+ ChannelType channelType();
+ }
+
+ private ClientEndpoint client;
+ private ServerEndpoint server;
+ private ExecutorService executor;
+ private Future<?> receivingFuture;
+ private volatile boolean stopping;
+ private static final AtomicLong bytesCounter = new AtomicLong();
+ private AtomicBoolean recording = new AtomicBoolean();
+
+ ServerSocketBenchmark(Config config) throws Exception {
+ recording.set(false);
+
+ byte[] message = newTextMessage(config.messageSize());
+
+ final ChannelType channelType = config.channelType();
+
+ server = config.socketType().newServer(
+ channelType, config.messageSize(), getProtocols(), ciphers(config));
+ server.setMessageProcessor(new MessageProcessor() {
+ @Override
+ public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+ try {
+ while (!stopping) {
+ os.write(inMessage, 0, numBytes);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ Future<?> connectedFuture = server.start();
+
+ // Always use the same client for consistency across the benchmarks.
+ client = SocketType.CONSCRYPT_ENGINE.newClient(
+ ChannelType.CHANNEL, server.port(), getProtocols(), ciphers(config));
+ client.start();
+
+ // Wait for the initial connection to complete.
+ connectedFuture.get(5, TimeUnit.SECONDS);
+
+ // Start the server-side streaming by sending a message to the server.
+ client.sendMessage(message);
+ client.flush();
+
+ executor = Executors.newSingleThreadExecutor();
+ receivingFuture = executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Thread thread = Thread.currentThread();
+ byte[] buffer = new byte[config.messageSize()];
+ while (!stopping && !thread.isInterrupted()) {
+ int numBytes = client.readMessage(buffer);
+ assertEquals(config.messageSize(), numBytes);
+
+ // Increment the message counter if we're recording.
+ if (recording.get()) {
+ bytesCounter.addAndGet(numBytes);
+ }
+ }
+ }
+ });
+ }
+
+ void close() throws Exception {
+ stopping = true;
+ client.stop();
+ server.stop();
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ receivingFuture.get(5, TimeUnit.SECONDS);
+ }
+
+ void throughput() throws Exception {
+ recording.set(true);
+ // Send as many messages as we can in a second.
+ Thread.sleep(1001);
+ recording.set(false);
+ }
+
+ static void reset() {
+ bytesCounter.set(0);
+ }
+
+ static long bytesPerSecond() {
+ return bytesCounter.get();
+ }
+
+ private String[] ciphers(Config config) {
+ return new String[] {config.cipher()};
+ }
+
+ /**
+ * A simple main for profiling.
+ */
+ public static void main(String[] args) throws Exception {
+ ServerSocketBenchmark bm = new ServerSocketBenchmark(new Config() {
+ @Override
+ public SocketType socketType() {
+ return SocketType.CONSCRYPT_ENGINE;
+ }
+
+ @Override
+ public int messageSize() {
+ return 512;
+ }
+
+ @Override
+ public String cipher() {
+ return TestUtils.TEST_CIPHER;
+ }
+
+ @Override
+ public ChannelType channelType() {
+ return ChannelType.CHANNEL;
+ }
+ });
+
+ // Just run forever for profiling.
+ while (true) {
+ bm.throughput();
+ }
+ }
+}
diff --git a/benchmark-base/src/main/java/org/conscrypt/SocketType.java b/benchmark-base/src/main/java/org/conscrypt/SocketType.java
new file mode 100644
index 0000000..da0c3e9
--- /dev/null
+++ b/benchmark-base/src/main/java/org/conscrypt/SocketType.java
@@ -0,0 +1,71 @@
+package org.conscrypt;
+
+import java.io.IOException;
+import java.security.Provider;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Utility for creating test client and server instances.
+ */
+@SuppressWarnings("ImmutableEnumChecker")
+public enum SocketType {
+ JDK(newJdkFactories()),
+ CONSCRYPT(newConscryptFactories(false)),
+ CONSCRYPT_ENGINE(newConscryptFactories(true));
+
+ private final Factories factories;
+
+ SocketType(Factories factories) {
+ this.factories = factories;
+ }
+
+ ClientEndpoint newClient(ChannelType channelType, int port, String[] protocols,
+ String[] ciphers) throws IOException {
+ return new ClientEndpoint(
+ factories.clientFactory, channelType, port, protocols, ciphers);
+ }
+
+ ServerEndpoint newServer(ChannelType channelType, int messageSize,
+ String[] protocols, String[] ciphers) throws IOException {
+ return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
+ channelType, messageSize, protocols, ciphers);
+ }
+
+ private static final class Factories {
+ final SSLSocketFactory clientFactory;
+ final SSLSocketFactory serverFactory;
+ final SSLServerSocketFactory serverSocketFactory;
+
+ private Factories(SSLSocketFactory clientFactory, SSLSocketFactory serverFactory,
+ SSLServerSocketFactory serverSocketFactory) {
+ this.clientFactory = clientFactory;
+ this.serverFactory = serverFactory;
+ this.serverSocketFactory = serverSocketFactory;
+ }
+ }
+
+ private static Factories newJdkFactories() {
+ Provider provider = TestUtils.getJdkProvider();
+ SSLContext clientContext = TestUtils.newClientSslContext(provider);
+ SSLContext serverContext = TestUtils.newServerSslContext(provider);
+ final SSLSocketFactory clientFactory = clientContext.getSocketFactory();
+ final SSLSocketFactory serverFactory = serverContext.getSocketFactory();
+ final SSLServerSocketFactory serverSocketFactory = serverContext.getServerSocketFactory();
+ return new Factories(clientFactory, serverFactory, serverSocketFactory);
+ }
+
+ private static Factories newConscryptFactories(boolean useEngineSocket) {
+ Provider provider = TestUtils.getConscryptProvider();
+ SSLContext clientContext = TestUtils.newClientSslContext(provider);
+ SSLContext serverContext = TestUtils.newServerSslContext(provider);
+ final SSLSocketFactory clientFactory = clientContext.getSocketFactory();
+ final SSLSocketFactory serverFactory = serverContext.getSocketFactory();
+ final SSLServerSocketFactory serverSocketFactory = serverContext.getServerSocketFactory();
+ TestUtils.setUseEngineSocket(clientFactory, useEngineSocket);
+ TestUtils.setUseEngineSocket(serverFactory, useEngineSocket);
+ TestUtils.setUseEngineSocket(serverSocketFactory, useEngineSocket);
+ return new Factories(clientFactory, serverFactory, serverSocketFactory);
+ }
+}
diff --git a/benchmark-graphs/build.gradle b/benchmark-graphs/build.gradle
index 9f5ee15..1625bea 100644
--- a/benchmark-graphs/build.gradle
+++ b/benchmark-graphs/build.gradle
@@ -6,3 +6,7 @@
}
mainClassName = 'org.conscrypt.graphgen.Main'
+
+// Don't include this artifact in the distribution.
+tasks.install.enabled = false
+tasks.uploadArchives.enabled = false;
diff --git a/benchmark-jmh/build.gradle b/benchmark-jmh/build.gradle
new file mode 100644
index 0000000..070211b
--- /dev/null
+++ b/benchmark-jmh/build.gradle
@@ -0,0 +1,71 @@
+plugins {
+ id 'me.champeau.gradle.jmh' version '0.3.1'
+}
+
+apply plugin: 'idea'
+
+description = 'Conscrypt: OpenJDK Benchmarks'
+
+ext {
+ genDir = "${buildDir}/jmh-generated-classes"
+ jmhInclude = System.getProperty('jmh.include')
+ jmhWarmupIterations = System.getProperty('jmh.wi', '10')
+ jmhIterations = System.getProperty('jmh.i', '10')
+ jmhFork = System.getProperty('jmh.f', '1')
+ jmhJvmArgs = System.getProperty('jmh.jvmArgs', '-server -Xms2g -Xmx2g')
+}
+
+jmh {
+ jmhVersion = "$jmhVersion"
+ if (jmhInclude != null) {
+ setInclude(jmhInclude.toString())
+ }
+ warmupIterations = "$jmhWarmupIterations".toInteger()
+ iterations = "$jmhIterations".toInteger();
+ fork = "$jmhFork".toInteger()
+ jvmArgs = jmhJvmArgs.toString()
+ duplicateClassesStrategy = 'warn'
+}
+
+configurations {
+ // The JMH plugin by defaults depends on all of the generators for an old version of JMH.
+ // Need to remove all the generators that we're not explicitly overriding to eliminate the
+ // dependency on the old version of JMH.
+ jmh.exclude module:'jmh-generator-asm'
+
+ jmhGeneratorAnnprocess
+}
+
+sourceSets {
+ sourceSets {
+ main {
+ resources {
+ // This shouldn't be needed but seems to help IntelliJ locate
+ // META_INF/BenchmarkList.
+ srcDirs += genDir
+ }
+ }
+ }
+}
+
+dependencies {
+ compile project(':conscrypt-benchmark-base'),
+ libraries.junit
+
+ jmhGeneratorAnnprocess libraries.jmh_generator_annprocess
+
+ // Override the default JMH dependencies with the new versions.
+ jmh libraries.jmh_core,
+ libraries.jmh_generator_reflection,
+ libraries.jmh_generator_bytecode
+}
+
+// Running benchmarks in IntelliJ seems broken without this.
+// See https://github.com/melix/jmh-gradle-plugin/issues/39
+idea.module {
+ scopes.PROVIDED.plus += [ configurations.compile, configurations.jmh ]//, configurations.jmhGeneratorAnnprocess ]
+}
+
+// Don't include this artifact in the distribution.
+tasks.install.enabled = false
+tasks.uploadArchives.enabled = false;
diff --git a/benchmark-jmh/src/jmh/java/org/conscrypt/JmhClientSocketBenchmark.java b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhClientSocketBenchmark.java
new file mode 100644
index 0000000..425aba3
--- /dev/null
+++ b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhClientSocketBenchmark.java
@@ -0,0 +1,110 @@
+/*
+ * 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 org.conscrypt.ClientSocketBenchmark.Config;
+import org.openjdk.jmh.annotations.AuxCounters;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Threads;
+
+/**
+ * Benchmark for comparing performance of client socket implementations. All benchmarks use Netty
+ * with tcnative as the server.
+ */
+@State(Scope.Benchmark)
+@Fork(1)
+@Threads(1)
+public class JmhClientSocketBenchmark {
+ /**
+ * Use an AuxCounter so we can measure that bytes per second as they accumulate without
+ * consuming CPU in the benchmark method.
+ */
+ @AuxCounters
+ @State(Scope.Thread)
+ public static class BytesPerSecondCounter {
+ @Setup(Level.Iteration)
+ public void clean() {
+ ClientSocketBenchmark.reset();
+ }
+
+ @SuppressWarnings("unused")
+ public long bytesPerSecond() {
+ return ClientSocketBenchmark.bytesPerSecond();
+ }
+ }
+
+ private final JmhConfig config = new JmhConfig();
+
+ @Param
+ public SocketType socketType;
+
+ @Param({"64", "512", "4096"})
+ public int messageSize;
+
+ @Param({TestUtils.TEST_CIPHER})
+ public String cipher;
+
+ @Param
+ public ChannelType channelType;
+
+ private ClientSocketBenchmark benchmark;
+
+ @Setup(Level.Iteration)
+ public void setup() throws Exception {
+ benchmark = new ClientSocketBenchmark(config);
+ }
+
+ @TearDown(Level.Iteration)
+ public void teardown() throws Exception {
+ benchmark.close();
+ }
+
+ @Benchmark
+ public final void bm(@SuppressWarnings("unused") BytesPerSecondCounter counter)
+ throws Exception {
+ benchmark.throughput();
+ }
+
+ private final class JmhConfig implements Config {
+ @Override
+ public SocketType socketType() {
+ return socketType;
+ }
+
+ @Override
+ public int messageSize() {
+ return messageSize;
+ }
+
+ @Override
+ public String cipher() {
+ return cipher;
+ }
+
+ @Override
+ public ChannelType channelType() {
+ return channelType;
+ }
+ }
+}
diff --git a/benchmark-jmh/src/jmh/java/org/conscrypt/JmhEngineHandshakeBenchmark.java b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhEngineHandshakeBenchmark.java
new file mode 100644
index 0000000..69c2773
--- /dev/null
+++ b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhEngineHandshakeBenchmark.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.conscrypt;
+
+import javax.net.ssl.SSLException;
+import org.conscrypt.EngineHandshakeBenchmark.Config;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ */
+@State(Scope.Benchmark)
+@Fork(1)
+@Threads(1)
+public class JmhEngineHandshakeBenchmark {
+ private final JmhConfig config = new JmhConfig();
+
+ @Param({TestUtils.TEST_CIPHER})
+ public String a_cipher;
+
+ @Param
+ public BufferType b_buffer;
+
+ @Param
+ public EngineType c_engine;
+
+ private EngineHandshakeBenchmark benchmark;
+
+ @Setup(Level.Iteration)
+ public void setup() throws Exception {
+ benchmark = new EngineHandshakeBenchmark(config);
+ }
+
+ @Benchmark
+ public void hs() throws SSLException {
+ benchmark.handshake();
+ }
+
+ private final class JmhConfig implements Config {
+
+ @Override
+ public BufferType bufferType() {
+ return b_buffer;
+ }
+
+ @Override
+ public EngineType engineType() {
+ return c_engine;
+ }
+
+ @Override
+ public String cipher() {
+ return a_cipher;
+ }
+ }
+}
diff --git a/benchmark-jmh/src/jmh/java/org/conscrypt/JmhEngineWrapBenchmark.java b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhEngineWrapBenchmark.java
new file mode 100644
index 0000000..7be5498
--- /dev/null
+++ b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhEngineWrapBenchmark.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.conscrypt;
+
+import javax.net.ssl.SSLException;
+import org.conscrypt.EngineWrapBenchmark.Config;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Threads;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ */
+@State(Scope.Benchmark)
+@Fork(1)
+@Threads(1)
+public class JmhEngineWrapBenchmark {
+ private final JmhConfig config = new JmhConfig();
+
+ @Param({TestUtils.TEST_CIPHER})
+ public String a_cipher;
+
+ @Param
+ public BufferType b_buffer;
+
+ @Param({"64", "512", "4096"})
+ public int c_message;
+
+ @Param
+ public EngineType d_engine;
+
+ private EngineWrapBenchmark benchmark;
+
+ @Setup(Level.Iteration)
+ public void setup() throws Exception {
+ benchmark = new EngineWrapBenchmark(config);
+ }
+
+ @TearDown(Level.Iteration)
+ public void teardown() {
+ benchmark.teardown();
+ }
+
+ @Benchmark
+ public void wrap() throws SSLException {
+ benchmark.wrap();
+ }
+
+ @Benchmark
+ public void wrapAndUnwrap() throws SSLException {
+ benchmark.wrapAndUnwrap();
+ }
+
+ private final class JmhConfig implements Config {
+
+ @Override
+ public BufferType bufferType() {
+ return b_buffer;
+ }
+
+ @Override
+ public EngineType engineType() {
+ return d_engine;
+ }
+
+ @Override
+ public int messageSize() {
+ return c_message;
+ }
+
+ @Override
+ public String cipher() {
+ return a_cipher;
+ }
+ }
+}
diff --git a/benchmark-jmh/src/jmh/java/org/conscrypt/JmhServerSocketBenchmark.java b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhServerSocketBenchmark.java
new file mode 100644
index 0000000..5452316
--- /dev/null
+++ b/benchmark-jmh/src/jmh/java/org/conscrypt/JmhServerSocketBenchmark.java
@@ -0,0 +1,110 @@
+/*
+ * 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 org.conscrypt.ServerSocketBenchmark.Config;
+import org.openjdk.jmh.annotations.AuxCounters;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Threads;
+
+/**
+ * Benchmark for comparing performance of server socket implementations. All benchmarks use the
+ * standard JDK TLS implementation.
+ */
+@State(Scope.Benchmark)
+@Fork(1)
+@Threads(1)
+public class JmhServerSocketBenchmark {
+ /**
+ * Use an AuxCounter so we can measure that bytes per second as they accumulate without
+ * consuming CPU in the benchmark method.
+ */
+ @AuxCounters
+ @State(Scope.Thread)
+ public static class BytesPerSecondCounter {
+ @Setup(Level.Iteration)
+ public void clean() {
+ ServerSocketBenchmark.reset();
+ }
+
+ @SuppressWarnings("unused")
+ public long bytesPerSecond() {
+ return ServerSocketBenchmark.bytesPerSecond();
+ }
+ }
+
+ private final JmhConfig config = new JmhConfig();
+
+ @Param
+ public SocketType socketType;
+
+ @Param
+ public ChannelType channelType;
+
+ @Param({"64", "512", "4096"})
+ public int messageSize;
+
+ @Param({"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"})
+ public String cipher;
+
+ private ServerSocketBenchmark benchmark;
+
+ @Setup(Level.Iteration)
+ public void setup() throws Exception {
+ benchmark = new ServerSocketBenchmark(config);
+ }
+
+ @TearDown(Level.Iteration)
+ public void teardown() throws Exception {
+ benchmark.close();
+ }
+
+ @Benchmark
+ public final void bm(@SuppressWarnings("unused") BytesPerSecondCounter counter)
+ throws Exception {
+ benchmark.throughput();
+ }
+
+ private final class JmhConfig implements Config {
+ @Override
+ public SocketType socketType() {
+ return socketType;
+ }
+
+ @Override
+ public int messageSize() {
+ return messageSize;
+ }
+
+ @Override
+ public String cipher() {
+ return cipher;
+ }
+
+ @Override
+ public ChannelType channelType() {
+ return channelType;
+ }
+ }
+}
diff --git a/build.gradle b/build.gradle
index e821006..a5ea976 100644
--- a/build.gradle
+++ b/build.gradle
@@ -58,11 +58,11 @@
}
boringsslHome = "$System.env.BORINGSSL_HOME"
- boringsslIncludeDir = "$boringsslHome/include"
- boringssl32BuildDir = "$boringsslHome/build32"
- boringssl64BuildDir = "$boringsslHome/build64"
+ boringsslIncludeDir = normalizePath("$boringsslHome/include")
+ boringssl32BuildDir = normalizePath("$boringsslHome/build32")
+ boringssl64BuildDir = normalizePath("$boringsslHome/build64")
jdkHome = "$System.env.JAVA_HOME"
- jdkIncludeDir = "$jdkHome/include"
+ jdkIncludeDir = normalizePath("$jdkHome/include")
// Needs to be binary compatible with androidMinSdkVersion
androidMinJavaVersion = JavaVersion.VERSION_1_7
@@ -80,16 +80,16 @@
boringSslGit = org.ajoberstar.grgit.Grgit.open(file("$boringsslHome"))
boringSslVersion = boringSslGit.head().id
- jmhVersion = '1.17.4'
+ jmhVersion = '1.19'
libraries = [
roboelectric: 'org.robolectric:android-all:7.1.0_r7-robolectric-0',
// Test dependencies.
+ bouncycastle_apis: 'org.bouncycastle:bcpkix-jdk15on:1.56',
+ bouncycastle_provider: 'org.bouncycastle:bcprov-jdk15on:1.56',
junit : 'junit:junit:4.12',
mockito: 'org.mockito:mockito-core:1.9.5',
truth : 'com.google.truth:truth:0.28',
- bouncycastle_provider: 'org.bouncycastle:bcprov-jdk15on:1.56',
- bouncycastle_apis: 'org.bouncycastle:bcpkix-jdk15on:1.56',
// Benchmark dependencies
jmh_core: "org.openjdk.jmh:jmh-core:${jmhVersion}",
@@ -234,3 +234,7 @@
}
}
}
+
+static String normalizePath(path) {
+ new File(path.toString()).absolutePath
+}
diff --git a/common/src/jni/main/cpp/NativeCrypto.cpp b/common/src/jni/main/cpp/NativeCrypto.cpp
index aeb6205..521295f 100644
--- a/common/src/jni/main/cpp/NativeCrypto.cpp
+++ b/common/src/jni/main/cpp/NativeCrypto.cpp
@@ -308,6 +308,35 @@
}
/**
+ * Finishes a pending CBB and returns a jbyteArray with the contents.
+ */
+jbyteArray CBBToByteArray(JNIEnv* env, CBB* cbb) {
+ uint8_t *data;
+ size_t len;
+ if (!CBB_finish(cbb, &data, &len)) {
+ Errors::jniThrowRuntimeException(env, "CBB_finish failed");
+ JNI_TRACE("creating byte array failed");
+ return nullptr;
+ }
+ bssl::UniquePtr<uint8_t> free_data(data);
+
+ ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(static_cast<jsize>(len)));
+ if (byteArray.get() == nullptr) {
+ JNI_TRACE("creating byte array failed");
+ return nullptr;
+ }
+
+ ScopedByteArrayRW bytes(env, byteArray.get());
+ if (bytes.get() == nullptr) {
+ JNI_TRACE("using byte array failed");
+ return nullptr;
+ }
+
+ memcpy(bytes.get(), data, len);
+ return byteArray.release();
+}
+
+/**
* Converts ASN.1 BIT STRING to a jbooleanArray.
*/
jbooleanArray ASN1BitStringToBooleanArray(JNIEnv* env, ASN1_BIT_STRING* bitStr) {
@@ -568,22 +597,6 @@
return ex_data->cached_size;
}
-// TODO(davidben): Remove this once
-// https://boringssl-review.googlesource.com/c/15864/ is in all Conscrypt
-// consumers.
-#if BORINGSSL_API_VERSION < 4
-int RsaMethodEncrypt(RSA* /* rsa */,
- size_t* /* out_len */,
- uint8_t* /* out */,
- size_t /* max_out */,
- const uint8_t* /* in */,
- size_t /* in_len */,
- int /* padding */) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE);
- return 0;
-}
-#endif
-
int RsaMethodSignRaw(RSA* rsa,
size_t* out_len,
uint8_t* out,
@@ -755,12 +768,6 @@
g_rsa_method.common.is_static = 1;
g_rsa_method.size = RsaMethodSize;
- // TODO(davidben): Remove this once
- // https://boringssl-review.googlesource.com/c/15864/ is in all Conscrypt
- // consumers.
-#if BORINGSSL_API_VERSION < 4
- g_rsa_method.encrypt = RsaMethodEncrypt;
-#endif
g_rsa_method.sign_raw = RsaMethodSignRaw;
g_rsa_method.decrypt = RsaMethodDecrypt;
g_rsa_method.flags = RSA_FLAG_OPAQUE;
@@ -981,22 +988,6 @@
return result;
}
-/**
- * private static native int EVP_PKEY_size(int pkey);
- */
-static int NativeCrypto_EVP_PKEY_size(JNIEnv* env, jclass, jobject pkeyRef) {
- EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
- JNI_TRACE("EVP_PKEY_size(%p)", pkey);
-
- if (pkey == nullptr) {
- return -1;
- }
-
- int result = EVP_PKEY_size(pkey);
- JNI_TRACE("EVP_PKEY_size(%p) => %d", pkey, result);
- return result;
-}
-
typedef int print_func(BIO*, const EVP_PKEY*, int, ASN1_PCTX*);
static jstring evp_print_func(JNIEnv* env, jobject pkeyRef, print_func* func,
@@ -1066,90 +1057,106 @@
}
/*
- * static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int, byte[])
+ * static native byte[] EVP_marshal_private_key(long)
*/
-static jbyteArray NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO(JNIEnv* env, jclass, jobject pkeyRef) {
+static jbyteArray NativeCrypto_EVP_marshal_private_key(JNIEnv* env, jclass, jobject pkeyRef) {
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
- JNI_TRACE("i2d_PKCS8_PRIV_KEY_INFO(%p)", pkey);
+ JNI_TRACE("EVP_marshal_private_key(%p)", pkey);
if (pkey == nullptr) {
return nullptr;
}
- bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> pkcs8(EVP_PKEY2PKCS8(pkey));
- if (pkcs8.get() == nullptr) {
- Errors::throwExceptionIfNecessary(env, "NativeCrypto_i2d_PKCS8_PRIV_KEY_INFO");
- JNI_TRACE("key=%p i2d_PKCS8_PRIV_KEY_INFO => error from key to PKCS8", pkey);
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 64)) {
+ Errors::jniThrowOutOfMemory(env, "CBB_init failed");
+ JNI_TRACE("CBB_init failed");
return nullptr;
}
- return ASN1ToByteArray<PKCS8_PRIV_KEY_INFO>(env, pkcs8.get(), i2d_PKCS8_PRIV_KEY_INFO);
+ if (!EVP_marshal_private_key(cbb.get(), pkey)) {
+ Errors::throwExceptionIfNecessary(env, "EVP_marshal_private_key");
+ JNI_TRACE("key=%p EVP_marshal_private_key => error", pkey);
+ return nullptr;
+ }
+
+ return CBBToByteArray(env, cbb.get());
}
/*
- * static native int d2i_PKCS8_PRIV_KEY_INFO(byte[])
+ * static native long EVP_parse_private_key(byte[])
*/
-static jlong NativeCrypto_d2i_PKCS8_PRIV_KEY_INFO(JNIEnv* env, jclass, jbyteArray keyJavaBytes) {
- JNI_TRACE("d2i_PKCS8_PRIV_KEY_INFO(%p)", keyJavaBytes);
+static jlong NativeCrypto_EVP_parse_private_key(JNIEnv* env, jclass, jbyteArray keyJavaBytes) {
+ JNI_TRACE("EVP_parse_private_key(%p)", keyJavaBytes);
ScopedByteArrayRO bytes(env, keyJavaBytes);
if (bytes.get() == nullptr) {
- JNI_TRACE("bytes=%p d2i_PKCS8_PRIV_KEY_INFO => threw exception", keyJavaBytes);
+ JNI_TRACE("bytes=%p EVP_parse_private_key => threw exception", keyJavaBytes);
return 0;
}
- const unsigned char* tmp = reinterpret_cast<const unsigned char*>(bytes.get());
- bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> pkcs8(
- d2i_PKCS8_PRIV_KEY_INFO(nullptr, &tmp, static_cast<long>(bytes.size())));
- if (pkcs8.get() == nullptr) {
- Errors::throwExceptionIfNecessary(env, "d2i_PKCS8_PRIV_KEY_INFO");
- JNI_TRACE("ssl=%p d2i_PKCS8_PRIV_KEY_INFO => error from DER to PKCS8", keyJavaBytes);
+ CBS cbs;
+ CBS_init(&cbs, reinterpret_cast<const uint8_t*>(bytes.get()), bytes.size());
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
+ if (!pkey || CBS_len(&cbs) != 0) {
+ Errors::throwParsingException(env, "Error parsing private key");
+ JNI_TRACE("bytes=%p EVP_parse_private_key => threw exception", keyJavaBytes);
return 0;
}
- bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKCS82PKEY(pkcs8.get()));
- if (pkey.get() == nullptr) {
- Errors::throwExceptionIfNecessary(env, "d2i_PKCS8_PRIV_KEY_INFO");
- JNI_TRACE("ssl=%p d2i_PKCS8_PRIV_KEY_INFO => error from PKCS8 to key", keyJavaBytes);
- return 0;
- }
-
- JNI_TRACE("bytes=%p d2i_PKCS8_PRIV_KEY_INFO => %p", keyJavaBytes, pkey.get());
+ JNI_TRACE("bytes=%p EVP_parse_private_key => %p", keyJavaBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
/*
- * static native byte[] i2d_PUBKEY(int)
+ * static native byte[] EVP_marshal_public_key(long)
*/
-static jbyteArray NativeCrypto_i2d_PUBKEY(JNIEnv* env, jclass, jobject pkeyRef) {
+static jbyteArray NativeCrypto_EVP_marshal_public_key(JNIEnv* env, jclass, jobject pkeyRef) {
EVP_PKEY* pkey = fromContextObject<EVP_PKEY>(env, pkeyRef);
- JNI_TRACE("i2d_PUBKEY(%p)", pkey);
+ JNI_TRACE("EVP_marshal_public_key(%p)", pkey);
+
if (pkey == nullptr) {
return nullptr;
}
- return ASN1ToByteArray<EVP_PKEY>(env, pkey, reinterpret_cast<int (*) (EVP_PKEY*, uint8_t **)>(i2d_PUBKEY));
+
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 64)) {
+ Errors::jniThrowOutOfMemory(env, "CBB_init failed");
+ JNI_TRACE("CBB_init failed");
+ return nullptr;
+ }
+
+ if (!EVP_marshal_public_key(cbb.get(), pkey)) {
+ Errors::throwExceptionIfNecessary(env, "EVP_marshal_public_key");
+ JNI_TRACE("key=%p EVP_marshal_public_key => error", pkey);
+ return nullptr;
+ }
+
+ return CBBToByteArray(env, cbb.get());
}
/*
- * static native int d2i_PUBKEY(byte[])
+ * static native long EVP_parse_public_key(byte[])
*/
-static jlong NativeCrypto_d2i_PUBKEY(JNIEnv* env, jclass, jbyteArray javaBytes) {
- JNI_TRACE("d2i_PUBKEY(%p)", javaBytes);
+static jlong NativeCrypto_EVP_parse_public_key(JNIEnv* env, jclass, jbyteArray keyJavaBytes) {
+ JNI_TRACE("EVP_parse_public_key(%p)", keyJavaBytes);
- ScopedByteArrayRO bytes(env, javaBytes);
+ ScopedByteArrayRO bytes(env, keyJavaBytes);
if (bytes.get() == nullptr) {
- JNI_TRACE("d2i_PUBKEY(%p) => threw error", javaBytes);
+ JNI_TRACE("bytes=%p EVP_parse_public_key => threw exception", keyJavaBytes);
return 0;
}
- const unsigned char* tmp = reinterpret_cast<const unsigned char*>(bytes.get());
- bssl::UniquePtr<EVP_PKEY> pkey(d2i_PUBKEY(nullptr, &tmp, static_cast<long>(bytes.size())));
- if (pkey.get() == nullptr) {
- JNI_TRACE("bytes=%p d2i_PUBKEY => threw exception", javaBytes);
- Errors::throwExceptionIfNecessary(env, "d2i_PUBKEY");
+ CBS cbs;
+ CBS_init(&cbs, reinterpret_cast<const uint8_t*>(bytes.get()), bytes.size());
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs));
+ if (!pkey || CBS_len(&cbs) != 0) {
+ Errors::throwParsingException(env, "Error parsing public key");
+ JNI_TRACE("bytes=%p EVP_parse_public_key => threw exception", keyJavaBytes);
return 0;
}
+ JNI_TRACE("bytes=%p EVP_parse_public_key => %p", keyJavaBytes, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
@@ -2366,23 +2373,6 @@
return result;
}
-/*
- * public static int void EVP_MD_block_size(long)
- */
-static jint NativeCrypto_EVP_MD_block_size(JNIEnv* env, jclass, jlong evpMdRef) {
- EVP_MD* evp_md = reinterpret_cast<EVP_MD*>(evpMdRef);
- JNI_TRACE("NativeCrypto_EVP_MD_block_size(%p)", evp_md);
-
- if (evp_md == nullptr) {
- Errors::jniThrowNullPointerException(env, nullptr);
- return -1;
- }
-
- jint result = static_cast<jint>(EVP_MD_block_size(evp_md));
- JNI_TRACE("NativeCrypto_EVP_MD_block_size(%p) => %d", evp_md, result);
- return result;
-}
-
static jlong evpDigestSignVerifyInit(
JNIEnv* env,
int (*init_func)(EVP_MD_CTX*, EVP_PKEY_CTX**, const EVP_MD*, ENGINE*, EVP_PKEY*),
@@ -3210,18 +3200,6 @@
return nonceLength;
}
-static jint NativeCrypto_EVP_AEAD_max_tag_len(JNIEnv* env, jclass, jlong evpAeadRef) {
- const EVP_AEAD* evpAead = reinterpret_cast<const EVP_AEAD*>(evpAeadRef);
- JNI_TRACE("EVP_AEAD_max_tag_len(%p)", evpAead);
- if (evpAead == nullptr) {
- Errors::jniThrowNullPointerException(env, "evpAead == null");
- return 0;
- }
- jint maxTagLen = static_cast<jint>(EVP_AEAD_max_tag_len(evpAead));
- JNI_TRACE("EVP_AEAD_max_tag_len(%p) => %d", evpAead, maxTagLen);
- return maxTagLen;
-}
-
typedef int (*evp_aead_ctx_op_func)(const EVP_AEAD_CTX *ctx, uint8_t *out,
size_t *out_len, size_t max_out_len,
const uint8_t *nonce, size_t nonce_len,
@@ -3471,41 +3449,6 @@
JNI_TRACE("NativeCrypto_RAND_bytes(%p) => success", output);
}
-static jint NativeCrypto_OBJ_txt2nid(JNIEnv* env, jclass, jstring oidStr) {
- JNI_TRACE("OBJ_txt2nid(%p)", oidStr);
-
- ScopedUtfChars oid(env, oidStr);
- if (oid.c_str() == nullptr) {
- return 0;
- }
-
- int nid = OBJ_txt2nid(oid.c_str());
- JNI_TRACE("OBJ_txt2nid(%s) => %d", oid.c_str(), nid);
- return nid;
-}
-
-static jstring NativeCrypto_OBJ_txt2nid_longName(JNIEnv* env, jclass, jstring oidStr) {
- JNI_TRACE("OBJ_txt2nid_longName(%p)", oidStr);
-
- ScopedUtfChars oid(env, oidStr);
- if (oid.c_str() == nullptr) {
- return nullptr;
- }
-
- JNI_TRACE("OBJ_txt2nid_longName(%s)", oid.c_str());
-
- int nid = OBJ_txt2nid(oid.c_str());
- if (nid == NID_undef) {
- JNI_TRACE("OBJ_txt2nid_longName(%s) => NID_undef", oid.c_str());
- ERR_clear_error();
- return nullptr;
- }
-
- const char* longName = OBJ_nid2ln(nid);
- JNI_TRACE("OBJ_txt2nid_longName(%s) => %s", oid.c_str(), longName);
- return env->NewStringUTF(longName);
-}
-
static jstring ASN1_OBJECT_to_OID_string(JNIEnv* env, const ASN1_OBJECT* obj) {
/*
* The OBJ_obj2txt API doesn't "measure" if you pass in nullptr as the buffer.
@@ -3566,71 +3509,6 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(bio.release()));
}
-static int NativeCrypto_BIO_read(JNIEnv* env, jclass, jlong bioRef, jbyteArray outputJavaBytes) {
- BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
- JNI_TRACE("BIO_read(%p, %p)", bio, outputJavaBytes);
-
- if (outputJavaBytes == nullptr) {
- Errors::jniThrowNullPointerException(env, "output == null");
- JNI_TRACE("BIO_read(%p, %p) => output == null", bio, outputJavaBytes);
- return 0;
- }
-
- jsize outputSize = env->GetArrayLength(outputJavaBytes);
-
- std::unique_ptr<unsigned char[]> buffer(
- new unsigned char[static_cast<unsigned int>(outputSize)]);
- if (buffer.get() == nullptr) {
- Errors::jniThrowOutOfMemory(env, "Unable to allocate buffer for read");
- return 0;
- }
-
- int read = BIO_read(bio, buffer.get(), static_cast<int>(outputSize));
- if (read <= 0) {
- Errors::throwIOException(env, "BIO_read");
- JNI_TRACE("BIO_read(%p, %p) => threw IO exception", bio, outputJavaBytes);
- return 0;
- }
-
- env->SetByteArrayRegion(outputJavaBytes, 0, read, reinterpret_cast<jbyte*>(buffer.get()));
- JNI_TRACE("BIO_read(%p, %p) => %d", bio, outputJavaBytes, read);
- return read;
-}
-
-static void NativeCrypto_BIO_write(JNIEnv* env, jclass, jlong bioRef, jbyteArray inputJavaBytes,
- jint offset, jint length) {
- BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
- JNI_TRACE("BIO_write(%p, %p, %d, %d)", bio, inputJavaBytes, offset, length);
-
- if (inputJavaBytes == nullptr) {
- Errors::jniThrowNullPointerException(env, "input == null");
- return;
- }
-
- int inputSize = env->GetArrayLength(inputJavaBytes);
- if (offset < 0 || offset > inputSize || length < 0 || length > inputSize - offset) {
- Errors::jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inputJavaBytes");
- JNI_TRACE("BIO_write(%p, %p, %d, %d) => IOOB", bio, inputJavaBytes, offset, length);
- return;
- }
-
- std::unique_ptr<unsigned char[]> buffer(new unsigned char[static_cast<unsigned int>(length)]);
- if (buffer.get() == nullptr) {
- Errors::jniThrowOutOfMemory(env, "Unable to allocate buffer for write");
- return;
- }
-
- env->GetByteArrayRegion(inputJavaBytes, offset, length, reinterpret_cast<jbyte*>(buffer.get()));
- if (BIO_write(bio, buffer.get(), length) != length) {
- ERR_clear_error();
- Errors::throwIOException(env, "BIO_write");
- JNI_TRACE("BIO_write(%p, %p, %d, %d) => IO error", bio, inputJavaBytes, offset, length);
- return;
- }
-
- JNI_TRACE("BIO_write(%p, %p, %d, %d) => success", bio, inputJavaBytes, offset, length);
-}
-
static void NativeCrypto_BIO_free_all(JNIEnv* env, jclass, jlong bioRef) {
BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
JNI_TRACE("BIO_free_all(%p)", bio);
@@ -4456,46 +4334,200 @@
sec);
}
-static jstring NativeCrypto_OBJ_txt2nid_oid(JNIEnv* env, jclass, jstring oidStr) {
- JNI_TRACE("OBJ_txt2nid_oid(%p)", oidStr);
+// A CbsHandle is a structure used to manage resources allocated by asn1_read-*
+// functions so that they can be freed properly when finished. This struct owns
+// all objects pointed to by its members.
+struct CbsHandle {
+ // A pointer to the CBS.
+ std::unique_ptr<CBS> cbs;
+ // A pointer to the data held by the CBS. If the data held by the CBS
+ // is owned by a different CbsHandle, data will be null.
+ std::unique_ptr<unsigned char[]> data;
+};
- ScopedUtfChars oid(env, oidStr);
- if (oid.c_str() == nullptr) {
- return nullptr;
+static jlong NativeCrypto_asn1_read_init(JNIEnv* env, jclass, jbyteArray data) {
+ JNI_TRACE("asn1_read_init(%p)", data);
+
+ ScopedByteArrayRO bytes(env, data);
+ if (bytes.get() == nullptr) {
+ Errors::throwIOException(env, "Error reading ASN.1 encoding");
+ return 0;
}
- JNI_TRACE("OBJ_txt2nid_oid(%s)", oid.c_str());
+ std::unique_ptr<CbsHandle> cbs(new CbsHandle());
+ cbs->data.reset(new unsigned char[bytes.size()]);
+ memcpy(cbs->data.get(), bytes.get(), bytes.size());
- int nid = OBJ_txt2nid(oid.c_str());
- if (nid == NID_undef) {
- JNI_TRACE("OBJ_txt2nid_oid(%s) => NID_undef", oid.c_str());
- ERR_clear_error();
- return nullptr;
- }
-
- const ASN1_OBJECT* obj = OBJ_nid2obj(nid);
- if (obj == nullptr) {
- Errors::throwExceptionIfNecessary(env, "OBJ_nid2obj");
- return nullptr;
- }
-
- ScopedLocalRef<jstring> ouputStr(env, ASN1_OBJECT_to_OID_string(env, obj));
- JNI_TRACE("OBJ_txt2nid_oid(%s) => %p", oid.c_str(), ouputStr.get());
- return ouputStr.release();
+ cbs->cbs.reset(new CBS());
+ CBS_init(cbs->cbs.get(), cbs->data.get(), bytes.size());
+ JNI_TRACE("asn1_read_init(%p) => %p", data, cbs.get());
+ return reinterpret_cast<uintptr_t>(cbs.release());
}
-static jstring NativeCrypto_X509_NAME_print_ex(JNIEnv* env, jclass, jlong x509NameRef, jlong jflags) {
- X509_NAME* x509name = reinterpret_cast<X509_NAME*>(static_cast<uintptr_t>(x509NameRef));
- unsigned long flags = static_cast<unsigned long>(jflags);
- JNI_TRACE("X509_NAME_print_ex(%p, %ld)", x509name, flags);
+static jlong NativeCrypto_asn1_read_sequence(JNIEnv* env, jclass, jlong cbsRef) {
+ CbsHandle* cbs = reinterpret_cast<CbsHandle*>(static_cast<uintptr_t>(cbsRef));
+ JNI_TRACE("asn1_read_sequence(%p)", cbs);
- if (x509name == nullptr) {
- Errors::jniThrowNullPointerException(env, "x509name == null");
- JNI_TRACE("X509_NAME_print_ex(%p, %ld) => x509name == null", x509name, flags);
- return nullptr;
+ std::unique_ptr<CbsHandle> seq(new CbsHandle());
+ seq->cbs.reset(new CBS());
+ if (!CBS_get_asn1(cbs->cbs.get(), seq->cbs.get(), CBS_ASN1_SEQUENCE)) {
+ Errors::throwIOException(env, "Error reading ASN.1 encoding");
+ return 0;
+ }
+ JNI_TRACE("asn1_read_sequence(%p) => %p", cbs, seq.get());
+ return reinterpret_cast<uintptr_t>(seq.release());
+}
+
+static jbyteArray NativeCrypto_asn1_read_octetstring(JNIEnv* env, jclass, jlong cbsRef) {
+ CbsHandle* cbs = reinterpret_cast<CbsHandle*>(static_cast<uintptr_t>(cbsRef));
+ JNI_TRACE("asn1_read_octetstring(%p)", cbs);
+
+ std::unique_ptr<CBS> str(new CBS());
+ if (!CBS_get_asn1(cbs->cbs.get(), str.get(), CBS_ASN1_OCTETSTRING)) {
+ Errors::throwIOException(env, "Error reading ASN.1 encoding");
+ return 0;
+ }
+ ScopedLocalRef<jbyteArray> out(env, env->NewByteArray(static_cast<jsize>(CBS_len(str.get()))));
+ if (out.get() == nullptr) {
+ Errors::throwIOException(env, "Error reading ASN.1 encoding");
+ return 0;
+ }
+ ScopedByteArrayRW outBytes(env, out.get());
+ if (outBytes.get() == nullptr) {
+ Errors::throwIOException(env, "Error reading ASN.1 encoding");
+ return 0;
+ }
+ memcpy(outBytes.get(), CBS_data(str.get()), CBS_len(str.get()));
+ JNI_TRACE("asn1_read_octetstring(%p) => %p", cbs, out.get());
+ return out.release();
+}
+
+static jlong NativeCrypto_asn1_read_uint64(JNIEnv* env, jclass, jlong cbsRef) {
+ CbsHandle* cbs = reinterpret_cast<CbsHandle*>(static_cast<uintptr_t>(cbsRef));
+ JNI_TRACE("asn1_read_uint64(%p)", cbs);
+
+ uint64_t value;
+ if (!CBS_get_asn1_uint64(cbs->cbs.get(), &value)) {
+ Errors::throwIOException(env, "Error reading ASN.1 encoding");
+ return 0;
+ }
+ return value;
+}
+
+static jboolean NativeCrypto_asn1_read_is_empty(CONSCRYPT_UNUSED JNIEnv* env, jclass, jlong cbsRef) {
+ CbsHandle* cbs = reinterpret_cast<CbsHandle*>(static_cast<uintptr_t>(cbsRef));
+ JNI_TRACE("asn1_read_is_empty(%p)", cbs);
+
+ bool empty = (CBS_len(cbs->cbs.get()) == 0);
+ JNI_TRACE("asn1_read_is_empty(%p) => %s", cbs, empty ? "true" : "false");
+ return empty;
+}
+
+static void NativeCrypto_asn1_read_free(CONSCRYPT_UNUSED JNIEnv* env, jclass, jlong cbsRef) {
+ if (cbsRef == 0) {
+ JNI_TRACE("asn1_read_free(0)");
+ return;
+ }
+ CbsHandle* cbs = reinterpret_cast<CbsHandle*>(static_cast<uintptr_t>(cbsRef));
+ JNI_TRACE("asn1_read_free(%p)", cbs);
+ delete cbs;
+}
+
+static jlong NativeCrypto_asn1_write_init(JNIEnv* env, jclass) {
+ JNI_TRACE("asn1_write_init");
+ std::unique_ptr<CBB> cbb(new CBB());
+ if (!CBB_init(cbb.get(), 128)) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return 0;
+ }
+ JNI_TRACE("asn1_write_init => %p", cbb.get());
+ return reinterpret_cast<uintptr_t>(cbb.release());
+}
+
+static jlong NativeCrypto_asn1_write_sequence(JNIEnv* env, jclass, jlong cbbRef) {
+ CBB* cbb = reinterpret_cast<CBB*>(static_cast<uintptr_t>(cbbRef));
+ JNI_TRACE("asn1_write_sequence(%p)", cbb);
+
+ std::unique_ptr<CBB> seq(new CBB());
+ if (!CBB_add_asn1(cbb, seq.get(), CBS_ASN1_SEQUENCE)) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return 0;
+ }
+ JNI_TRACE("asn1_write_sequence(%p) => %p", cbb, seq.get());
+ return reinterpret_cast<uintptr_t>(seq.release());
+}
+
+static void NativeCrypto_asn1_write_octetstring(JNIEnv* env, jclass, jlong cbbRef, jbyteArray data) {
+ CBB* cbb = reinterpret_cast<CBB*>(static_cast<uintptr_t>(cbbRef));
+ JNI_TRACE("asn1_write_octetstring(%p, %p)", cbb, data);
+
+ ScopedByteArrayRO bytes(env, data);
+ if (bytes.get() == nullptr) {
+ JNI_TRACE("asn1_write_octetstring(%p, %p) => using byte array failed", cbb, data);
+ return;
}
- return X509_NAME_to_jstring(env, x509name, flags);
+ std::unique_ptr<CBB> octetstring(new CBB());
+ if (!CBB_add_asn1(cbb, octetstring.get(), CBS_ASN1_OCTETSTRING)) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return;
+ }
+ if (!CBB_add_bytes(octetstring.get(), reinterpret_cast<const uint8_t*>(bytes.get()), bytes.size())) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return;
+ }
+}
+
+static void NativeCrypto_asn1_write_uint64(JNIEnv* env, jclass, jlong cbbRef, jlong data) {
+ CBB* cbb = reinterpret_cast<CBB*>(static_cast<uintptr_t>(cbbRef));
+ JNI_TRACE("asn1_write_uint64(%p)", cbb);
+
+ if (!CBB_add_asn1_uint64(cbb, static_cast<uint64_t>(data))) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return;
+ }
+}
+
+static jbyteArray NativeCrypto_asn1_write_finish(JNIEnv* env, jclass, jlong cbbRef) {
+ CBB* cbb = reinterpret_cast<CBB*>(static_cast<uintptr_t>(cbbRef));
+ JNI_TRACE("asn1_write_finish(%p)", cbb);
+
+ uint8_t* data;
+ size_t data_len;
+ if (!CBB_finish(cbb, &data, &data_len)) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return 0;
+ }
+ bssl::UniquePtr<uint8_t> data_storage(data);
+ ScopedLocalRef<jbyteArray> out(env, env->NewByteArray(static_cast<jsize>(data_len)));
+ if (out.get() == nullptr) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return 0;
+ }
+ ScopedByteArrayRW outBytes(env, out.get());
+ if (outBytes.get() == nullptr) {
+ Errors::throwIOException(env, "Error writing ASN.1 encoding");
+ return 0;
+ }
+ memcpy(outBytes.get(), data, data_len);
+ return out.release();
+}
+
+static void NativeCrypto_asn1_write_cleanup(CONSCRYPT_UNUSED JNIEnv* env, jclass, jlong cbbRef) {
+ CBB* cbb = reinterpret_cast<CBB*>(static_cast<uintptr_t>(cbbRef));
+ JNI_TRACE("asn1_write_cleanup(%p)", cbb);
+
+ CBB_cleanup(cbb);
+}
+
+static void NativeCrypto_asn1_write_free(CONSCRYPT_UNUSED JNIEnv* env, jclass, jlong cbbRef) {
+ if (cbbRef == 0) {
+ JNI_TRACE("asn1_write_free(0)");
+ return;
+ }
+ CBB* cbb = reinterpret_cast<CBB*>(static_cast<uintptr_t>(cbbRef));
+ JNI_TRACE("asn1_write_free(%p)", cbb);
+ delete cbb;
}
template <typename T, T* (*d2i_func)(BIO*, T**)>
@@ -4646,29 +4678,7 @@
sk_X509_free(stack);
- uint8_t *derBytes;
- size_t derLen;
- if (!CBB_finish(out.get(), &derBytes, &derLen)) {
- Errors::throwExceptionIfNecessary(env, "CBB_finish");
- return nullptr;
- }
-
- ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(static_cast<jsize>(derLen)));
- if (byteArray.get() == nullptr) {
- JNI_TRACE("creating byte array failed");
- return nullptr;
- }
-
- ScopedByteArrayRW bytes(env, byteArray.get());
- if (bytes.get() == nullptr) {
- JNI_TRACE("using byte array failed");
- return nullptr;
- }
-
- uint8_t* p = reinterpret_cast<unsigned char*>(bytes.get());
- memcpy(p, derBytes, derLen);
-
- return byteArray.release();
+ return CBBToByteArray(env, out.get());
}
static jlongArray NativeCrypto_PEM_read_bio_PKCS7(JNIEnv* env, jclass, jlong bioRef, jint which) {
@@ -4847,29 +4857,7 @@
}
}
- uint8_t *out;
- size_t out_len;
- if (!CBB_finish(result.get(), &out, &out_len)) {
- return nullptr;
- }
- std::unique_ptr<uint8_t> out_storage(out);
-
- ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(static_cast<jsize>(out_len)));
- if (byteArray.get() == nullptr) {
- JNI_TRACE("ASN1_seq_pack_X509(%p) => creating byte array failed", certs);
- return nullptr;
- }
-
- ScopedByteArrayRW bytes(env, byteArray.get());
- if (bytes.get() == nullptr) {
- JNI_TRACE("ASN1_seq_pack_X509(%p) => using byte array failed", certs);
- return nullptr;
- }
-
- uint8_t *p = reinterpret_cast<uint8_t*>(bytes.get());
- memcpy(p, out, out_len);
-
- return byteArray.release();
+ return CBBToByteArray(env, result.get());
}
static void NativeCrypto_X509_free(JNIEnv* env, jclass, jlong x509Ref) {
@@ -5846,6 +5834,81 @@
return static_cast<unsigned int>(keyLen);
}
+static int new_session_callback(SSL* ssl, SSL_SESSION* session) {
+ JNI_TRACE("ssl=%p new_session_callback session=%p", ssl, session);
+
+ AppData* appData = toAppData(ssl);
+ JNIEnv* env = appData->env;
+ if (env == nullptr) {
+ ALOGE("AppData->env missing in new_session_callback");
+ JNI_TRACE("ssl=%p new_session_callback env error", ssl);
+ return 0;
+ }
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p new_session_callback already pending exception", ssl);
+ return 0;
+ }
+
+ jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
+ jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
+ jmethodID methodID = env->GetMethodID(cls, "onNewSessionEstablished", "(J)V");
+ JNI_TRACE("ssl=%p new_session_callback calling onNewSessionEstablished", ssl);
+ env->CallVoidMethod(sslHandshakeCallbacks, methodID, reinterpret_cast<jlong>(session));
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p new_session_callback exception cleared", ssl);
+ env->ExceptionClear();
+ }
+ JNI_TRACE("ssl=%p new_session_callback completed", ssl);
+
+ // Always returning 0 (not taking ownership). The Java code is responsible for incrementing
+ // the reference count.
+ return 0;
+}
+
+static SSL_SESSION* server_session_requested_callback(SSL* ssl, uint8_t* id, int id_len,
+ int* out_copy) {
+ JNI_TRACE("ssl=%p server_session_requested_callback", ssl);
+
+ // Always set to out_copy to zero. The Java callback will be responsible for incrementing
+ // the reference count (and any required synchronization).
+ *out_copy = 0;
+
+ AppData* appData = toAppData(ssl);
+ JNIEnv* env = appData->env;
+ if (env == nullptr) {
+ ALOGE("AppData->env missing in server_session_requested_callback");
+ JNI_TRACE("ssl=%p server_session_requested_callback env error", ssl);
+ return 0;
+ }
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p server_session_requested_callback already pending exception", ssl);
+ return 0;
+ }
+
+ // Copy the ID to a byte[].
+ jbyteArray id_array = env->NewByteArray(static_cast<jsize>(id_len));
+ if (id_array == nullptr) {
+ JNI_TRACE("ssl=%p id_array bytes == null => 0", ssl);
+ return 0;
+ }
+ env->SetByteArrayRegion(id_array, 0, static_cast<jsize>(id_len),
+ reinterpret_cast<const jbyte*>(id));
+
+ jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
+ jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
+ jmethodID methodID = env->GetMethodID(cls, "serverSessionRequested", "([B)J");
+ JNI_TRACE("ssl=%p server_session_requested_callback calling serverSessionRequested", ssl);
+ jlong ssl_session_address = env->CallLongMethod(sslHandshakeCallbacks, methodID, id_array);
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p server_session_requested_callback exception cleared", ssl);
+ env->ExceptionClear();
+ }
+ SSL_SESSION* ssl_session_ptr = reinterpret_cast<SSL_SESSION*>(
+ static_cast<uintptr_t>(ssl_session_address));
+ JNI_TRACE("ssl=%p server_session_requested_callback completed => %p", ssl, ssl_session_ptr);
+ return ssl_session_ptr;
+}
+
static jint NativeCrypto_EVP_has_aes_hardware(JNIEnv*, jclass) {
int ret = 0;
ret = EVP_has_aes_hardware();
@@ -5959,6 +6022,14 @@
SSL_CTX_set_keylog_callback(sslCtx.get(), debug_print_session_key);
}
+ // By default BoringSSL will cache in server mode, but we want to get
+ // notified of new sessions being created in client mode. We set
+ // SSL_SESS_CACHE_BOTH in order to get the callback in client mode, but
+ // ignore it in server mode in favor of the internal cache.
+ SSL_CTX_set_session_cache_mode(sslCtx.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_sess_set_new_cb(sslCtx.get(), new_session_callback);
+ SSL_CTX_sess_set_get_cb(sslCtx.get(), server_session_requested_callback);
+
// Disable RSA-PSS deliberately until CryptoUpcalls supports it.
if (!SSL_CTX_set_signing_algorithm_prefs(
sslCtx.get(), kDefaultSignatureAlgorithms,
@@ -6016,6 +6087,18 @@
JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_session_id_context => ok", ssl_ctx);
}
+static jlong NativeCrypto_SSL_CTX_set_timeout(JNIEnv* env, jclass, jlong ssl_ctx_address,
+ jlong seconds)
+{
+ SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_set_timeout seconds=%d", ssl_ctx, (int) seconds);
+ if (ssl_ctx == nullptr) {
+ return 0L;
+ }
+
+ return SSL_CTX_set_timeout(ssl_ctx, static_cast<uint32_t>(seconds));
+}
+
/**
* public static native int SSL_new(long ssl_ctx) throws SSLException;
*/
@@ -6315,20 +6398,6 @@
}
/**
- * public static native long SSL_get_mode(long ssl);
- */
-static jlong NativeCrypto_SSL_get_mode(JNIEnv* env, jclass, jlong ssl_address) {
- SSL* ssl = to_SSL(env, ssl_address, true);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode", ssl);
- if (ssl == nullptr) {
- return 0;
- }
- long mode = static_cast<long>(SSL_get_mode(ssl));
- JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode => 0x%lx", ssl, mode);
- return mode;
-}
-
-/**
* public static native long SSL_set_mode(long ssl, long mode);
*/
static jlong NativeCrypto_SSL_set_mode(JNIEnv* env, jclass,
@@ -6344,36 +6413,6 @@
}
/**
- * public static native long SSL_clear_mode(long ssl, long mode);
- */
-static jlong NativeCrypto_SSL_clear_mode(JNIEnv* env, jclass,
- jlong ssl_address, jlong mode) {
- SSL* ssl = to_SSL(env, ssl_address, true);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode mode=0x%llx", ssl, (long long) mode);
- if (ssl == nullptr) {
- return 0;
- }
- long result = static_cast<long>(SSL_clear_mode(ssl, static_cast<uint32_t>(mode)));
- JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode => 0x%lx", ssl, result);
- return result;
-}
-
-/**
- * public static native long SSL_get_options(long ssl);
- */
-static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
- jlong ssl_address) {
- SSL* ssl = to_SSL(env, ssl_address, true);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options", ssl);
- if (ssl == nullptr) {
- return 0;
- }
- long options = static_cast<long>(SSL_get_options(ssl));
- JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options => 0x%lx", ssl, options);
- return options;
-}
-
-/**
* public static native long SSL_set_options(long ssl, long options);
*/
static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass,
@@ -7108,33 +7147,27 @@
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => success", ssl);
}
-/**
- * Perform SSL renegotiation
- */
-static void NativeCrypto_SSL_renegotiate(JNIEnv* env, jclass, jlong ssl_address)
-{
+static jstring NativeCrypto_SSL_get_current_cipher(JNIEnv* env, jclass, jlong ssl_address) {
SSL* ssl = to_SSL(env, ssl_address, true);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_renegotiate", ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_current_cipher", ssl);
if (ssl == nullptr) {
- return;
+ return nullptr;
}
- int result = SSL_renegotiate(ssl);
- if (result != 1) {
- Errors::throwSSLExceptionStr(env, "Problem with SSL_renegotiate");
- return;
+ const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
+ const char* name = SSL_CIPHER_standard_name(cipher);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_current_cipher => %s", ssl, name);
+ return env->NewStringUTF(name);
+}
+
+static jstring NativeCrypto_SSL_get_version(JNIEnv* env, jclass, jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_version", ssl);
+ if (ssl == nullptr) {
+ return nullptr;
}
- // first call asks client to perform renegotiation
- int ret = SSL_do_handshake(ssl);
- if (ret != 1) {
- OpenSslError sslError(ssl, ret);
- Errors::throwSSLExceptionWithSslErrors(env, ssl, sslError.release(),
- "Problem with SSL_do_handshake after SSL_renegotiate");
- return;
- }
- // if client agrees, set ssl state and perform renegotiation
- SSL_set_state(ssl, SSL_ST_ACCEPT);
- SSL_do_handshake(ssl);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_renegotiate =>", ssl);
+ const char* protocol = SSL_get_version(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_version => %s", ssl, protocol);
+ return env->NewStringUTF(protocol);
}
/**
@@ -7954,6 +7987,114 @@
}
/**
+ * Gets and returns in a long integer the creation's time of the
+ * actual SSL session.
+ */
+static jlong NativeCrypto_SSL_get_time(JNIEnv* env, jclass, jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_time", ssl);
+ if (ssl == nullptr) {
+ return 0;
+ }
+
+ SSL_SESSION* ssl_session = SSL_get_session(ssl);
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_get_time", ssl_session);
+ if (ssl_session == nullptr) {
+ // BoringSSL does not protect against a NULL session.
+ return 0;
+ }
+ // result must be jlong, not long or *1000 will overflow
+ jlong result = SSL_SESSION_get_time(ssl_session);
+ result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_get_time => %lld", ssl_session, (long long) result);
+ return result;
+}
+
+/**
+ * Sets the timeout on the SSL session.
+ */
+static jlong NativeCrypto_SSL_set_timeout(JNIEnv* env, jclass, jlong ssl_address, jlong millis) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_timeout", ssl);
+ if (ssl == nullptr) {
+ return 0;
+ }
+
+ SSL_SESSION* ssl_session = SSL_get_session(ssl);
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_set_timeout", ssl_session);
+ if (ssl_session == nullptr) {
+ // BoringSSL does not protect against a NULL session.
+ return 0;
+ }
+
+ // Convert to seconds
+ long timeout = millis / 1000;
+ return SSL_set_timeout(ssl_session, timeout);
+}
+
+/**
+ * Gets the timeout for the SSL session.
+ */
+static jlong NativeCrypto_SSL_get_timeout(JNIEnv* env, jclass, jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_timeout", ssl);
+ if (ssl == nullptr) {
+ return 0;
+ }
+
+ SSL_SESSION* ssl_session = SSL_get_session(ssl);
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_get_timeout", ssl_session);
+ if (ssl_session == nullptr) {
+ // BoringSSL does not protect against a NULL session.
+ return 0;
+ }
+
+ jlong result = SSL_get_timeout(ssl_session);
+ result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_get_timeout => %lld", ssl_session, (long long) result);
+ return result;
+}
+
+/**
+ * Gets the timeout for the SSL session.
+ */
+static jlong NativeCrypto_SSL_SESSION_get_timeout(JNIEnv* env, jclass, jlong ssl_session_address) {
+ SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true);
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_timeout", ssl_session);
+ if (ssl_session == nullptr) {
+ return 0;
+ }
+
+ return SSL_get_timeout(ssl_session);
+}
+
+/**
+ * Gets the ID for the SSL session, or null if no session is currently available.
+ */
+static jbyteArray NativeCrypto_SSL_session_id(JNIEnv* env, jclass, jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_session_id", ssl);
+ if (ssl == nullptr) {
+ return nullptr;
+ }
+
+ SSL_SESSION* ssl_session = SSL_get_session(ssl);
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_session_id", ssl_session);
+ if (ssl_session == nullptr) {
+ return nullptr;
+ }
+
+ jbyteArray result = env->NewByteArray(static_cast<jsize>(ssl_session->session_id_length));
+ if (result != nullptr) {
+ jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
+ env->SetByteArrayRegion(result, 0, static_cast<jsize>(ssl_session->session_id_length), src);
+ }
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_session_id => %p session_id_length=%d",
+ ssl_session, result, ssl_session->session_id_length);
+ return result;
+}
+
+/**
* Gets and returns in a string the version of the SSL protocol. If it
* returns the string "unknown" it means that no connection is established.
*/
@@ -7978,22 +8119,21 @@
return nullptr;
}
const SSL_CIPHER* cipher = ssl_session->cipher;
- const char* name = SSL_CIPHER_get_name(cipher);
+ const char* name = SSL_CIPHER_standard_name(cipher);
JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher => %s", ssl_session, name);
return env->NewStringUTF(name);
}
-static jstring NativeCrypto_get_SSL_SESSION_tlsext_hostname(JNIEnv* env, jclass, jlong sessionJava) {
- SSL_SESSION* ssl_session = to_SSL_SESSION(env, sessionJava, true);
- JNI_TRACE("ssl_session=%p NativeCrypto_get_SSL_SESSION_tlsext_hostname", ssl_session);
- if (ssl_session == nullptr || ssl_session->tlsext_hostname == nullptr) {
- JNI_TRACE("ssl_session=%p NativeCrypto_get_SSL_SESSION_tlsext_hostname => null",
- ssl_session);
- return nullptr;
+/**
+ * Increments the reference count of the session.
+ */
+static void NativeCrypto_SSL_SESSION_up_ref(JNIEnv* env, jclass, jlong ssl_session_address) {
+ SSL_SESSION* ssl_session = to_SSL_SESSION(env, ssl_session_address, true);
+ JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_up_ref", ssl_session);
+ if (ssl_session == nullptr) {
+ return;
}
- JNI_TRACE("ssl_session=%p NativeCrypto_get_SSL_SESSION_tlsext_hostname => \"%s\"",
- ssl_session, ssl_session->tlsext_hostname);
- return env->NewStringUTF(ssl_session->tlsext_hostname);
+ SSL_SESSION_up_ref(ssl_session);
}
/**
@@ -8083,18 +8223,27 @@
size_t size = sk_SSL_CIPHER_num(ciphers);
ScopedLocalRef<jobjectArray> cipherNamesArray(
- env, env->NewObjectArray(static_cast<jsize>(size), JniConstants::stringClass, nullptr));
+ env,
+ env->NewObjectArray(static_cast<jsize>(2 * size), JniConstants::stringClass, nullptr));
if (cipherNamesArray.get() == nullptr) {
return nullptr;
}
+ // Return an array of standard and OpenSSL name pairs.
for (size_t i = 0; i < size; i++) {
- const char *name = SSL_CIPHER_get_name(sk_SSL_CIPHER_value(ciphers, i));
- ScopedLocalRef<jstring> cipherName(env, env->NewStringUTF(name));
- env->SetObjectArrayElement(cipherNamesArray.get(), static_cast<jsize>(i), cipherName.get());
+ const SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i);
+ ScopedLocalRef<jstring> cipherName(env,
+ env->NewStringUTF(SSL_CIPHER_standard_name(cipher)));
+ env->SetObjectArrayElement(cipherNamesArray.get(), static_cast<jsize>(2 * i),
+ cipherName.get());
+
+ ScopedLocalRef<jstring> opensslName(env, env->NewStringUTF(SSL_CIPHER_get_name(cipher)));
+ env->SetObjectArrayElement(cipherNamesArray.get(), static_cast<jsize>(2 * i + 1),
+ opensslName.get());
}
- JNI_TRACE("NativeCrypto_get_cipher_names(%s) => success (%zd entries)", selector.c_str(), size);
+ JNI_TRACE("NativeCrypto_get_cipher_names(%s) => success (%zd entries)", selector.c_str(),
+ 2 * size);
return cipherNamesArray.release();
}
@@ -8389,22 +8538,6 @@
return static_cast<jint>(BIO_ctrl_pending(bio));
}
-static jlong NativeCrypto_SSL_get0_session(JNIEnv* env, jclass, jlong ssl_address) {
- SSL* ssl = to_SSL(env, ssl_address, true);
- if (ssl == nullptr) {
- return 0;
- }
- return reinterpret_cast<uintptr_t>(SSL_get0_session(ssl));
-}
-
-static jlong NativeCrypto_SSL_get1_session(JNIEnv* env, jclass, jlong ssl_address) {
- SSL* ssl = to_SSL(env, ssl_address, true);
- if (ssl == nullptr) {
- return 0;
- }
- return reinterpret_cast<uintptr_t>(SSL_get1_session(ssl));
-}
-
static jint NativeCrypto_SSL_max_seal_overhead(JNIEnv* env, jclass, jlong ssl_address) {
SSL* ssl = to_SSL(env, ssl_address, true);
if (ssl == nullptr) {
@@ -8505,6 +8638,9 @@
JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_do_handshake appData => 0", ssl);
return 0;
}
+
+ errno = 0;
+
if (!appData->setCallbackState(env, shc, nullptr)) {
Errors::throwSSLExceptionStr(env, "Unable to set appdata callback");
ERR_clear_error();
@@ -8513,8 +8649,6 @@
return 0;
}
- errno = 0;
-
int ret = SSL_do_handshake(ssl);
appData->clearCallbackState();
if (env->ExceptionCheck()) {
@@ -8525,8 +8659,47 @@
return 0;
}
- JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_do_handshake shc=%p => ret=%d", ssl, shc, ret);
- return ret;
+ OpenSslError sslError(ssl, ret);
+ int code = sslError.get();
+
+ if (ret > 0 || code == SSL_ERROR_WANT_READ || code == SSL_ERROR_WANT_WRITE) {
+ // Non-exceptional case.
+ JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_do_handshake shc=%p => ret=%d", ssl, shc, code);
+ return code;
+ }
+
+ // Exceptional case...
+ if (ret == 0) {
+ // TODO(nmittler): Can this happen with memory BIOs?
+ /*
+ * Clean error. See SSL_do_handshake(3SSL) man page.
+ * The other side closed the socket before the handshake could be
+ * completed, but everything is within the bounds of the TLS protocol.
+ * We still might want to find out the real reason of the failure.
+ */
+ if (code == SSL_ERROR_NONE || (code == SSL_ERROR_SYSCALL && errno == 0) ||
+ (code == SSL_ERROR_ZERO_RETURN)) {
+ Errors::throwSSLHandshakeExceptionStr(env, "Connection closed by peer");
+ } else {
+ Errors::throwSSLExceptionWithSslErrors(env, ssl, sslError.release(),
+ "SSL handshake terminated",
+ Errors::throwSSLHandshakeExceptionStr);
+ }
+ safeSslClear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake clean error => exception", ssl);
+ return code;
+ }
+
+ /*
+ * Unclean error. See SSL_do_handshake(3SSL) man page.
+ * Translate the error and throw exception. We are sure it is an error
+ * at this point.
+ */
+ Errors::throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "SSL handshake aborted",
+ Errors::throwSSLHandshakeExceptionStr);
+ safeSslClear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake unclean error => exception", ssl);
+ return code;
}
static void NativeCrypto_ENGINE_SSL_shutdown(JNIEnv* env, jclass, jlong ssl_address, jobject shc) {
@@ -8595,6 +8768,91 @@
safeSslClear(ssl);
}
+static int doEngineRead(JNIEnv* env, const char* methodName, SSL* ssl, jobject shc, char* destPtr,
+ int length) {
+ if (shc == nullptr) {
+ Errors::jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
+ JNI_TRACE("ssl=%p %s => sslHandshakeCallbacks == null", ssl, methodName);
+ return -1;
+ }
+ AppData* appData = toAppData(ssl);
+ if (appData == nullptr) {
+ Errors::throwSSLExceptionStr(env, "Unable to retrieve application data");
+ safeSslClear(ssl);
+ JNI_TRACE("ssl=%p %s => appData == null", ssl, methodName);
+ return -1;
+ }
+ if (!appData->setCallbackState(env, shc, nullptr)) {
+ Errors::throwSSLExceptionStr(env, "Unable to set appdata callback");
+ ERR_clear_error();
+ safeSslClear(ssl);
+ JNI_TRACE("ssl=%p %s => exception", ssl, methodName);
+ return -1;
+ }
+
+ errno = 0;
+
+ int result = SSL_read(ssl, destPtr, length);
+ appData->clearCallbackState();
+ if (env->ExceptionCheck()) {
+ // An exception was thrown by one of the callbacks. Just propagate that exception.
+ safeSslClear(ssl);
+ JNI_TRACE("ssl=%p %s => THROWN_EXCEPTION", ssl, methodName);
+ return -1;
+ }
+
+ OpenSslError sslError(ssl, result);
+ switch (sslError.get()) {
+ case SSL_ERROR_NONE: {
+ // Successfully read at least one byte. Just return the result.
+ break;
+ }
+ case SSL_ERROR_ZERO_RETURN: {
+ // TODO(nmittler): Can this happen with memory BIOs?
+ // Read zero bytes. End of stream reached.
+ Errors::jniThrowException(env, "java/io/EOFException", "Read error");
+ break;
+ }
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE: {
+ // Return the negative of these values.
+ result = -sslError.get();
+ break;
+ }
+ case SSL_ERROR_SYSCALL: {
+ // A problem occurred during a system call, but this is not
+ // necessarily an error.
+ if (result == 0) {
+ // TODO(nmittler): Can this happen with memory BIOs?
+ // Connection closed without proper shutdown. Tell caller we
+ // have reached end-of-stream.
+ Errors::jniThrowException(env, "java/io/EOFException", "Read error");
+ break;
+ }
+
+ if (errno == EINTR) {
+ // TODO(nmittler): Can this happen with memory BIOs?
+ // System call has been interrupted. Simply retry.
+ Errors::jniThrowException(env, "java/io/InterruptedIOException", "Read error");
+ break;
+ }
+
+ // Note that for all other system call errors we fall through
+ // to the default case, which results in an Exception.
+ FALLTHROUGH_INTENDED;
+ }
+ default: {
+ // Everything else is basically an error.
+ Errors::throwSSLExceptionWithSslErrors(env, ssl, sslError.release(), "Read error");
+ break;
+ }
+ }
+
+ JNI_TRACE("ssl=%p %s address=%p length=%d shc=%p result=%d", ssl, methodName, destPtr, length,
+ shc, result);
+ return result;
+}
+
static jint NativeCrypto_ENGINE_SSL_read_direct(JNIEnv* env, jclass, jlong sslRef, jlong address,
jint length, jobject shc) {
SSL* ssl = to_SSL(env, sslRef, true);
@@ -8605,36 +8863,7 @@
JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_direct address=%p length=%d shc=%p", ssl,
destPtr, length, shc);
- if (shc == nullptr) {
- Errors::jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
- JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_direct => sslHandshakeCallbacks == null",
- ssl);
- return -1;
- }
-
- AppData* appData = toAppData(ssl);
- if (appData == nullptr) {
- Errors::throwSSLExceptionStr(env, "Unable to retrieve application data");
- safeSslClear(ssl);
- JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_direct => appData == null", ssl);
- return -1;
- }
- if (!appData->setCallbackState(env, shc, nullptr)) {
- Errors::throwSSLExceptionStr(env, "Unable to set appdata callback");
- ERR_clear_error();
- safeSslClear(ssl);
- JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_direct => exception", ssl);
- return -1;
- }
-
- errno = 0;
-
- int result = SSL_read(ssl, destPtr, length);
- appData->clearCallbackState();
-
- JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_direct address=%p length=%d shc=%p result=%d",
- ssl, destPtr, length, shc, result);
- return result;
+ return doEngineRead(env, "NativeCrypto_ENGINE_SSL_read_direct", ssl, shc, destPtr, length);
}
static jint NativeCrypto_ENGINE_SSL_read_heap(JNIEnv* env, jclass, jlong sslRef,
@@ -8644,11 +8873,6 @@
if (ssl == nullptr) {
return -1;
}
- if (shc == nullptr) {
- Errors::jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
- JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_heap => sslHandshakeCallbacks == null", ssl);
- return -1;
- }
ScopedByteArrayRW dest(env, destJava);
if (dest.get() == nullptr) {
JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_read_heap => threw exception", ssl);
@@ -8663,32 +8887,8 @@
return -1;
}
- AppData* appData = toAppData(ssl);
- if (appData == nullptr) {
- Errors::throwSSLExceptionStr(env, "Unable to retrieve application data");
- safeSslClear(ssl);
- ERR_clear_error();
- JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_netty appData => null", ssl);
- return -1;
- }
- if (!appData->setCallbackState(env, shc, nullptr)) {
- Errors::throwSSLExceptionStr(env, "Unable to set appdata callback");
- ERR_clear_error();
- safeSslClear(ssl);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_netty => exception", ssl);
- return -1;
- }
-
- errno = 0;
-
- int result = SSL_read(ssl, reinterpret_cast<char*>(dest.get()) + destOffset, destLength);
- appData->clearCallbackState();
-
- JNI_TRACE(
- "ssl=%p NativeCrypto_ENGINE_SSL_read_heap dest=%p destOffset=%d destLength=%d shc=%p "
- "=> ret=%d",
- ssl, dest.get(), destOffset, destLength, shc, result);
- return result;
+ return doEngineRead(env, "NativeCrypto_ENGINE_SSL_read_heap", ssl, shc,
+ reinterpret_cast<char*>(dest.get()) + destOffset, destLength);
}
static int NativeCrypto_ENGINE_SSL_write_BIO_direct(JNIEnv* env, jclass, jlong sslRef, jlong bioRef,
@@ -9004,7 +9204,7 @@
Errors::throwSSLExceptionStr(env, "Unable to set appdata callback");
ERR_clear_error();
safeSslClear(ssl);
- JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_netty => exception", ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_ENGINE_SSL_write_heap => exception", ssl);
return -1;
}
@@ -9020,6 +9220,127 @@
return result;
}
+// TESTING METHODS BEGIN
+
+static int NativeCrypto_BIO_read(JNIEnv* env, jclass, jlong bioRef, jbyteArray outputJavaBytes) {
+ BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
+ JNI_TRACE("BIO_read(%p, %p)", bio, outputJavaBytes);
+
+ if (outputJavaBytes == nullptr) {
+ Errors::jniThrowNullPointerException(env, "output == null");
+ JNI_TRACE("BIO_read(%p, %p) => output == null", bio, outputJavaBytes);
+ return 0;
+ }
+
+ jsize outputSize = env->GetArrayLength(outputJavaBytes);
+
+ std::unique_ptr<unsigned char[]> buffer(
+ new unsigned char[static_cast<unsigned int>(outputSize)]);
+ if (buffer.get() == nullptr) {
+ Errors::jniThrowOutOfMemory(env, "Unable to allocate buffer for read");
+ return 0;
+ }
+
+ int read = BIO_read(bio, buffer.get(), static_cast<int>(outputSize));
+ if (read <= 0) {
+ Errors::throwIOException(env, "BIO_read");
+ JNI_TRACE("BIO_read(%p, %p) => threw IO exception", bio, outputJavaBytes);
+ return 0;
+ }
+
+ env->SetByteArrayRegion(outputJavaBytes, 0, read, reinterpret_cast<jbyte*>(buffer.get()));
+ JNI_TRACE("BIO_read(%p, %p) => %d", bio, outputJavaBytes, read);
+ return read;
+}
+
+static void NativeCrypto_BIO_write(JNIEnv* env, jclass, jlong bioRef, jbyteArray inputJavaBytes,
+ jint offset, jint length) {
+ BIO* bio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(bioRef));
+ JNI_TRACE("BIO_write(%p, %p, %d, %d)", bio, inputJavaBytes, offset, length);
+
+ if (inputJavaBytes == nullptr) {
+ Errors::jniThrowNullPointerException(env, "input == null");
+ return;
+ }
+
+ int inputSize = env->GetArrayLength(inputJavaBytes);
+ if (offset < 0 || offset > inputSize || length < 0 || length > inputSize - offset) {
+ Errors::jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inputJavaBytes");
+ JNI_TRACE("BIO_write(%p, %p, %d, %d) => IOOB", bio, inputJavaBytes, offset, length);
+ return;
+ }
+
+ std::unique_ptr<unsigned char[]> buffer(new unsigned char[static_cast<unsigned int>(length)]);
+ if (buffer.get() == nullptr) {
+ Errors::jniThrowOutOfMemory(env, "Unable to allocate buffer for write");
+ return;
+ }
+
+ env->GetByteArrayRegion(inputJavaBytes, offset, length, reinterpret_cast<jbyte*>(buffer.get()));
+ if (BIO_write(bio, buffer.get(), length) != length) {
+ ERR_clear_error();
+ Errors::throwIOException(env, "BIO_write");
+ JNI_TRACE("BIO_write(%p, %p, %d, %d) => IO error", bio, inputJavaBytes, offset, length);
+ return;
+ }
+
+ JNI_TRACE("BIO_write(%p, %p, %d, %d) => success", bio, inputJavaBytes, offset, length);
+}
+
+/**
+ * public static native long SSL_clear_mode(long ssl, long mode);
+ */
+static jlong NativeCrypto_SSL_clear_mode(JNIEnv* env, jclass,
+ jlong ssl_address, jlong mode) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode mode=0x%llx", ssl, (long long) mode);
+ if (ssl == nullptr) {
+ return 0;
+ }
+ long result = static_cast<long>(SSL_clear_mode(ssl, static_cast<uint32_t>(mode)));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode => 0x%lx", ssl, result);
+ return result;
+}
+
+/**
+ * public static native long SSL_get_mode(long ssl);
+ */
+static jlong NativeCrypto_SSL_get_mode(JNIEnv* env, jclass, jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode", ssl);
+ if (ssl == nullptr) {
+ return 0;
+ }
+ long mode = static_cast<long>(SSL_get_mode(ssl));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode => 0x%lx", ssl, mode);
+ return mode;
+}
+
+/**
+ * public static native long SSL_get_options(long ssl);
+ */
+static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
+ jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options", ssl);
+ if (ssl == nullptr) {
+ return 0;
+ }
+ long options = static_cast<long>(SSL_get_options(ssl));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options => 0x%lx", ssl, options);
+ return options;
+}
+
+static jlong NativeCrypto_SSL_get1_session(JNIEnv* env, jclass, jlong ssl_address) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ if (ssl == nullptr) {
+ return 0;
+ }
+ return reinterpret_cast<uintptr_t>(SSL_get1_session(ssl));
+}
+
+// TESTING METHODS END
+
#define CONSCRYPT_NATIVE_METHOD(className, functionName, signature) \
{ \
(char*)#functionName, (char*)(signature), \
@@ -9043,15 +9364,14 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_RSA, "([B[B[B[B[B[B[B[B)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_EC_KEY, "(" REF_EC_GROUP REF_EC_POINT "[B)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_type, "(" REF_EVP_PKEY ")I"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_size, "(" REF_EVP_PKEY ")I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_print_public, "(" REF_EVP_PKEY ")Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_print_params, "(" REF_EVP_PKEY ")Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_free, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_PKEY_cmp, "(" REF_EVP_PKEY REF_EVP_PKEY ")I"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, i2d_PKCS8_PRIV_KEY_INFO, "(" REF_EVP_PKEY ")[B"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, d2i_PKCS8_PRIV_KEY_INFO, "([B)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, i2d_PUBKEY, "(" REF_EVP_PKEY ")[B"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, d2i_PUBKEY, "([B)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_marshal_private_key, "(" REF_EVP_PKEY ")[B"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_parse_private_key, "([B)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_marshal_public_key, "(" REF_EVP_PKEY ")[B"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_parse_public_key, "([B)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, PEM_read_bio_PUBKEY, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, PEM_read_bio_PrivateKey, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, getRSAPrivateKeyWrapper, "(Ljava/security/PrivateKey;[B)J"),
@@ -9098,7 +9418,6 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_DigestUpdateDirect, "(" REF_EVP_MD_CTX "JI)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_DigestFinal_ex, "(" REF_EVP_MD_CTX "[BI)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_get_digestbyname, "(Ljava/lang/String;)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_MD_block_size, "(J)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_MD_size, "(J)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_DigestSignInit, "(" REF_EVP_MD_CTX "J" REF_EVP_PKEY ")J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdate, "(" REF_EVP_MD_CTX "[BII)V"),
@@ -9134,7 +9453,6 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_aead_aes_256_gcm, "()J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_AEAD_max_overhead, "(J)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_AEAD_nonce_length, "(J)I"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_AEAD_max_tag_len, "(J)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_AEAD_CTX_seal, "(J[BI[BI[B[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_AEAD_CTX_open, "(J[BI[BI[B[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, HMAC_CTX_new, "()J"),
@@ -9144,15 +9462,9 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, HMAC_UpdateDirect, "(" REF_HMAC_CTX "JI)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, HMAC_Final, "(" REF_HMAC_CTX ")[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, RAND_bytes, "([B)V"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, OBJ_txt2nid, "(Ljava/lang/String;)I"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, OBJ_txt2nid_longName, "(Ljava/lang/String;)Ljava/lang/String;"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, OBJ_txt2nid_oid, "(Ljava/lang/String;)Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, create_BIO_InputStream, ("(" REF_BIO_IN_STREAM "Z)J")),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, create_BIO_OutputStream, "(Ljava/io/OutputStream;)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, BIO_read, "(J[B)I"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, BIO_write, "(J[BII)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, BIO_free_all, "(J)V"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, X509_NAME_print_ex, "(JJ)Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, d2i_X509_bio, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, d2i_X509, "([B)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, i2d_X509, "(J)[B"),
@@ -9221,10 +9533,24 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, i2d_X509_REVOKED, "(J)[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, X509_supported_extension, "(J)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, ASN1_TIME_to_Calendar, "(JLjava/util/Calendar;)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_read_init, "([B)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_read_sequence, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_read_octetstring, "(J)[B"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_read_uint64, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_read_is_empty, "(J)Z"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_read_free, "(J)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_init, "()J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_sequence, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_octetstring, "(J[B)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_uint64, "(JJ)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_cleanup, "(J)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_finish, "(J)[B"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, asn1_write_free, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, EVP_has_aes_hardware, "()I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_CTX_new, "()J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_CTX_free, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_CTX_set_session_id_context, "(J[B)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_CTX_set_timeout, "(JJ)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_new, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_enable_tls_channel_id, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_tls_channel_id, "(J)[B"),
@@ -9233,10 +9559,7 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_use_certificate, "(J[J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_check_private_key, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_set_client_CA_list, "(J[[B)V"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_mode, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_set_mode, "(JJ)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_clear_mode, "(JJ)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_options, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_set_options, "(JJ)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_clear_options, "(JJ)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_enable_signed_cert_timestamps, "(J)V"),
@@ -9260,7 +9583,8 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_set_tlsext_host_name, "(JLjava/lang/String;)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_servername, "(J)Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "I)V"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_renegotiate, "(J)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_current_cipher, "(J)Ljava/lang/String;"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_version, "(J)Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_certificate, "(J)[J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_peer_cert_chain, "(J)[J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_read, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)I"),
@@ -9272,9 +9596,14 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_free, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_session_id, "(J)[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_time, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_time, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_set_timeout, "(JJ)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_timeout, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_timeout, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_session_id, "(J)[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_version, "(J)Ljava/lang/String;"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_cipher, "(J)Ljava/lang/String;"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, get_SSL_SESSION_tlsext_hostname, "(J)Ljava/lang/String;"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_up_ref, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_SESSION_free, "(J)V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, i2d_SSL_SESSION, "(J)[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, d2i_SSL_SESSION, "([B)J"),
@@ -9285,8 +9614,6 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, get_ocsp_single_extension, "([BLjava/lang/String;JJ)[B"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, getDirectBufferAddress, "(Ljava/nio/Buffer;)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_BIO_new, "(J)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get0_session, "(J)J"),
- CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get1_session, "(J)J"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_max_seal_overhead, "(J)I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_clear_error, "()V"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_pending_readable_bytes, "(J)I"),
@@ -9305,6 +9632,14 @@
CONSCRYPT_NATIVE_METHOD(NativeCrypto, ENGINE_SSL_write_BIO_heap, "(JJ[BII" SSL_CALLBACKS ")I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, ENGINE_SSL_read_BIO_heap, "(JJ[BII" SSL_CALLBACKS ")I"),
CONSCRYPT_NATIVE_METHOD(NativeCrypto, ENGINE_SSL_shutdown, "(J" SSL_CALLBACKS ")V"),
+
+ // Used for testing only.
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, BIO_read, "(J[B)I"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, BIO_write, "(J[BII)V"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_clear_mode, "(JJ)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_mode, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get_options, "(J)J"),
+ CONSCRYPT_NATIVE_METHOD(NativeCrypto, SSL_get1_session, "(J)J"),
};
void NativeCrypto::registerNativeMethods(JNIEnv* env) {
diff --git a/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java b/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java
new file mode 100644
index 0000000..df6fbaa
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/AbstractConscryptSocket.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 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 static org.conscrypt.Preconditions.checkArgument;
+import static org.conscrypt.Preconditions.checkNotNull;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SocketChannel;
+import java.security.PrivateKey;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Abstract base class for all Conscrypt sockets that extends the basic {@link SSLSocket} API.
+ */
+abstract class AbstractConscryptSocket extends SSLSocket {
+ final Socket socket;
+ private final boolean autoClose;
+
+ /**
+ * The peer's DNS hostname if it was supplied during creation. Note that
+ * this may be a raw IP address, so it should be checked before use with
+ * extensions that don't use it like Server Name Indication (SNI).
+ */
+ private String peerHostname;
+
+ /**
+ * The peer's port if it was supplied during creation. Should only be set if
+ * {@link #peerHostname} is also set.
+ */
+ private final int peerPort;
+
+ private final PeerInfoProvider peerInfoProvider = new PeerInfoProvider() {
+ @Override
+ String getHostname() {
+ return AbstractConscryptSocket.this.getHostname();
+ }
+
+ @Override
+ String getHostnameOrIP() {
+ return AbstractConscryptSocket.this.getHostnameOrIP();
+ }
+
+ @Override
+ int getPort() {
+ return AbstractConscryptSocket.this.getPort();
+ }
+ };
+
+ private final List<HandshakeCompletedListener> listeners =
+ new ArrayList<HandshakeCompletedListener>(2);
+
+ /**
+ * Local cache of timeout to avoid getsockopt on every read and
+ * write for non-wrapped sockets. Note that this is not used when delegating
+ * to another socket.
+ */
+ private int readTimeoutMilliseconds;
+
+ AbstractConscryptSocket() throws IOException {
+ this.socket = this;
+ this.peerHostname = null;
+ this.peerPort = -1;
+ this.autoClose = false;
+ }
+
+ AbstractConscryptSocket(String hostname, int port) throws IOException {
+ super(hostname, port);
+ this.socket = this;
+ this.peerHostname = hostname;
+ this.peerPort = port;
+ this.autoClose = false;
+ }
+
+ AbstractConscryptSocket(InetAddress address, int port) throws IOException {
+ super(address, port);
+ this.socket = this;
+ this.peerHostname = null;
+ this.peerPort = -1;
+ this.autoClose = false;
+ }
+
+ AbstractConscryptSocket(String hostname, int port, InetAddress clientAddress, int clientPort)
+ throws IOException {
+ super(hostname, port, clientAddress, clientPort);
+ this.socket = this;
+ this.peerHostname = hostname;
+ this.peerPort = port;
+ this.autoClose = false;
+ }
+
+ AbstractConscryptSocket(InetAddress address, int port, InetAddress clientAddress,
+ int clientPort) throws IOException {
+ super(address, port, clientAddress, clientPort);
+ this.socket = this;
+ this.peerHostname = null;
+ this.peerPort = -1;
+ this.autoClose = false;
+ }
+
+ AbstractConscryptSocket(Socket socket, String hostname, int port, boolean autoClose)
+ throws IOException {
+ this.socket = checkNotNull(socket, "socket");
+ this.peerHostname = hostname;
+ this.peerPort = port;
+ this.autoClose = autoClose;
+ }
+
+ @Override
+ public final void connect(SocketAddress endpoint) throws IOException {
+ connect(endpoint, 0);
+ }
+
+ /**
+ * Try to extract the peer's hostname if it's available from the endpoint address.
+ */
+ @Override
+ public final void connect(SocketAddress endpoint, int timeout) throws IOException {
+ if (peerHostname == null && endpoint instanceof InetSocketAddress) {
+ peerHostname =
+ Platform.getHostStringFromInetSocketAddress((InetSocketAddress) endpoint);
+ }
+
+ if (isDelegating()) {
+ socket.connect(endpoint, timeout);
+ } else {
+ super.connect(endpoint, timeout);
+ }
+ }
+
+ @Override
+ public void bind(SocketAddress bindpoint) throws IOException {
+ if (isDelegating()) {
+ socket.bind(bindpoint);
+ } else {
+ super.bind(bindpoint);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public void close() throws IOException {
+ if (isDelegating()) {
+ if (autoClose && !socket.isClosed()) {
+ socket.close();
+ }
+ } else {
+ if (!super.isClosed()) {
+ super.close();
+ }
+ }
+ }
+
+ @Override
+ public InetAddress getInetAddress() {
+ if (isDelegating()) {
+ return socket.getInetAddress();
+ }
+ return super.getInetAddress();
+ }
+
+ @Override
+ public InetAddress getLocalAddress() {
+ if (isDelegating()) {
+ return socket.getLocalAddress();
+ }
+ return super.getLocalAddress();
+ }
+
+ @Override
+ public int getLocalPort() {
+ if (isDelegating()) {
+ return socket.getLocalPort();
+ }
+ return super.getLocalPort();
+ }
+
+ @Override
+ public SocketAddress getRemoteSocketAddress() {
+ if (isDelegating()) {
+ return socket.getRemoteSocketAddress();
+ }
+ return super.getRemoteSocketAddress();
+ }
+
+ @Override
+ public SocketAddress getLocalSocketAddress() {
+ if (isDelegating()) {
+ return socket.getLocalSocketAddress();
+ }
+ return super.getLocalSocketAddress();
+ }
+
+ @Override
+ public final int getPort() {
+ if (isDelegating()) {
+ return socket.getPort();
+ }
+
+ if (peerPort != -1) {
+ // Return the port that has been explicitly set in the constructor.
+ return peerPort;
+ }
+ return super.getPort();
+ }
+
+ @Override
+ public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ checkArgument(listener != null, "Provided listener is null");
+ listeners.add(listener);
+ }
+
+ @Override
+ public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ checkArgument(listener != null, "Provided listener is null");
+ if (!listeners.remove(listener)) {
+ throw new IllegalArgumentException("Provided listener is not registered");
+ }
+ }
+
+ /* @Override */
+ @SuppressWarnings("MissingOverride") // For compilation with Java 6.
+ public abstract SSLSession getHandshakeSession();
+
+ /* @Override */
+ public FileDescriptor getFileDescriptor$() {
+ if (isDelegating()) {
+ return Platform.getFileDescriptor(socket);
+ }
+ return Platform.getFileDescriptorFromSSLSocket(this);
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public final void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
+ if (isDelegating()) {
+ socket.setSoTimeout(readTimeoutMilliseconds);
+ } else {
+ super.setSoTimeout(readTimeoutMilliseconds);
+ this.readTimeoutMilliseconds = readTimeoutMilliseconds;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public final int getSoTimeout() throws SocketException {
+ if (isDelegating()) {
+ return socket.getSoTimeout();
+ }
+ return readTimeoutMilliseconds;
+ }
+
+ @Override
+ public final void sendUrgentData(int data) throws IOException {
+ throw new SocketException("Method sendUrgentData() is not supported.");
+ }
+
+ @Override
+ public final void setOOBInline(boolean on) throws SocketException {
+ throw new SocketException("Method setOOBInline() is not supported.");
+ }
+
+ @Override
+ public boolean getOOBInline() throws SocketException {
+ return false;
+ }
+
+ @Override
+ public SocketChannel getChannel() {
+ // TODO(nmittler): Support channels?
+ return null;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (isDelegating()) {
+ return socket.getInputStream();
+ }
+ return super.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ if (isDelegating()) {
+ return socket.getOutputStream();
+ }
+ return super.getOutputStream();
+ }
+
+ @Override
+ public void setTcpNoDelay(boolean on) throws SocketException {
+ if (isDelegating()) {
+ socket.setTcpNoDelay(on);
+ } else {
+ super.setTcpNoDelay(on);
+ }
+ }
+
+ @Override
+ public boolean getTcpNoDelay() throws SocketException {
+ if (isDelegating()) {
+ return socket.getTcpNoDelay();
+ }
+ return super.getTcpNoDelay();
+ }
+
+ @Override
+ public void setSoLinger(boolean on, int linger) throws SocketException {
+ if (isDelegating()) {
+ socket.setSoLinger(on, linger);
+ } else {
+ super.setSoLinger(on, linger);
+ }
+ }
+
+ @Override
+ public int getSoLinger() throws SocketException {
+ if (isDelegating()) {
+ return socket.getSoLinger();
+ }
+ return super.getSoLinger();
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public void setSendBufferSize(int size) throws SocketException {
+ if (isDelegating()) {
+ socket.setSendBufferSize(size);
+ } else {
+ super.setSendBufferSize(size);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public int getSendBufferSize() throws SocketException {
+ if (isDelegating()) {
+ return socket.getSendBufferSize();
+ }
+ return super.getSendBufferSize();
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public void setReceiveBufferSize(int size) throws SocketException {
+ if (isDelegating()) {
+ socket.setReceiveBufferSize(size);
+ } else {
+ super.setReceiveBufferSize(size);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public int getReceiveBufferSize() throws SocketException {
+ if (isDelegating()) {
+ return socket.getReceiveBufferSize();
+ }
+ return super.getReceiveBufferSize();
+ }
+
+ @Override
+ public void setKeepAlive(boolean on) throws SocketException {
+ if (isDelegating()) {
+ socket.setKeepAlive(on);
+ } else {
+ super.setKeepAlive(on);
+ }
+ }
+
+ @Override
+ public boolean getKeepAlive() throws SocketException {
+ if (isDelegating()) {
+ return socket.getKeepAlive();
+ }
+ return super.getKeepAlive();
+ }
+
+ @Override
+ public void setTrafficClass(int tc) throws SocketException {
+ if (isDelegating()) {
+ socket.setTrafficClass(tc);
+ } else {
+ super.setTrafficClass(tc);
+ }
+ }
+
+ @Override
+ public int getTrafficClass() throws SocketException {
+ if (isDelegating()) {
+ return socket.getTrafficClass();
+ }
+ return super.getTrafficClass();
+ }
+
+ @Override
+ public void setReuseAddress(boolean on) throws SocketException {
+ if (isDelegating()) {
+ socket.setReuseAddress(on);
+ } else {
+ super.setReuseAddress(on);
+ }
+ }
+
+ @Override
+ public boolean getReuseAddress() throws SocketException {
+ if (isDelegating()) {
+ return socket.getReuseAddress();
+ }
+ return super.getReuseAddress();
+ }
+
+ @Override
+ public void shutdownInput() throws IOException {
+ if (isDelegating()) {
+ socket.shutdownInput();
+ } else {
+ super.shutdownInput();
+ }
+ }
+
+ @Override
+ public void shutdownOutput() throws IOException {
+ if (isDelegating()) {
+ socket.shutdownOutput();
+ } else {
+ super.shutdownOutput();
+ }
+ }
+
+ @Override
+ public boolean isConnected() {
+ if (isDelegating()) {
+ return socket.isConnected();
+ }
+ return super.isConnected();
+ }
+
+ @Override
+ public boolean isBound() {
+ if (isDelegating()) {
+ return socket.isBound();
+ }
+ return super.isBound();
+ }
+
+ @Override
+ public boolean isClosed() {
+ if (isDelegating()) {
+ return socket.isClosed();
+ }
+ return super.isClosed();
+ }
+
+ @Override
+ public boolean isInputShutdown() {
+ if (isDelegating()) {
+ return socket.isInputShutdown();
+ }
+ return super.isInputShutdown();
+ }
+
+ @Override
+ public boolean isOutputShutdown() {
+ if (isDelegating()) {
+ return socket.isOutputShutdown();
+ }
+ return super.isOutputShutdown();
+ }
+
+ @Override
+ public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
+ if (isDelegating()) {
+ socket.setPerformancePreferences(connectionTime, latency, bandwidth);
+ } else {
+ super.setPerformancePreferences(connectionTime, latency, bandwidth);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("SSL socket over ");
+ if (isDelegating()) {
+ builder.append(socket.toString());
+ } else {
+ builder.append(super.toString());
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Returns the hostname that was supplied during socket creation. No DNS resolution is
+ * attempted before returning the hostname.
+ */
+ String getHostname() {
+ return peerHostname;
+ }
+
+ /**
+ * This method enables Server Name Indication
+ *
+ * @param hostname the desired SNI hostname, or null to disable
+ */
+ void setHostname(String hostname) {
+ peerHostname = hostname;
+ }
+
+ /**
+ * For the purposes of an SSLSession, we want a way to represent the supplied hostname
+ * or the IP address in a textual representation. We do not want to perform reverse DNS
+ * lookups on this address.
+ */
+ String getHostnameOrIP() {
+ if (peerHostname != null) {
+ return peerHostname;
+ }
+
+ InetAddress peerAddress = getInetAddress();
+ if (peerAddress != null) {
+ return peerAddress.getHostAddress();
+ }
+
+ return null;
+ }
+
+ /**
+ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+ */
+ void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+ throw new SocketException("Method setSoWriteTimeout() is not supported.");
+ }
+
+ /**
+ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+ */
+ int getSoWriteTimeout() throws SocketException {
+ return 0;
+ }
+
+ /**
+ * Set the handshake timeout on this socket. This timeout is specified in
+ * milliseconds and will be used only during the handshake process.
+ */
+ void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+ throw new SocketException("Method setHandshakeTimeout() is not supported.");
+ }
+
+ /**
+ * This method enables session ticket support.
+ *
+ * @param useSessionTickets True to enable session tickets
+ */
+ abstract void setUseSessionTickets(boolean useSessionTickets);
+
+ /**
+ * Enables/disables TLS Channel ID for this server socket.
+ *
+ * <p>This method needs to be invoked before the handshake starts.
+ *
+ * @throws IllegalStateException if this is a client socket or if the handshake has already
+ * started.
+ */
+ abstract void setChannelIdEnabled(boolean enabled);
+
+ /**
+ * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
+ * handshake completes.
+ *
+ * @return channel ID or {@code null} if not available.
+ *
+ * @throws IllegalStateException if this is a client socket or if the handshake has not yet
+ * completed.
+ * @throws SSLException if channel ID is available but could not be obtained.
+ */
+ abstract byte[] getChannelId() throws SSLException;
+
+ /**
+ * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
+ *
+ * <p>This method needs to be invoked before the handshake starts.
+ *
+ * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
+ * TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
+ * P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
+ *
+ * @throws IllegalStateException if this is a server socket or if the handshake has already
+ * started.
+ */
+ abstract void setChannelIdPrivateKey(PrivateKey privateKey);
+
+ /**
+ * Returns null always for backward compatibility.
+ */
+ byte[] getNpnSelectedProtocol() {
+ return null;
+ }
+
+ /**
+ * This method does nothing and is kept for backward compatibility.
+ */
+ void setNpnProtocols(byte[] npnProtocols) {}
+
+ /**
+ * Returns the protocol agreed upon by client and server, or {@code null} if
+ * no protocol was agreed upon.
+ */
+ abstract byte[] getAlpnSelectedProtocol();
+
+ /**
+ * Sets the list of ALPN protocols. This method internally converts the protocols to their
+ * wire-format form.
+ *
+ * @param alpnProtocols the list of ALPN protocols
+ * @see #setAlpnProtocols(byte[])
+ */
+ abstract void setAlpnProtocols(String[] alpnProtocols);
+
+ /**
+ * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of
+ * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings).
+ * Requires that all strings be encoded with US-ASCII.
+ *
+ * @param alpnProtocols the encoded form of the ALPN protocol list
+ * @see #setAlpnProtocols(String[])
+ */
+ abstract void setAlpnProtocols(byte[] alpnProtocols);
+
+ /**
+ * Called by {@link #notifyHandshakeCompletedListeners()} to get the currently active session.
+ * Unlike {@link #getSession()}, this method must not block.
+ */
+ abstract SSLSession getActiveSession();
+
+ final PeerInfoProvider peerInfoProvider() {
+ return peerInfoProvider;
+ }
+
+ final void checkOpen() throws SocketException {
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+ }
+
+ final void notifyHandshakeCompletedListeners() {
+ if (listeners != null && !listeners.isEmpty()) {
+ // notify the listeners
+ HandshakeCompletedEvent event = new HandshakeCompletedEvent(this, getActiveSession());
+ for (HandshakeCompletedListener listener : listeners) {
+ try {
+ listener.handshakeCompleted(event);
+ } catch (RuntimeException e) {
+ // The RI runs the handlers in a separate thread,
+ // which we do not. But we try to preserve their
+ // behavior of logging a problem and not killing
+ // the handshaking thread just because a listener
+ // has a problem.
+ Thread thread = Thread.currentThread();
+ thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
+ }
+ }
+ }
+ }
+
+ private boolean isDelegating() {
+ // Checking for null to handle the case of calling virtual methods in the super class
+ // constructor.
+ return socket != null && socket != this;
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java b/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java
deleted file mode 100644
index e5de29a..0000000
--- a/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt;
-
-import java.security.Principal;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionBindingEvent;
-import javax.net.ssl.SSLSessionBindingListener;
-import javax.net.ssl.SSLSessionContext;
-import javax.security.cert.CertificateException;
-
-/**
- * Extends the base SSLSession with some methods used exclusively in Conscrypt.
- */
-abstract class AbstractOpenSSLSession implements SSLSession {
- private final Map<String, Object> values = new HashMap<String, Object>();
-
- private volatile javax.security.cert.X509Certificate[] peerCertificateChain;
-
- private AbstractSessionContext sessionContext;
-
- private boolean isValid = true;
-
- /**
- * Class constructor creates an SSL session context given the appropriate
- * session context.
- */
- AbstractOpenSSLSession(AbstractSessionContext sessionContext) {
- this.sessionContext = sessionContext;
- }
-
- protected abstract X509Certificate[] getX509PeerCertificates()
- throws SSLPeerUnverifiedException;
-
- protected abstract X509Certificate[] getX509LocalCertificates();
-
- /**
- * Throw SSLPeerUnverifiedException on null or empty peerCertificates array
- */
- private void checkPeerCertificatesPresent() throws SSLPeerUnverifiedException {
- X509Certificate[] peerCertificates = getX509PeerCertificates();
- if (peerCertificates == null || peerCertificates.length == 0) {
- throw new SSLPeerUnverifiedException("No peer certificates");
- }
- }
-
- /**
- * Return the identity of the peer in this SSL session
- * determined via certificate(s).
- * @return an array of X509 certificates (the peer's one first and then
- * eventually that of the certification authority) or null if no
- * certificate were used during the SSL connection.
- * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
- * was used (i.e. Kerberos certificates) or the peer could not
- * be verified.
- */
- @Override
- public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
- return getX509PeerCertificates();
- }
-
- /**
- * Returns the certificate(s) of the peer in this SSL session
- * used in the handshaking phase of the connection.
- * Please notice hat this method is superseded by
- * <code>getPeerCertificates()</code>.
- * @return an array of X509 certificates (the peer's one first and then
- * eventually that of the certification authority) or null if no
- * certificate were used during the SSL connection.
- * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
- * was used (i.e. Kerberos certificates) or the peer could not
- * be verified.
- */
- @Override
- public javax.security.cert.X509Certificate[] getPeerCertificateChain()
- throws SSLPeerUnverifiedException {
- checkPeerCertificatesPresent();
- javax.security.cert.X509Certificate[] result = peerCertificateChain;
- if (result == null) {
- // single-check idiom
- peerCertificateChain = result = createPeerCertificateChain();
- }
- return result;
- }
-
- /**
- * Provide a value to initialize the volatile peerCertificateChain
- * field based on the native SSL_SESSION
- */
- private javax.security.cert.X509Certificate[] createPeerCertificateChain()
- throws SSLPeerUnverifiedException {
- X509Certificate[] peerCertificates = getX509PeerCertificates();
- try {
- javax.security.cert.X509Certificate[] chain =
- new javax.security.cert.X509Certificate[peerCertificates.length];
-
- for (int i = 0; i < peerCertificates.length; i++) {
- byte[] encoded = peerCertificates[i].getEncoded();
- chain[i] = javax.security.cert.X509Certificate.getInstance(encoded);
- }
- return chain;
- } catch (CertificateEncodingException e) {
- SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
- exception.initCause(exception);
- throw exception;
- } catch (CertificateException e) {
- SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
- exception.initCause(exception);
- throw exception;
- }
- }
-
- /**
- * The identity of the principal that was used by the peer during the SSL
- * handshake phase is returned by this method.
- * @return a X500Principal of the last certificate for X509-based
- * cipher suites.
- * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
- * was used (i.e. Kerberos certificates) or the peer does not exist.
- *
- */
- @Override
- public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
- checkPeerCertificatesPresent();
- return getX509PeerCertificates()[0].getSubjectX500Principal();
- }
-
- /**
- * Returns the principal (subject) of this concrete SSL session used in the
- * handshaking phase of the connection.
- * @return a X509 certificate or null if no principal was defined
- */
- @Override
- public Principal getLocalPrincipal() {
- X509Certificate[] localCertificates = getX509LocalCertificates();
- if (localCertificates != null && localCertificates.length > 0) {
- return localCertificates[0].getSubjectX500Principal();
- } else {
- return null;
- }
- }
-
- /**
- * Returns the certificate(s) of the principal (subject) of this concrete SSL
- * session used in the handshaking phase of the connection. The OpenSSL
- * native method supports only RSA certificates.
- * @return an array of certificates (the local one first and then eventually
- * that of the certification authority) or null if no certificate
- * were used during the handshaking phase.
- */
- @Override
- public Certificate[] getLocalCertificates() {
- return getX509LocalCertificates();
- }
-
- /**
- * Returns the largest buffer size for the application's data bound to this
- * concrete SSL session.
- * @return the largest buffer size
- */
- @Override
- public int getApplicationBufferSize() {
- return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
- }
-
- /**
- * Returns the largest SSL/TLS packet size one can expect for this concrete
- * SSL session.
- * @return the largest packet size
- */
- @Override
- public int getPacketBufferSize() {
- return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
- }
-
- /**
- * Returns the object which is bound to the the input parameter name.
- * This name is a sort of link to the data of the SSL session's application
- * layer, if any exists.
- *
- * @param name the name of the binding to find.
- * @return the value bound to that name, or null if the binding does not
- * exist.
- * @throws IllegalArgumentException if the argument is null.
- */
- @Override
- public Object getValue(String name) {
- if (name == null) {
- throw new IllegalArgumentException("name == null");
- }
- return values.get(name);
- }
-
- /**
- * Returns an array with the names (sort of links) of all the data
- * objects of the application layer bound into the SSL session.
- *
- * @return a non-null (possibly empty) array of names of the data objects
- * bound to this SSL session.
- */
- @Override
- public String[] getValueNames() {
- return values.keySet().toArray(new String[values.size()]);
- }
-
- /**
- * A link (name) with the specified value object of the SSL session's
- * application layer data is created or replaced. If the new (or existing)
- * value object implements the <code>SSLSessionBindingListener</code>
- * interface, that object will be notified in due course.
- *
- * @param name the name of the link (no null are
- * accepted!)
- * @param value data object that shall be bound to
- * name.
- * @throws IllegalArgumentException if one or both argument(s) is null.
- */
- @Override
- public void putValue(String name, Object value) {
- if (name == null || value == null) {
- throw new IllegalArgumentException("name == null || value == null");
- }
- Object old = values.put(name, value);
- if (value instanceof SSLSessionBindingListener) {
- ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
- }
- if (old instanceof SSLSessionBindingListener) {
- ((SSLSessionBindingListener) old).valueUnbound(new SSLSessionBindingEvent(this, name));
- }
- }
-
- /**
- * Removes a link (name) with the specified value object of the SSL
- * session's application layer data.
- *
- * <p>If the value object implements the <code>SSLSessionBindingListener</code>
- * interface, the object will receive a <code>valueUnbound</code> notification.
- *
- * @param name the name of the link (no null are
- * accepted!)
- * @throws IllegalArgumentException if the argument is null.
- */
- @Override
- public void removeValue(String name) {
- if (name == null) {
- throw new IllegalArgumentException("name == null");
- }
- Object old = values.remove(name);
- if (old instanceof SSLSessionBindingListener) {
- SSLSessionBindingListener listener = (SSLSessionBindingListener) old;
- listener.valueUnbound(new SSLSessionBindingEvent(this, name));
- }
- }
-
- /**
- * Returns the context to which the actual SSL session is bound. A SSL
- * context consists of (1) a possible delegate, (2) a provider and (3) a
- * protocol.
- * @return the SSL context used for this session, or null if it is
- * unavailable.
- */
- @Override
- public SSLSessionContext getSessionContext() {
- return sessionContext;
- }
-
- /**
- * Returns a boolean flag signaling whether a SSL session is valid
- * and available for resuming or joining or not.
- *
- * @return true if this session may be resumed.
- */
- @Override
- public boolean isValid() {
- if (!isValid) {
- return false;
- }
- // The session has't yet been invalidated -- check whether it timed out.
-
- SSLSessionContext context = getSessionContext();
- if (context == null) {
- // Session not associated with a context -- no way to tell what its timeout should be.
- return true;
- }
-
- int timeoutSeconds = context.getSessionTimeout();
- if (timeoutSeconds == 0) {
- // Infinite timeout -- session still valid
- return true;
- }
-
- long creationTimestampMillis = getCreationTime();
- long ageSeconds = (System.currentTimeMillis() - creationTimestampMillis) / 1000;
- // NOTE: The age might be negative if something was/is wrong with the system clock. We time
- // out such sessions to be safe.
- if ((ageSeconds >= timeoutSeconds) || (ageSeconds < 0)) {
- // Session timed out -- no longer valid
- isValid = false;
- return false;
- }
-
- // Session still valid
- return true;
- }
-
- /**
- * It invalidates a SSL session forbidding any resumption.
- */
- @Override
- public void invalidate() {
- isValid = false;
- sessionContext = null;
- }
-
- /**
- * Returns the name requested by the SNI extension.
- */
- public abstract String getRequestedServerName();
-
- /**
- * Returns the OCSP stapled response.
- */
- public abstract List<byte[]> getStatusResponses();
-
- /**
- * Returns the TLS Stapled Certificate Transparency data.
- */
- public abstract byte[] getTlsSctData();
-
- /**
- * Sets the last accessed time for this session in milliseconds since Jan 1,
- * 1970 00:00:00 UTC.
- */
- public abstract void setLastAccessedTime(long accessTimeMillis);
-
- /**
- * Indicates that this session's ID may have changed and should be
- * re-cached.
- */
- abstract void resetId();
-}
diff --git a/common/src/main/java/org/conscrypt/AbstractSessionContext.java b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
index 61d595d..a7171f1 100644
--- a/common/src/main/java/org/conscrypt/AbstractSessionContext.java
+++ b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
@@ -16,19 +16,10 @@
package org.conscrypt;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.net.ssl.SSLSession;
@@ -50,28 +41,22 @@
final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
- /** Identifies OpenSSL sessions. */
- private static final int OPEN_SSL = 1;
-
- /** Identifies OpenSSL sessions with OCSP stapled data. */
- private static final int OPEN_SSL_WITH_OCSP = 2;
-
- /** Identifies OpenSSL sessions with TLS SCT data. */
- private static final int OPEN_SSL_WITH_TLS_SCT = 3;
-
@SuppressWarnings("serial")
- private final Map<ByteArray, SSLSession> sessions = new LinkedHashMap<ByteArray, SSLSession>() {
- @Override
- protected boolean removeEldestEntry(
- Map.Entry<ByteArray, SSLSession> eldest) {
- boolean remove = maximumSize > 0 && size() > maximumSize;
- if (remove) {
- remove(eldest.getKey());
- sessionRemoved(eldest.getValue());
- }
- return false;
- }
- };
+ private final Map<ByteArray, SslSessionWrapper> sessions =
+ new LinkedHashMap<ByteArray, SslSessionWrapper>() {
+ @Override
+ protected boolean removeEldestEntry(
+ Map.Entry<ByteArray, SslSessionWrapper> eldest) {
+ // NOTE: does not take into account any session that may have become
+ // invalid.
+ if (maximumSize > 0 && size() > maximumSize) {
+ // Let the subclass know.
+ onBeforeRemoveSession(eldest.getValue());
+ return true;
+ }
+ return false;
+ }
+ };
/**
* Constructs a new session context.
@@ -83,29 +68,27 @@
}
/**
- * Returns the collection of sessions ordered from oldest to newest
+ * This method is provided for API-compatibility only, not intended for use. No guarantees
+ * are made WRT performance.
*/
- private Iterator<SSLSession> sessionIterator() {
- synchronized (sessions) {
- SSLSession[] array = sessions.values().toArray(
- new SSLSession[sessions.size()]);
- return Arrays.asList(array).iterator();
- }
- }
-
@Override
public final Enumeration<byte[]> getIds() {
- final Iterator<SSLSession> i = sessionIterator();
+ // Make a copy of the IDs.
+ final Iterator<SslSessionWrapper> iter;
+ synchronized (sessions) {
+ iter = Arrays.asList(sessions.values().toArray(new SslSessionWrapper[sessions.size()]))
+ .iterator();
+ }
return new Enumeration<byte[]>() {
- private SSLSession next;
+ private SslSessionWrapper next;
@Override
public boolean hasMoreElements() {
if (next != null) {
return true;
}
- while (i.hasNext()) {
- SSLSession session = i.next();
+ while (iter.hasNext()) {
+ SslSessionWrapper session = iter.next();
if (session.isValid()) {
next = session;
return true;
@@ -127,6 +110,26 @@
};
}
+ /**
+ * This is provided for API-compatibility only, not intended for use. No guarantees are
+ * made WRT performance or the validity of the returned session.
+ */
+ @Override
+ public final SSLSession getSession(byte[] sessionId) {
+ if (sessionId == null) {
+ throw new NullPointerException("sessionId");
+ }
+ ByteArray key = new ByteArray(sessionId);
+ SslSessionWrapper session;
+ synchronized (sessions) {
+ session = sessions.get(key);
+ }
+ if (session != null && session.isValid()) {
+ return session.toSSLSession();
+ }
+ return null;
+ }
+
@Override
public final int getSessionCacheSize() {
return maximumSize;
@@ -137,55 +140,33 @@
return timeout;
}
- /**
- * Makes sure cache size is < maximumSize.
- */
- private void trimToSize() {
- synchronized (sessions) {
- int size = sessions.size();
- if (size > maximumSize) {
- int removals = size - maximumSize;
- Iterator<SSLSession> i = sessions.values().iterator();
- do {
- SSLSession session = i.next();
- i.remove();
- sessionRemoved(session);
- } while (--removals > 0);
- }
- }
- }
-
@Override
- public void setSessionTimeout(int seconds)
- throws IllegalArgumentException {
+ public final void setSessionTimeout(int seconds) throws IllegalArgumentException {
if (seconds < 0) {
throw new IllegalArgumentException("seconds < 0");
}
- timeout = seconds;
synchronized (sessions) {
- Iterator<SSLSession> i = sessions.values().iterator();
+ // Set the timeout on this context.
+ timeout = seconds;
+ NativeCrypto.SSL_CTX_set_timeout(sslCtxNativePointer, seconds);
+
+ Iterator<SslSessionWrapper> i = sessions.values().iterator();
while (i.hasNext()) {
- SSLSession session = i.next();
+ SslSessionWrapper session = i.next();
// SSLSession's know their context and consult the
// timeout as part of their validity condition.
if (!session.isValid()) {
+ // Let the subclass know.
+ onBeforeRemoveSession(session);
i.remove();
- sessionRemoved(session);
}
}
}
}
- /**
- * Called when a session is removed. Used by ClientSessionContext
- * to update its host-and-port based cache.
- */
- protected abstract void sessionRemoved(SSLSession session);
-
@Override
- public final void setSessionCacheSize(int size)
- throws IllegalArgumentException {
+ public final void setSessionCacheSize(int size) throws IllegalArgumentException {
if (size < 0) {
throw new IllegalArgumentException("size < 0");
}
@@ -199,202 +180,6 @@
}
}
- /**
- * Converts the given session to bytes.
- *
- * @return session data as bytes or null if the session can't be converted
- */
- byte[] toBytes(SSLSession session) {
- // TODO: Support SSLSessionImpl, too.
- if (!(session instanceof OpenSSLSessionImpl)) {
- return null;
- }
-
- OpenSSLSessionImpl sslSession = (OpenSSLSessionImpl) session;
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DataOutputStream daos = new DataOutputStream(baos);
-
- daos.writeInt(OPEN_SSL_WITH_TLS_SCT); // session type ID
-
- // Session data.
- byte[] data = sslSession.getEncoded();
- daos.writeInt(data.length);
- daos.write(data);
-
- // Certificates.
- Certificate[] certs = session.getPeerCertificates();
- daos.writeInt(certs.length);
-
- for (Certificate cert : certs) {
- data = cert.getEncoded();
- daos.writeInt(data.length);
- daos.write(data);
- }
-
- List<byte[]> ocspResponses = sslSession.getStatusResponses();
- daos.writeInt(ocspResponses.size());
- for (byte[] ocspResponse : ocspResponses) {
- daos.writeInt(ocspResponse.length);
- daos.write(ocspResponse);
- }
-
- byte[] tlsSctData = sslSession.getTlsSctData();
- if (tlsSctData != null) {
- daos.writeInt(tlsSctData.length);
- daos.write(tlsSctData);
- } else {
- daos.writeInt(0);
- }
-
- // TODO: local certificates?
-
- return baos.toByteArray();
- } catch (IOException e) {
- System.err.println("Failed to convert saved SSL Session: " + e.getMessage());
- return null;
- } catch (CertificateEncodingException e) {
- log(e);
- return null;
- }
- }
-
- private static void checkRemaining(ByteBuffer buf, int length) throws IOException {
- if (length < 0) {
- throw new IOException("Length is negative: " + length);
- }
- if (length > buf.remaining()) {
- throw new IOException(
- "Length of blob is longer than available: " + length + " > " + buf.remaining());
- }
- }
-
- /**
- * Creates a session from the given bytes.
- *
- * @return a session or null if the session can't be converted
- */
- OpenSSLSessionImpl toSession(byte[] data, String host, int port) {
- ByteBuffer buf = ByteBuffer.wrap(data);
- try {
- int type = buf.getInt();
- if (type != OPEN_SSL && type != OPEN_SSL_WITH_OCSP && type != OPEN_SSL_WITH_TLS_SCT) {
- throw new IOException("Unexpected type ID: " + type);
- }
-
- int length = buf.getInt();
- checkRemaining(buf, length);
-
- byte[] sessionData = new byte[length];
- buf.get(sessionData);
-
- int count = buf.getInt();
- checkRemaining(buf, count);
-
- X509Certificate[] certs = new X509Certificate[count];
- for (int i = 0; i < count; i++) {
- length = buf.getInt();
- checkRemaining(buf, length);
-
- byte[] certData = new byte[length];
- buf.get(certData);
- try {
- certs[i] = OpenSSLX509Certificate.fromX509Der(certData);
- } catch (Exception e) {
- throw new IOException("Can not read certificate " + i + "/" + count);
- }
- }
-
- byte[] ocspData = null;
- if (type >= OPEN_SSL_WITH_OCSP) {
- // We only support one OCSP response now, but in the future
- // we may support RFC 6961 which has multiple.
- int countOcspResponses = buf.getInt();
- checkRemaining(buf, countOcspResponses);
-
- if (countOcspResponses >= 1) {
- int ocspLength = buf.getInt();
- checkRemaining(buf, ocspLength);
-
- ocspData = new byte[ocspLength];
- buf.get(ocspData);
-
- // Skip the rest of the responses.
- for (int i = 1; i < countOcspResponses; i++) {
- ocspLength = buf.getInt();
- checkRemaining(buf, ocspLength);
- buf.position(buf.position() + ocspLength);
- }
- }
- }
-
- byte[] tlsSctData = null;
- if (type == OPEN_SSL_WITH_TLS_SCT) {
- int tlsSctDataLength = buf.getInt();
- checkRemaining(buf, tlsSctDataLength);
-
- if (tlsSctDataLength > 0) {
- tlsSctData = new byte[tlsSctDataLength];
- buf.get(tlsSctData);
- }
- }
-
- if (buf.remaining() != 0) {
- log(new AssertionError("Read entire session, but data still remains; rejecting"));
- return null;
- }
-
- return new OpenSSLSessionImpl(sessionData, host, port, certs, ocspData, tlsSctData,
- this);
- } catch (IOException e) {
- log(e);
- return null;
- } catch (BufferUnderflowException e) {
- log(e);
- return null;
- }
- }
-
- SSLSession wrapSSLSessionIfNeeded(SSLSession session) {
- if (session instanceof AbstractOpenSSLSession) {
- return Platform.wrapSSLSession((AbstractOpenSSLSession) session);
- } else {
- return session;
- }
- }
-
- @Override
- public SSLSession getSession(byte[] sessionId) {
- if (sessionId == null) {
- throw new NullPointerException("sessionId == null");
- }
- ByteArray key = new ByteArray(sessionId);
- SSLSession session;
- synchronized (sessions) {
- session = sessions.get(key);
- }
- if (session != null && session.isValid()) {
- return wrapSSLSessionIfNeeded(session);
- }
- return null;
- }
-
- void putSession(SSLSession session) {
- byte[] id = session.getId();
- if (id.length == 0) {
- return;
- }
- ByteArray key = new ByteArray(id);
- synchronized (sessions) {
- sessions.put(key, session);
- }
- }
-
- private static void log(Throwable t) {
- System.out.println("Error inflating SSL session: "
- + (t.getMessage() != null ? t.getMessage() : t.getClass().getName()));
- }
-
@Override
protected void finalize() throws Throwable {
try {
@@ -403,4 +188,85 @@
super.finalize();
}
}
+
+ /**
+ * Adds the given session to the cache.
+ */
+ final void cacheSession(SslSessionWrapper session) {
+ byte[] id = session.getId();
+ if (id == null || id.length == 0) {
+ return;
+ }
+
+ // Let the subclass know.
+ onBeforeAddSession(session);
+
+ ByteArray key = new ByteArray(id);
+ synchronized (sessions) {
+ sessions.put(key, session);
+ }
+ }
+
+ /**
+ * Called for server sessions only. Retrieves the session by its ID. Overridden by
+ * {@link ServerSessionContext} to
+ */
+ final SslSessionWrapper getSessionFromCache(byte[] sessionId) {
+ if (sessionId == null) {
+ return null;
+ }
+
+ // First, look in the in-memory cache.
+ SslSessionWrapper session;
+ synchronized (sessions) {
+ session = sessions.get(new ByteArray(sessionId));
+ }
+ if (session != null && session.isValid()) {
+ return session;
+ }
+
+ // Not found in-memory - look it up in the persistent cache.
+ return getSessionFromPersistentCache(sessionId);
+ }
+
+ /**
+ * Called when the given session is about to be added. Used by {@link ClientSessionContext} to
+ * update its host-and-port based cache.
+ *
+ * <p>Visible for extension only, not intended to be called directly.
+ */
+ abstract void onBeforeAddSession(SslSessionWrapper session);
+
+ /**
+ * Called when a session is about to be removed. Used by {@link ClientSessionContext}
+ * to update its host-and-port based cache.
+ *
+ * <p>Visible for extension only, not intended to be called directly.
+ */
+ abstract void onBeforeRemoveSession(SslSessionWrapper session);
+
+ /**
+ * Called for server sessions only. Retrieves the session by ID from the persistent cache.
+ *
+ * <p>Visible for extension only, not intended to be called directly.
+ */
+ abstract SslSessionWrapper getSessionFromPersistentCache(byte[] sessionId);
+
+ /**
+ * Makes sure cache size is < maximumSize.
+ */
+ private void trimToSize() {
+ synchronized (sessions) {
+ int size = sessions.size();
+ if (size > maximumSize) {
+ int removals = size - maximumSize;
+ Iterator<SslSessionWrapper> i = sessions.values().iterator();
+ while (removals-- > 0) {
+ SslSessionWrapper session = i.next();
+ onBeforeRemoveSession(session);
+ i.remove();
+ }
+ }
+ }
+ }
}
diff --git a/common/src/main/java/org/conscrypt/ActiveSession.java b/common/src/main/java/org/conscrypt/ActiveSession.java
new file mode 100644
index 0000000..c7f6431
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ActiveSession.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 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 static org.conscrypt.Preconditions.checkNotNull;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * A session that is dedicated a single connection and operates directly on the underlying
+ * {@code SSL}.
+ */
+final class ActiveSession implements SSLSession {
+ private final SslWrapper ssl;
+ private AbstractSessionContext sessionContext;
+ private byte[] id;
+ private long creationTime;
+ private String cipherSuite;
+ private String protocol;
+ private String peerHost;
+ private int peerPort = -1;
+ private long lastAccessedTime = 0;
+ private volatile javax.security.cert.X509Certificate[] peerCertificateChain;
+ private X509Certificate[] localCertificates;
+ private X509Certificate[] peerCertificates;
+ private byte[] peerCertificateOcspData;
+ private byte[] peerTlsSctData;
+
+ // lazy init for memory reasons
+ private Map<String, Object> values;
+
+ ActiveSession(SslWrapper ssl, AbstractSessionContext sessionContext) {
+ this.ssl = checkNotNull(ssl, "ssl");
+ this.sessionContext = checkNotNull(sessionContext, "sessionContext");
+ }
+
+ @Override
+ public byte[] getId() {
+ if (id == null) {
+ id = ssl.getSessionId();
+ }
+ return id != null ? id.clone() : EmptyArray.BYTE;
+ }
+
+ /**
+ * Indicates that this session's ID may have changed and should be re-cached.
+ */
+ void resetId() {
+ id = null;
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ return isValid() ? sessionContext : null;
+ }
+
+ @Override
+ public long getCreationTime() {
+ if (creationTime == 0) {
+ creationTime = ssl.getTime();
+ }
+ return creationTime;
+ }
+
+ /**
+ * Returns the last time this SSL session was accessed. Accessing
+ * here is to mean that a new connection with the same SSL context data was
+ * established.
+ *
+ * @return the session's last access time in milliseconds since the epoch
+ */
+ // TODO(nathanmittler): Does lastAccessedTime need to account for session reuse?
+ @Override
+ public long getLastAccessedTime() {
+ return lastAccessedTime == 0 ? getCreationTime() : lastAccessedTime;
+ }
+
+ void setLastAccessedTime(long accessTimeMillis) {
+ lastAccessedTime = accessTimeMillis;
+ }
+
+ /**
+ * Returns the OCSP stapled response. Returns a copy of the internal arrays.
+ *
+ * The method signature matches
+ * <a
+ * href="http://download.java.net/java/jdk9/docs/api/javax/net/ssl/ExtendedSSLSession.html#getStatusResponses--">Java
+ * 9</a>.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
+ * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
+ */
+ /* @Override */
+ @SuppressWarnings("MissingOverride") // For Pre-Java9 compatibility.
+ public List<byte[]> getStatusResponses() {
+ if (peerCertificateOcspData == null) {
+ return Collections.<byte[]>emptyList();
+ }
+
+ return Collections.singletonList(peerCertificateOcspData.clone());
+ }
+
+ /**
+ * Returns the signed certificate timestamp (SCT) received from the peer. Returns a
+ * copy of the internal array.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
+ */
+ byte[] getPeerSignedCertificateTimestamp() {
+ if (peerTlsSctData == null) {
+ return null;
+ }
+ return peerTlsSctData.clone();
+ }
+
+ String getRequestedServerName() {
+ return ssl.getRequestedServerName();
+ }
+
+ @Override
+ public void invalidate() {
+ ssl.setTimeout(0L);
+ }
+
+ @Override
+ public boolean isValid() {
+ long creationTimeMillis = ssl.getTime();
+ long timeoutMillis = ssl.getTimeout();
+ return (System.currentTimeMillis() - timeoutMillis) < creationTimeMillis;
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ if (value == null) {
+ throw new NullPointerException("value");
+ }
+ Map<String, Object> values = this.values;
+ if (values == null) {
+ // Use size of 2 to keep the memory overhead small
+ values = this.values = new HashMap<String, Object>(2);
+ }
+ Object old = values.put(name, value);
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
+ }
+ if (old instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) old).valueUnbound(new SSLSessionBindingEvent(this, name));
+ }
+ notifyUnbound(old, name);
+ }
+
+ @Override
+ public Object getValue(String name) {
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ if (values == null) {
+ return null;
+ }
+ return values.get(name);
+ }
+
+ @Override
+ public void removeValue(String name) {
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ Map<String, Object> values = this.values;
+ if (values == null) {
+ return;
+ }
+ Object old = values.remove(name);
+ notifyUnbound(old, name);
+ }
+
+ @Override
+ public String[] getValueNames() {
+ Map<String, Object> values = this.values;
+ if (values == null || values.isEmpty()) {
+ return EmptyArray.STRING;
+ }
+ return values.keySet().toArray(new String[values.size()]);
+ }
+
+ @Override
+ public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ checkPeerCertificatesPresent();
+ return peerCertificates.clone();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ return localCertificates == null ? null : localCertificates.clone();
+ }
+
+ /**
+ * Returns the certificate(s) of the peer in this SSL session
+ * used in the handshaking phase of the connection.
+ * Please notice hat this method is superseded by
+ * <code>getPeerCertificates()</code>.
+ * @return an array of X509 certificates (the peer's one first and then
+ * eventually that of the certification authority) or null if no
+ * certificate were used during the SSL connection.
+ * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
+ * was used (i.e. Kerberos certificates) or the peer could not
+ * be verified.
+ */
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ checkPeerCertificatesPresent();
+ // TODO(nathanmittler): Should we clone?
+ javax.security.cert.X509Certificate[] result = peerCertificateChain;
+ if (result == null) {
+ // single-check idiom
+ peerCertificateChain = result = SSLUtils.toCertificateChain(peerCertificates);
+ }
+ return result;
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ checkPeerCertificatesPresent();
+ return peerCertificates[0].getSubjectX500Principal();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ if (localCertificates != null && localCertificates.length > 0) {
+ return localCertificates[0].getSubjectX500Principal();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String getCipherSuite() {
+ if (cipherSuite == null) {
+ cipherSuite = ssl.getCipherSuite();
+ }
+ return cipherSuite;
+ }
+
+ @Override
+ public String getProtocol() {
+ String protocol = this.protocol;
+ if (protocol == null) {
+ protocol = ssl.getVersion();
+ this.protocol = protocol;
+ }
+ return protocol;
+ }
+
+ @Override
+ public String getPeerHost() {
+ return peerHost;
+ }
+
+ @Override
+ public int getPeerPort() {
+ return peerPort;
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
+ }
+
+ /**
+ * Configures the peer information once it has been received by the handshake.
+ */
+ void onPeerCertificatesReceived(
+ String peerHost, int peerPort, OpenSSLX509Certificate[] peerCertificates) {
+ configurePeer(peerHost, peerPort, peerCertificates);
+ }
+
+ /**
+ * Configures the peer and local state from a newly created BoringSSL session.
+ */
+ void onSessionEstablished(String peerHost, int peerPort) {
+ id = null;
+ this.localCertificates = ssl.getLocalCertificates();
+ configurePeer(peerHost, peerPort, ssl.getPeerCertificates());
+ }
+
+ private void configurePeer(
+ String peerHost, int peerPort, OpenSSLX509Certificate[] peerCertificates) {
+ this.peerHost = peerHost;
+ this.peerPort = peerPort;
+ this.peerCertificates = peerCertificates;
+ this.peerCertificateOcspData = ssl.getPeerCertificateOcspData();
+ this.peerTlsSctData = ssl.getPeerTlsSctData();
+ }
+
+ private X509Certificate[] getX509PeerCertificates() throws SSLPeerUnverifiedException {
+ if (peerCertificates == null || peerCertificates.length == 0) {
+ throw new SSLPeerUnverifiedException("No peer certificates");
+ }
+ return peerCertificates;
+ }
+
+ /**
+ * Throw SSLPeerUnverifiedException on null or empty peerCertificates array
+ */
+ private void checkPeerCertificatesPresent() throws SSLPeerUnverifiedException {
+ if (peerCertificates == null || peerCertificates.length == 0) {
+ throw new SSLPeerUnverifiedException("No peer certificates");
+ }
+ }
+
+ private void notifyUnbound(Object value, String name) {
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value)
+ .valueUnbound(new SSLSessionBindingEvent(this, name));
+ }
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/AllocatedBuffer.java b/common/src/main/java/org/conscrypt/AllocatedBuffer.java
new file mode 100644
index 0000000..a672678
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/AllocatedBuffer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 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.
+ */
+
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package org.conscrypt;
+
+import static org.conscrypt.Preconditions.checkNotNull;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A buffer that was allocated by a {@link BufferAllocator}.
+ */
+@ExperimentalApi
+public abstract class AllocatedBuffer {
+ /**
+ * Returns the {@link ByteBuffer} that backs this buffer.
+ */
+ public abstract ByteBuffer nioBuffer();
+
+ /**
+ * Increases the reference count by {@code 1}.
+ */
+ public abstract AllocatedBuffer retain();
+
+ /**
+ * Decreases the reference count by {@code 1} and deallocates this object if the reference count
+ * reaches at {@code 0}.
+ *
+ * @return {@code true} if and only if the reference count became {@code 0} and this object has
+ * been deallocated
+ */
+ public abstract AllocatedBuffer release();
+
+ /**
+ * Creates a new {@link AllocatedBuffer} that is backed by the given {@link ByteBuffer}.
+ */
+ public static AllocatedBuffer wrap(final ByteBuffer buffer) {
+ checkNotNull(buffer, "buffer");
+
+ return new AllocatedBuffer() {
+
+ @Override
+ public ByteBuffer nioBuffer() {
+ return buffer;
+ }
+
+ @Override
+ public AllocatedBuffer retain() {
+ // Do nothing.
+ return this;
+ }
+
+ @Override
+ public AllocatedBuffer release() {
+ // Do nothing.
+ return this;
+ }
+ };
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/BufferAllocator.java b/common/src/main/java/org/conscrypt/BufferAllocator.java
new file mode 100644
index 0000000..a45544b
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/BufferAllocator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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 java.nio.ByteBuffer;
+
+/**
+ * An object responsible for allocation of buffers. This is an extension point to enable buffer
+ * pooling within an application.
+ */
+@ExperimentalApi
+public abstract class BufferAllocator {
+ private static BufferAllocator UNPOOLED = new BufferAllocator() {
+ @Override
+ public AllocatedBuffer allocateDirectBuffer(int capacity) {
+ return AllocatedBuffer.wrap(ByteBuffer.allocateDirect(capacity));
+ }
+ };
+
+ /**
+ * Returns an unpooled buffer allocator, which will create a new buffer for each request.
+ */
+ public static BufferAllocator unpooled() {
+ return UNPOOLED;
+ }
+
+ /**
+ * Allocates a direct (i.e. non-heap) buffer with the given capacity.
+ */
+ public abstract AllocatedBuffer allocateDirectBuffer(int capacity);
+}
diff --git a/common/src/main/java/org/conscrypt/ClientSessionContext.java b/common/src/main/java/org/conscrypt/ClientSessionContext.java
index 48bbe8e..6265a7b 100644
--- a/common/src/main/java/org/conscrypt/ClientSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ClientSessionContext.java
@@ -17,7 +17,8 @@
package org.conscrypt;
import java.util.HashMap;
-import javax.net.ssl.SSLSession;
+import java.util.Map;
+import javax.net.ssl.SSLContext;
/**
* Caches client sessions. Indexes by host and port. Users are typically
@@ -26,39 +27,70 @@
* @hide
*/
@Internal
-public class ClientSessionContext extends AbstractSessionContext {
-
+public final class ClientSessionContext extends AbstractSessionContext {
/**
* Sessions indexed by host and port. Protect from concurrent
* access by holding a lock on sessionsByHostAndPort.
*/
- private final HashMap<HostAndPort, SSLSession> sessionsByHostAndPort = new HashMap<>();
+ @SuppressWarnings("serial")
+ private final Map<HostAndPort, SslSessionWrapper> sessionsByHostAndPort = new HashMap<>();
private SSLClientSessionCache persistentCache;
- public ClientSessionContext() {
+ ClientSessionContext() {
super(10);
}
- public int size() {
- return sessionsByHostAndPort.size();
- }
-
+ /**
+ * Applications should not use this method. Instead use {@link
+ * Conscrypt.Contexts#setClientSessionCache(SSLContext, SSLClientSessionCache)}.
+ */
public void setPersistentCache(SSLClientSessionCache persistentCache) {
this.persistentCache = persistentCache;
}
- @Override
- protected void sessionRemoved(SSLSession session) {
- String host = session.getPeerHost();
- int port = session.getPeerPort();
- if (host == null) {
- return;
+ /**
+ * Gets the suitable session reference from the session cache container.
+ */
+ SslSessionWrapper getCachedSession(String hostName, int port, SSLParametersImpl sslParameters) {
+ if (hostName == null) {
+ return null;
}
- HostAndPort hostAndPortKey = new HostAndPort(host, port);
- synchronized (sessionsByHostAndPort) {
- sessionsByHostAndPort.remove(hostAndPortKey);
+
+ SslSessionWrapper session = getSession(hostName, port);
+ if (session == null) {
+ return null;
}
+
+ String protocol = session.getProtocol();
+ boolean protocolFound = false;
+ for (String enabledProtocol : sslParameters.enabledProtocols) {
+ if (protocol.equals(enabledProtocol)) {
+ protocolFound = true;
+ break;
+ }
+ }
+ if (!protocolFound) {
+ return null;
+ }
+
+ String cipherSuite = session.getCipherSuite();
+ boolean cipherSuiteFound = false;
+ for (String enabledCipherSuite : sslParameters.enabledCipherSuites) {
+ if (cipherSuite.equals(enabledCipherSuite)) {
+ cipherSuiteFound = true;
+ break;
+ }
+ }
+ if (!cipherSuiteFound) {
+ return null;
+ }
+
+ return session;
+ }
+
+ int size() {
+ return sessionsByHostAndPort.size();
}
/**
@@ -68,30 +100,30 @@
* @param port of server
* @return cached session or null if none found
*/
- public SSLSession getSession(String host, int port) {
+ private SslSessionWrapper getSession(String host, int port) {
if (host == null) {
return null;
}
- SSLSession session;
- HostAndPort hostAndPortKey = new HostAndPort(host, port);
+
+ HostAndPort key = new HostAndPort(host, port);
+ SslSessionWrapper session;
synchronized (sessionsByHostAndPort) {
- session = sessionsByHostAndPort.get(hostAndPortKey);
+ session = sessionsByHostAndPort.get(key);
}
if (session != null && session.isValid()) {
- return wrapSSLSessionIfNeeded(session);
+ return session;
}
// Look in persistent cache.
if (persistentCache != null) {
byte[] data = persistentCache.getSessionData(host, port);
if (data != null) {
- session = toSession(data, host, port);
+ session = SslSessionWrapper.newInstance(this, data, host, port);
if (session != null && session.isValid()) {
- super.putSession(session);
synchronized (sessionsByHostAndPort) {
- sessionsByHostAndPort.put(hostAndPortKey, session);
+ sessionsByHostAndPort.put(key, session);
}
- return wrapSSLSessionIfNeeded(session);
+ return session;
}
}
}
@@ -100,30 +132,47 @@
}
@Override
- public void putSession(SSLSession session) {
- super.putSession(session);
-
+ void onBeforeAddSession(SslSessionWrapper session) {
String host = session.getPeerHost();
int port = session.getPeerPort();
if (host == null) {
return;
}
- HostAndPort hostAndPortKey = new HostAndPort(host, port);
+ HostAndPort key = new HostAndPort(host, port);
synchronized (sessionsByHostAndPort) {
- sessionsByHostAndPort.put(hostAndPortKey, session);
+ sessionsByHostAndPort.put(key, session);
}
- // TODO: This in a background thread.
+ // TODO: Do this in a background thread.
if (persistentCache != null) {
- byte[] data = toBytes(session);
+ byte[] data = session.toBytes();
if (data != null) {
- persistentCache.putSessionData(session, data);
+ persistentCache.putSessionData(session.toSSLSession(), data);
}
}
}
- static class HostAndPort {
+ @Override
+ void onBeforeRemoveSession(SslSessionWrapper session) {
+ String host = session.getPeerHost();
+ if (host == null) {
+ return;
+ }
+ int port = session.getPeerPort();
+ HostAndPort hostAndPortKey = new HostAndPort(host, port);
+ synchronized (sessionsByHostAndPort) {
+ sessionsByHostAndPort.remove(hostAndPortKey);
+ }
+ }
+
+ @Override
+ SslSessionWrapper getSessionFromPersistentCache(byte[] sessionId) {
+ // Not implemented for clients.
+ return null;
+ }
+
+ private static final class HostAndPort {
final String host;
final int port;
diff --git a/common/src/main/java/org/conscrypt/Conscrypt.java b/common/src/main/java/org/conscrypt/Conscrypt.java
index f2ede81..a0597c9 100644
--- a/common/src/main/java/org/conscrypt/Conscrypt.java
+++ b/common/src/main/java/org/conscrypt/Conscrypt.java
@@ -15,18 +15,18 @@
*/
package org.conscrypt;
-import java.io.FileDescriptor;
import java.io.UnsupportedEncodingException;
-import java.net.SocketException;
import java.nio.ByteBuffer;
import java.security.KeyManagementException;
import java.security.PrivateKey;
import java.security.Provider;
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
@@ -38,6 +38,32 @@
private Conscrypt() {}
/**
+ * Returns {@code true} if the Conscrypt native library has been successfully loaded.
+ */
+ public static boolean isAvailable() {
+ try {
+ checkAvailability();
+ return true;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks that the Conscrypt support is available for the system.
+ *
+ * @throws UnsatisfiedLinkError if unavailable
+ */
+ public static void checkAvailability() {
+ try {
+ NativeCrypto.checkAvailability();
+ } catch (Throwable e) {
+ throw (Error) new UnsatisfiedLinkError("failed to load the required native library")
+ .initCause(e);
+ }
+ }
+
+ /**
* Constructs a new {@link Provider} with the default name.
*/
public static Provider newProvider() {
@@ -82,6 +108,44 @@
}
/**
+ * Utility methods for configuring Conscrypt {@link SSLContext} instances.
+ */
+ public static final class Contexts {
+ private Contexts() {}
+
+ /**
+ * Indicates whether the given object is a Conscrypt client-side session context.
+ */
+ public static boolean isConscrypt(SSLContext context) {
+ return context.getProvider() instanceof OpenSSLProvider;
+ }
+
+ /**
+ * Sets the client-side persistent cache to be used by the context.
+ */
+ public static void setClientSessionCache(SSLContext context, SSLClientSessionCache cache) {
+ SSLSessionContext clientContext = context.getClientSessionContext();
+ if (!(clientContext instanceof ClientSessionContext)) {
+ throw new IllegalArgumentException(
+ "Not a conscrypt client context: " + clientContext.getClass().getName());
+ }
+ ((ClientSessionContext) clientContext).setPersistentCache(cache);
+ }
+
+ /**
+ * Sets the server-side persistent cache to be used by the context.
+ */
+ public static void setServerSessionCache(SSLContext context, SSLServerSessionCache cache) {
+ SSLSessionContext serverContext = context.getServerSessionContext();
+ if (!(serverContext instanceof ServerSessionContext)) {
+ throw new IllegalArgumentException(
+ "Not a conscrypt client context: " + serverContext.getClass().getName());
+ }
+ ((ServerSessionContext) serverContext).setPersistentCache(cache);
+ }
+ }
+
+ /**
* Utility methods for configuring Conscrypt socket factories.
*/
public static final class SocketFactories {
@@ -168,15 +232,43 @@
* Indicates whether the given socket is a Conscrypt socket.
*/
public static boolean isConscrypt(SSLSocket socket) {
- return socket instanceof OpenSSLSocketImpl;
+ return socket instanceof AbstractConscryptSocket;
}
- private static OpenSSLSocketImpl toConscrypt(SSLSocket socket) {
+ private static AbstractConscryptSocket toConscrypt(SSLSocket socket) {
if (!isConscrypt(socket)) {
throw new IllegalArgumentException(
"Not a conscrypt socket: " + socket.getClass().getName());
}
- return (OpenSSLSocketImpl) socket;
+ return (AbstractConscryptSocket) socket;
+ }
+
+ /**
+ * This method enables Server Name Indication (SNI) and overrides the hostname supplied
+ * during socket creation.
+ *
+ * @param socket the socket
+ * @param hostname the desired SNI hostname, or null to disable
+ */
+ public static void setHostname(SSLSocket socket, String hostname) {
+ toConscrypt(socket).setHostname(hostname);
+ }
+
+ /**
+ * Returns either the hostname supplied during socket creation or via
+ * {@link #setHostname(SSLSocket, String)}. No DNS resolution is attempted before
+ * returning the hostname.
+ */
+ public static String getHostname(SSLSocket socket) {
+ return toConscrypt(socket).getHostname();
+ }
+
+ /**
+ * This method attempts to create a textual representation of the peer host or IP. Does
+ * not perform a reverse DNS lookup. This is typically used during session creation.
+ */
+ public static String getHostnameOrIP(SSLSocket socket) {
+ return toConscrypt(socket).getHostnameOrIP();
}
/**
@@ -190,64 +282,6 @@
}
/**
- * This method enables Server Name Indication
- *
- * @param socket the socket
- * @param hostname the desired SNI hostname, or null to disable
- */
- public static void setHostname(SSLSocket socket, String hostname) {
- toConscrypt(socket).setHostname(hostname);
- }
-
- /**
- * Returns the hostname that was supplied during socket creation. No DNS resolution is
- * attempted before returning the hostname.
- */
- public static String getHostname(SSLSocket socket) {
- return toConscrypt(socket).getHostname();
- }
-
- /**
- * For the purposes of an SSLSession, we want a way to represent the supplied hostname
- * or the IP address in a textual representation. We do not want to perform reverse DNS
- * lookups on this address.
- */
- public static String getHostnameOrIP(SSLSocket socket) {
- return toConscrypt(socket).getHostnameOrIP();
- }
-
- /**
- * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
- */
- public static void setSoWriteTimeout(SSLSocket socket, int writeTimeoutMilliseconds)
- throws SocketException {
- toConscrypt(socket).setSoWriteTimeout(writeTimeoutMilliseconds);
- }
-
- /**
- * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
- */
- public static int getSoWriteTimeout(SSLSocket socket) throws SocketException {
- return toConscrypt(socket).getSoWriteTimeout();
- }
-
- /**
- * Set the handshake timeout on this socket. This timeout is specified in
- * milliseconds and will be used only during the handshake process.
- */
- public static void setHandshakeTimeout(SSLSocket socket, int handshakeTimeoutMilliseconds)
- throws SocketException {
- toConscrypt(socket).setHandshakeTimeout(handshakeTimeoutMilliseconds);
- }
-
- /**
- * Gets the underlying file descriptor for the given socket.
- */
- public static FileDescriptor getFileDescriptor(SSLSocket socket) {
- return toConscrypt(socket).getFileDescriptor$();
- }
-
- /**
* Enables/disables TLS Channel ID for the given server-side socket.
*
* <p>This method needs to be invoked before the handshake starts.
@@ -324,34 +358,44 @@
* Indicates whether the given engine is a Conscrypt engine.
*/
public static boolean isConscrypt(SSLEngine engine) {
- return engine instanceof OpenSSLEngineImpl;
+ return engine instanceof ConscryptEngine;
}
- private static OpenSSLEngineImpl toConscrypt(SSLEngine engine) {
+ private static ConscryptEngine toConscrypt(SSLEngine engine) {
if (!isConscrypt(engine)) {
throw new IllegalArgumentException(
"Not a conscrypt engine: " + engine.getClass().getName());
}
- return (OpenSSLEngineImpl) engine;
+ return (ConscryptEngine) engine;
}
/**
- * This method enables Server Name Indication (SNI) and sets the host name used for
- * SNI.
+ * Provides the given engine with the provided bufferAllocator.
+ * @param engine
+ * @param bufferAllocator
+ */
+ public static void setBufferAllocator(SSLEngine engine, BufferAllocator bufferAllocator) {
+ toConscrypt(engine).setBufferAllocator(bufferAllocator);
+ }
+
+ /**
+ * This method enables Server Name Indication (SNI) and overrides the hostname supplied
+ * during engine creation.
*
* @param engine the engine
* @param hostname the desired SNI hostname, or {@code null} to disable
*/
public static void setHostname(SSLEngine engine, String hostname) {
- toConscrypt(engine).setSniHostname(hostname);
+ toConscrypt(engine).setHostname(hostname);
}
/**
- * Returns the SNI hostname that was set for the {@code engine}. If no SNI hostname
- * was set, it will return the hostname supplied during creation of the {@code engine}.
+ * Returns either the hostname supplied during socket creation or via
+ * {@link #setHostname(SSLEngine, String)}. No DNS resolution is attempted before
+ * returning the hostname.
*/
public static String getHostname(SSLEngine engine) {
- return toConscrypt(engine).getSniHostname();
+ return toConscrypt(engine).getHostname();
}
/**
diff --git a/common/src/main/java/org/conscrypt/OpenSSLEngineImpl.java b/common/src/main/java/org/conscrypt/ConscryptEngine.java
similarity index 64%
rename from common/src/main/java/org/conscrypt/OpenSSLEngineImpl.java
rename to common/src/main/java/org/conscrypt/ConscryptEngine.java
index d12d4d8..6e1a929 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLEngineImpl.java
+++ b/common/src/main/java/org/conscrypt/ConscryptEngine.java
@@ -32,6 +32,8 @@
package org.conscrypt;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_WRAP;
@@ -45,16 +47,27 @@
import static org.conscrypt.NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
import static org.conscrypt.NativeConstants.SSL_CB_HANDSHAKE_DONE;
import static org.conscrypt.NativeConstants.SSL_CB_HANDSHAKE_START;
-import static org.conscrypt.NativeConstants.SSL_ERROR_NONE;
import static org.conscrypt.NativeConstants.SSL_ERROR_WANT_READ;
import static org.conscrypt.NativeConstants.SSL_ERROR_WANT_WRITE;
import static org.conscrypt.NativeConstants.SSL_ERROR_ZERO_RETURN;
-import static org.conscrypt.NativeConstants.SSL_RECEIVED_SHUTDOWN;
-import static org.conscrypt.NativeConstants.SSL_SENT_SHUTDOWN;
+import static org.conscrypt.Preconditions.checkArgument;
+import static org.conscrypt.Preconditions.checkNotNull;
+import static org.conscrypt.Preconditions.checkPositionIndexes;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED_INBOUND;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED_OUTBOUND;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_MODE_SET;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_READY;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
import static org.conscrypt.SSLUtils.calculateOutNetBufSize;
import static org.conscrypt.SSLUtils.toSSLHandshakeException;
+import java.io.EOFException;
import java.io.IOException;
+import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.security.InvalidKeyException;
@@ -76,15 +89,15 @@
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
+import org.conscrypt.NativeRef.SSL_SESSION;
+import org.conscrypt.SslWrapper.BioWrapper;
/**
* Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces.
- *
- * @hide
*/
-final class OpenSSLEngineImpl extends SSLEngine implements NativeCrypto.SSLHandshakeCallbacks,
- SSLParametersImpl.AliasChooser,
- SSLParametersImpl.PSKCallbacks {
+final class ConscryptEngine extends SSLEngine implements NativeCrypto.SSLHandshakeCallbacks,
+ SSLParametersImpl.AliasChooser,
+ SSLParametersImpl.PSKCallbacks {
private static final SSLEngineResult NEED_UNWRAP_OK =
new SSLEngineResult(OK, NEED_UNWRAP, 0, 0);
private static final SSLEngineResult NEED_UNWRAP_CLOSED =
@@ -95,59 +108,29 @@
private static final SSLEngineResult CLOSED_NOT_HANDSHAKING =
new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
private static final ByteBuffer EMPTY = ByteBuffer.allocateDirect(0);
- private static final long EMPTY_ADDR = NativeCrypto.getDirectBufferAddress(EMPTY);
-
- /**
- * Hostname used with the TLS extension SNI hostname. {@link #getPeerHost()} is used if this is
- * not set.
- */
- private String sniHostname;
private final SSLParametersImpl sslParameters;
+ private BufferAllocator bufferAllocator;
/**
- * Protects {@link #engineState} and {@link #handshakeFinished}.
+ * 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;
+
+ /**
+ * Hostname used with the TLS extension SNI hostname.
+ */
+ private String peerHostname;
+
+ /**
+ * Protects {@link #state} and {@link #handshakeFinished}.
*/
private final Object stateLock = new Object();
- private enum EngineState {
- /**
- * The {@link OpenSSLSocketImpl} object is constructed, but {@link #beginHandshake()} has
- * not yet been called.
- */
- NEW,
- /**
- * {@link #setUseClientMode(boolean)} has been called at least once.
- */
- MODE_SET,
- /**
- * Handshake task has been started.
- */
- HANDSHAKE_STARTED,
- /**
- * Handshake has been completed, but {@link #beginHandshake()} hasn't returned yet.
- */
- HANDSHAKE_COMPLETED,
- /**
- * {@link #beginHandshake()} has completed but the task hasn't been called. This is expected
- * behaviour in cut-through mode, where SSL_do_handshake returns before the handshake is
- * complete. We can now start writing data to the socket.
- */
- READY_HANDSHAKE_CUT_THROUGH,
- /**
- * {@link #beginHandshake()} has completed and socket is ready to go.
- */
- READY,
- CLOSED_INBOUND,
- CLOSED_OUTBOUND,
- /**
- * Inbound and outbound has been called.
- */
- CLOSED,
- }
-
// @GuardedBy("stateLock");
- private EngineState engineState = EngineState.NEW;
+ private int state = STATE_NEW;
private boolean handshakeFinished;
/**
@@ -155,24 +138,18 @@
* close.
*/
// @GuardedBy("stateLock");
- private long sslNativePointer;
+ private final SslWrapper ssl;
/**
- * Protected by synchronizing on stateLock. Starts as 0, set by startHandshake, reset to 0 on
- * close.
+ * The BIO used for reading/writing encrypted bytes.
*/
// @GuardedBy("stateLock");
- private long networkBio;
+ private final BioWrapper networkBio;
/**
* Set during startHandshake.
*/
- private AbstractOpenSSLSession sslSession;
-
- /**
- * Used during handshake callbacks.
- */
- private AbstractOpenSSLSession handshakeSession;
+ private final ActiveSession sslSession;
/**
* Private key for the TLS Channel ID extension. This field is client-side only. Set during
@@ -186,14 +163,50 @@
private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1];
private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
+ private final PeerInfoProvider peerInfoProvider;
- OpenSSLEngineImpl(SSLParametersImpl sslParameters) {
+ private SSLException handshakeException;
+
+ ConscryptEngine(SSLParametersImpl sslParameters) {
this.sslParameters = sslParameters;
+ peerInfoProvider = PeerInfoProvider.nullProvider();
+ this.ssl = newSsl(sslParameters, this);
+ this.networkBio = ssl.newBio();
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
}
- OpenSSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) {
- super(host, port);
+ ConscryptEngine(String host, int port, SSLParametersImpl sslParameters) {
this.sslParameters = sslParameters;
+ this.peerInfoProvider = PeerInfoProvider.forHostAndPort(host, port);
+ this.ssl = newSsl(sslParameters, this);
+ this.networkBio = ssl.newBio();
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ ConscryptEngine(SSLParametersImpl sslParameters, PeerInfoProvider peerInfoProvider) {
+ this.sslParameters = sslParameters;
+ this.peerInfoProvider = checkNotNull(peerInfoProvider, "peerInfoProvider");
+ this.ssl = newSsl(sslParameters, this);
+ this.networkBio = ssl.newBio();
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ private static SslWrapper newSsl(SSLParametersImpl sslParameters, ConscryptEngine engine) {
+ try {
+ return SslWrapper.newInstance(sslParameters, engine, engine, engine);
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void setBufferAllocator(BufferAllocator bufferAllocator) {
+ synchronized (stateLock) {
+ if (isHandshakeStarted()) {
+ throw new IllegalStateException(
+ "Could not set buffer allocator after the initial handshake has begun.");
+ }
+ this.bufferAllocator = bufferAllocator;
+ }
}
/**
@@ -244,7 +257,7 @@
throw new IllegalStateException(
"Channel ID is only available after handshake completes");
}
- return NativeCrypto.SSL_get_tls_channel_id(sslNativePointer);
+ return ssl.getTlsChannelId();
}
}
@@ -310,15 +323,43 @@
}
private boolean isHandshakeStarted() {
- switch (engineState) {
- case NEW:
- case MODE_SET:
+ switch (state) {
+ case STATE_NEW:
+ case STATE_MODE_SET:
return false;
default:
return true;
}
}
+ /**
+ * This method enables Server Name Indication (SNI) and overrides the {@link PeerInfoProvider}
+ * supplied during engine creation.
+ */
+ void setHostname(String hostname) {
+ sslParameters.setUseSni(hostname != null);
+ this.peerHostname = hostname;
+ }
+
+ /**
+ * Returns the hostname from {@link #setHostname(String)} or supplied by the
+ * {@link PeerInfoProvider} upon creation. No DNS resolution is attempted before
+ * returning the hostname.
+ */
+ String getHostname() {
+ return peerHostname != null ? peerHostname : peerInfoProvider.getHostname();
+ }
+
+ @Override
+ public String getPeerHost() {
+ return peerHostname != null ? peerHostname : peerInfoProvider.getHostnameOrIP();
+ }
+
+ @Override
+ public int getPeerPort() {
+ return peerInfoProvider.getPort();
+ }
+
@Override
public void beginHandshake() throws SSLException {
synchronized (stateLock) {
@@ -327,38 +368,38 @@
}
private void beginHandshakeInternal() throws SSLException {
- switch (engineState) {
- case MODE_SET:
+ switch (state) {
+ case STATE_MODE_SET:
// This is the only allowed state.
break;
- case HANDSHAKE_STARTED:
+ case STATE_HANDSHAKE_STARTED:
throw new IllegalStateException("Handshake has already been started");
- case CLOSED_INBOUND:
- case CLOSED_OUTBOUND:
- case CLOSED:
+ case STATE_CLOSED_INBOUND:
+ case STATE_CLOSED_OUTBOUND:
+ case STATE_CLOSED:
throw new IllegalStateException("Engine has already been closed");
default:
throw new IllegalStateException("Client/server mode must be set before handshake");
}
- engineState = EngineState.HANDSHAKE_STARTED;
+ state = STATE_HANDSHAKE_STARTED;
boolean releaseResources = true;
try {
- final AbstractSessionContext sessionContext = sslParameters.getSessionContext();
- sslNativePointer = NativeCrypto.SSL_new(sessionContext.sslCtxNativePointer);
- networkBio = NativeCrypto.SSL_BIO_new(sslNativePointer);
- sslSession = sslParameters.getSessionToReuse(
- sslNativePointer, getSniHostname(), getPeerPort());
- sslParameters.setSSLParameters(sslNativePointer, this, this, getSniHostname());
- sslParameters.setCertificateValidation(sslNativePointer);
- sslParameters.setTlsChannelId(sslNativePointer, channelIdPrivateKey);
+ // Prepare the SSL object for the handshake.
+ ssl.initialize(getHostname(), channelIdPrivateKey);
+
+ // For clients, offer to resume a previously cached session to avoid the
+ // full TLS handshake.
if (getUseClientMode()) {
- NativeCrypto.SSL_set_connect_state(sslNativePointer);
- } else {
- NativeCrypto.SSL_set_accept_state(sslNativePointer);
+ SslSessionWrapper cachedSession = clientSessionContext().getCachedSession(
+ getHostname(), getPeerPort(), sslParameters);
+ if (cachedSession != null) {
+ cachedSession.offerToResume(ssl);
+ }
}
- maxSealOverhead = NativeCrypto.SSL_max_seal_overhead(sslNativePointer);
+
+ maxSealOverhead = ssl.getMaxSealOverhead();
handshake();
releaseResources = false;
} catch (IOException e) {
@@ -366,13 +407,13 @@
String message = e.getMessage();
// Must match error reason string of SSL_R_UNEXPECTED_CCS (in ssl/ssl_err.c)
if (message.contains("unexpected CCS")) {
- String logMessage = String.format("ssl_unexpected_ccs: host=%s", getSniHostname());
+ String logMessage = String.format("ssl_unexpected_ccs: host=%s", getPeerHost());
Platform.logEvent(logMessage);
}
throw SSLUtils.toSSLHandshakeException(e);
} finally {
if (releaseResources) {
- engineState = EngineState.CLOSED;
+ state = STATE_CLOSED;
shutdownAndFreeSslNative();
}
}
@@ -381,13 +422,13 @@
@Override
public void closeInbound() throws SSLException {
synchronized (stateLock) {
- if (engineState == EngineState.CLOSED) {
+ if (state == STATE_CLOSED) {
return;
}
- if (engineState == EngineState.CLOSED_OUTBOUND) {
- engineState = EngineState.CLOSED;
+ if (state == STATE_CLOSED_OUTBOUND) {
+ state = STATE_CLOSED;
} else {
- engineState = EngineState.CLOSED_INBOUND;
+ state = STATE_CLOSED_INBOUND;
}
}
// TODO anything else to notify OpenSSL layer?
@@ -396,16 +437,16 @@
@Override
public void closeOutbound() {
synchronized (stateLock) {
- if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) {
+ if (state == STATE_CLOSED || state == STATE_CLOSED_OUTBOUND) {
return;
}
if (isHandshakeStarted()) {
shutdownAndFreeSslNative();
}
- if (engineState == EngineState.CLOSED_INBOUND) {
- engineState = EngineState.CLOSED;
+ if (state == STATE_CLOSED_INBOUND) {
+ state = STATE_CLOSED;
} else {
- engineState = EngineState.CLOSED_OUTBOUND;
+ state = STATE_CLOSED_OUTBOUND;
}
}
shutdown();
@@ -439,14 +480,6 @@
return params;
}
- public void setSniHostname(String sniHostname) {
- this.sniHostname = sniHostname;
- }
-
- public String getSniHostname() {
- return sniHostname != null ? sniHostname : getPeerHost();
- }
-
@Override
public void setSSLParameters(SSLParameters p) {
super.setSSLParameters(p);
@@ -464,31 +497,31 @@
if (handshakeFinished) {
return HandshakeStatus.NOT_HANDSHAKING;
}
- switch (engineState) {
- case HANDSHAKE_STARTED:
+ switch (state) {
+ case STATE_HANDSHAKE_STARTED:
return pendingStatus(pendingOutboundEncryptedBytes());
- case HANDSHAKE_COMPLETED:
+ case STATE_HANDSHAKE_COMPLETED:
return HandshakeStatus.NEED_WRAP;
- case NEW:
- case MODE_SET:
- case CLOSED:
- case CLOSED_INBOUND:
- case CLOSED_OUTBOUND:
- case READY:
- case READY_HANDSHAKE_CUT_THROUGH:
+ case STATE_NEW:
+ case STATE_MODE_SET:
+ case STATE_CLOSED:
+ case STATE_CLOSED_INBOUND:
+ case STATE_CLOSED_OUTBOUND:
+ case STATE_READY:
+ case STATE_READY_HANDSHAKE_CUT_THROUGH:
return HandshakeStatus.NOT_HANDSHAKING;
default:
break;
}
- throw new IllegalStateException("Unexpected engine state: " + engineState);
+ throw new IllegalStateException("Unexpected engine state: " + state);
}
private int pendingOutboundEncryptedBytes() {
- return NativeCrypto.SSL_pending_written_bytes_in_BIO(networkBio);
+ return networkBio.getPendingWrittenBytes();
}
private int pendingInboundCleartextBytes() {
- return NativeCrypto.SSL_pending_readable_bytes(sslNativePointer);
+ return ssl.getPendingReadableBytes();
}
private static SSLEngineResult.HandshakeStatus pendingStatus(int pendingOutboundBytes) {
@@ -501,13 +534,30 @@
return sslParameters.getNeedClientAuth();
}
+ /* @Override */
+ @SuppressWarnings("MissingOverride") // For compilation with Java 6.
+ public SSLSession getHandshakeSession() {
+ return handshakeSession();
+ }
+
+ /**
+ * Work-around to allow this method to be called on older versions of Android.
+ */
+ SSLSession handshakeSession() {
+ synchronized (stateLock) {
+ return state == STATE_HANDSHAKE_STARTED ? sslSession : null;
+ }
+ }
+
@Override
public SSLSession getSession() {
- if (sslSession == null) {
- return handshakeSession != null ? Platform.wrapSSLSession(handshakeSession)
- : SSLNullSession.getNullSession();
+ synchronized (stateLock) {
+ if (state < STATE_HANDSHAKE_COMPLETED) {
+ // Return an invalid session with invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+ return SSLNullSession.getNullSession();
+ }
+ return Platform.wrapSSLSession(sslSession);
}
- return Platform.wrapSSLSession(sslSession);
}
@Override
@@ -532,24 +582,22 @@
@Override
public boolean isInboundDone() {
- if (sslNativePointer == 0) {
- synchronized (stateLock) {
- return engineState == EngineState.CLOSED
- || engineState == EngineState.CLOSED_INBOUND;
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED || state == STATE_CLOSED_INBOUND) {
+ return true;
}
}
- return (NativeCrypto.SSL_get_shutdown(sslNativePointer) & SSL_RECEIVED_SHUTDOWN) != 0;
+ return ssl.wasShutdownReceived();
}
@Override
public boolean isOutboundDone() {
- if (sslNativePointer == 0) {
- synchronized (stateLock) {
- return engineState == EngineState.CLOSED
- || engineState == EngineState.CLOSED_OUTBOUND;
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED || state == STATE_CLOSED_OUTBOUND) {
+ return true;
}
}
- return (NativeCrypto.SSL_get_shutdown(sslNativePointer) & SSL_SENT_SHUTDOWN) != 0;
+ return ssl.wasShutdownSent();
}
@Override
@@ -577,9 +625,9 @@
synchronized (stateLock) {
if (isHandshakeStarted()) {
throw new IllegalArgumentException(
- "Can not change mode after handshake: engineState == " + engineState);
+ "Can not change mode after handshake: state == " + state);
}
- engineState = EngineState.MODE_SET;
+ state = STATE_MODE_SET;
}
sslParameters.setUseClientMode(mode);
}
@@ -625,19 +673,18 @@
}
SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException {
- checkNotNull(srcs, "srcs");
- checkNotNull(dsts, "dsts");
+ checkArgument(srcs != null, "srcs is null");
+ checkArgument(dsts != null, "dsts is null");
return unwrap(srcs, 0, srcs.length, dsts, 0, dsts.length);
}
SSLEngineResult unwrap(final ByteBuffer[] srcs, int srcsOffset, final int srcsLength,
final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength)
throws SSLException {
- checkNotNull(srcs, "srcs");
- checkNotNull(dsts, "dsts");
-
- checkIndex(srcs.length, srcsOffset, srcsLength, "srcs");
- checkIndex(dsts.length, dstsOffset, dstsLength, "dsts");
+ checkArgument(srcs != null, "srcs is null");
+ checkArgument(dsts != null, "dsts is null");
+ checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, srcs.length);
+ checkPositionIndexes(dstsOffset, dstsOffset + dstsLength, dsts.length);
// Determine the output capacity.
final int dstLength = calcDstsLength(dsts, dstsOffset, dstsLength);
@@ -647,16 +694,16 @@
final long srcLength = calcSrcsLength(srcs, srcsOffset, srcsEndOffset);
synchronized (stateLock) {
- switch (engineState) {
- case MODE_SET:
+ switch (state) {
+ case STATE_MODE_SET:
// Begin the handshake implicitly.
beginHandshakeInternal();
break;
- case CLOSED_INBOUND:
- case CLOSED:
+ case STATE_CLOSED_INBOUND:
+ case STATE_CLOSED:
// If the inbound direction is closed. we can't send anymore.
return new SSLEngineResult(Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
- case NEW:
+ case STATE_NEW:
throw new IllegalStateException(
"Client/server mode must be set before calling unwrap");
default:
@@ -669,7 +716,7 @@
if (handshakeStatus == NEED_WRAP) {
return NEED_WRAP_OK;
}
- if (engineState == EngineState.CLOSED) {
+ if (state == STATE_CLOSED) {
return NEED_WRAP_CLOSED;
}
// NEED_UNWRAP - just fall through to perform the unwrap.
@@ -715,7 +762,7 @@
continue;
}
// Write the source encrypted data to the networkBio.
- int written = writeEncryptedData(src, Math.min(lenRemaining, remaining));
+ int written = writeEncryptedData(src, min(lenRemaining, remaining));
if (written > 0) {
bytesConsumed += written;
lenRemaining -= written;
@@ -746,57 +793,65 @@
// Now read any available plaintext data.
int bytesProduced = 0;
- if (dstLength > 0) {
- // Write decrypted data to dsts buffers
- for (int idx = dstsOffset; idx < endOffset; ++idx) {
- ByteBuffer dst = dsts[idx];
- if (!dst.hasRemaining()) {
- continue;
- }
+ try {
+ if (dstLength > 0) {
+ // Write decrypted data to dsts buffers
+ for (int idx = dstsOffset; idx < endOffset; ++idx) {
+ ByteBuffer dst = dsts[idx];
+ if (!dst.hasRemaining()) {
+ continue;
+ }
- int bytesRead = readPlaintextData(dst);
- if (bytesRead > 0) {
- bytesProduced += bytesRead;
- if (dst.hasRemaining()) {
- // We haven't filled this buffer fully, break out of the loop
- // and determine the correct response status below.
- break;
- }
- } else {
- // Return an appropriate result based on the error code.
- int sslError = NativeCrypto.SSL_get_error(sslNativePointer, bytesRead);
- switch (sslError) {
- case SSL_ERROR_ZERO_RETURN:
- // This means the connection was shutdown correctly, close inbound
- // and outbound
- closeAll();
- return newResult(bytesConsumed, bytesProduced, handshakeStatus);
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- return newResult(bytesConsumed, bytesProduced, handshakeStatus);
- default:
- return sslReadErrorResult(NativeCrypto.SSL_get_last_error_number(),
- bytesConsumed, bytesProduced);
+ int bytesRead = readPlaintextData(dst);
+ if (bytesRead > 0) {
+ bytesProduced += bytesRead;
+ if (dst.hasRemaining()) {
+ // We haven't filled this buffer fully, break out of the loop
+ // and determine the correct response status below.
+ break;
+ }
+ } else {
+ switch (bytesRead) {
+ case -SSL_ERROR_WANT_READ:
+ case -SSL_ERROR_WANT_WRITE: {
+ return newResult(bytesConsumed, bytesProduced, handshakeStatus);
+ }
+ default: {
+ // Should never get here.
+ throw shutdownWithError("SSL_read");
+ }
+ }
}
}
+ } else {
+ // If the capacity of all destination buffers is 0 we need to trigger a SSL_read
+ // anyway to ensure everything is flushed in the BIO pair and so we can detect
+ // it in the pendingInboundCleartextBytes() call.
+ readPlaintextData(EMPTY);
}
- } else {
- // If the capacity of all destination buffers is 0 we need to trigger a SSL_read
- // anyway to ensure everything is flushed in the BIO pair and so we can detect it
- // in the pendingInboundCleartextBytes() call.
- try {
- if (NativeCrypto.ENGINE_SSL_read_direct(sslNativePointer, EMPTY_ADDR, 0, this)
- <= 0) {
- // We do not check SSL_get_error as we are not interested in any error that
- // is not fatal.
- int err = NativeCrypto.SSL_get_last_error_number();
- if (err != SSL_ERROR_NONE) {
- return sslReadErrorResult(err, bytesConsumed, bytesProduced);
- }
+ } catch (SSLException e) {
+ if (pendingOutboundEncryptedBytes() > 0) {
+ // We need to flush any pending bytes to the remote endpoint in case
+ // there is an alert that needs to be propagated.
+ if (!handshakeFinished && handshakeException == null) {
+ // Save the handshake exception. We will re-throw during the next
+ // handshake.
+ handshakeException = e;
}
- } catch (IOException e) {
- throw new SSLException(e);
+ return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced);
}
+
+ // Nothing to write, just shutdown and throw the exception.
+ shutdown();
+ throw convertException(e);
+ } catch (InterruptedIOException e) {
+ return newResult(bytesConsumed, bytesProduced, handshakeStatus);
+ } catch (EOFException e) {
+ closeAll();
+ throw convertException(e);
+ } catch (IOException e) {
+ shutdown();
+ throw convertException(e);
}
// There won't be any application data until we're done handshaking.
@@ -821,7 +876,7 @@
int capacity = 0;
for (int i = 0; i < dsts.length; i++) {
ByteBuffer dst = dsts[i];
- checkNotNull(dst, "one of the dst");
+ checkArgument(dst != null, "dsts[%d] is null", i);
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
@@ -845,45 +900,59 @@
}
private SSLEngineResult.HandshakeStatus handshake() throws SSLException {
- long sslSessionCtx = 0L;
try {
// Only actually perform the handshake if we haven't already just completed it
// via BIO operations.
- int code = NativeCrypto.ENGINE_SSL_do_handshake(sslNativePointer, this);
- if (code <= 0) {
- int sslError = NativeCrypto.SSL_get_error(sslNativePointer, code);
- switch (sslError) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- return pendingStatus(pendingOutboundEncryptedBytes());
- default:
- // Everything else is considered as error
- throw shutdownWithError("SSL_do_handshake");
+ try {
+ // First, check to see if we already have a pending alert that needs to be written.
+ if (handshakeException != null) {
+ if (pendingOutboundEncryptedBytes() > 0) {
+ // Need to finish writing the alert to the remote peer.
+ return NEED_WRAP;
+ }
+
+ // We've finished writing the alert, just throw the exception.
+ SSLException e = handshakeException;
+ handshakeException = null;
+ throw e;
}
+
+ int ssl_error_code = ssl.doHandshake();
+ switch (ssl_error_code) {
+ case SSL_ERROR_WANT_READ:
+ return pendingStatus(pendingOutboundEncryptedBytes());
+ case SSL_ERROR_WANT_WRITE: {
+ return NEED_WRAP;
+ }
+ default: {
+ // SSL_ERROR_NONE.
+ }
+ }
+ } catch (SSLException e) {
+ if (pendingOutboundEncryptedBytes() > 0) {
+ // Delay throwing the exception since we appear to have an outbound alert
+ // that needs to be written to the remote endpoint.
+ handshakeException = e;
+ return NEED_WRAP;
+ }
+
+ // There is no pending alert to write - just shutdown and throw.
+ shutdown();
+ throw e;
+ } catch (IOException e) {
+ shutdown();
+ throw e;
}
- // Handshake is finished!
- sslSessionCtx = NativeCrypto.SSL_get1_session(sslNativePointer);
- if (sslSessionCtx == 0) {
- // TODO(nathanmittler): Should we throw here?
- // return pendingStatus(pendingOutboundBytes());
- throw shutdownWithError("Failed to obtain session after handshake completed");
- }
- sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession,
- getSniHostname(), getPeerPort(), true);
- if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) {
- engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH;
- } else {
- engineState = EngineState.READY;
- }
+ // The handshake has completed successfully...
+
+ // Update the session from the current state of the SSL object.
+ sslSession.onSessionEstablished(getPeerHost(), getPeerPort());
+
finishHandshake();
return FINISHED;
} catch (Exception e) {
throw toSSLHandshakeException(e);
- } finally {
- if (sslSession == null && sslSessionCtx != 0) {
- NativeCrypto.SSL_SESSION_free(sslSessionCtx);
- }
}
}
@@ -894,6 +963,7 @@
handshakeListener.onHandshakeFinished();
}
}
+
/**
* Write plaintext data to the OpenSSL internal BIO
*
@@ -903,14 +973,10 @@
try {
final int pos = src.position();
final int sslWrote;
-
if (src.isDirect()) {
- long addr = NativeCrypto.getDirectBufferAddress(src) + pos;
- sslWrote = NativeCrypto.ENGINE_SSL_write_direct(sslNativePointer, addr, len, this);
+ sslWrote = writePlaintextDataDirect(src, pos, len);
} else {
- ByteBuffer heapSrc = toHeapBuffer(src, len);
- sslWrote = NativeCrypto.ENGINE_SSL_write_heap(sslNativePointer, heapSrc.array(),
- heapSrc.arrayOffset() + heapSrc.position(), len, this);
+ sslWrote = writePlaintextDataHeap(src, pos, len);
}
if (sslWrote > 0) {
src.position(pos + sslWrote);
@@ -921,40 +987,105 @@
}
}
+ private int writePlaintextDataDirect(ByteBuffer src, int pos, int len) throws IOException {
+ return ssl.writeDirectByteBuffer(directByteBufferAddress(src, pos), len);
+ }
+
+ 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();
+ }
+
+ // Copy the data to the direct buffer.
+ int limit = src.limit();
+ int bytesToWrite = min(len, buffer.remaining());
+ src.limit(pos + bytesToWrite);
+ buffer.put(src);
+ buffer.flip();
+ // Restore the original position and limit.
+ src.limit(limit);
+ src.position(pos);
+
+ return writePlaintextDataDirect(buffer, 0, bytesToWrite);
+ } finally {
+ if (allocatedBuffer != null) {
+ // Release the buffer back to the pool.
+ allocatedBuffer.release();
+ }
+ }
+ }
+
/**
* Read plaintext data from the OpenSSL internal BIO
*/
- private int readPlaintextData(final ByteBuffer dst) throws SSLException {
+ private int readPlaintextData(final ByteBuffer dst) throws IOException {
try {
- final int sslRead;
final int pos = dst.position();
final int limit = dst.limit();
- final int len = Math.min(SSL3_RT_MAX_PACKET_SIZE, limit - pos);
+ final int len = min(SSL3_RT_MAX_PACKET_SIZE, limit - pos);
if (dst.isDirect()) {
- long addr = NativeCrypto.getDirectBufferAddress(dst) + pos;
- sslRead = NativeCrypto.ENGINE_SSL_read_direct(sslNativePointer, addr, len, this);
- if (sslRead > 0) {
- dst.position(pos + sslRead);
+ int bytesRead = readPlaintextDataDirect(dst, pos, len);
+ if (bytesRead > 0) {
+ dst.position(pos + bytesRead);
}
- } else if (dst.hasArray()) {
- sslRead = NativeCrypto.ENGINE_SSL_read_heap(
- sslNativePointer, dst.array(), dst.arrayOffset() + pos, len, this);
- if (sslRead > 0) {
- dst.position(pos + sslRead);
- }
- } else {
- byte[] data = new byte[len];
- sslRead = NativeCrypto.ENGINE_SSL_read_heap(sslNativePointer, data, 0, len, this);
- if (sslRead > 0) {
- dst.put(data, 0, sslRead);
- }
+ return bytesRead;
}
- return sslRead;
- } catch (Exception e) {
+
+ // The heap method updates the dst position automatically.
+ return readPlaintextDataHeap(dst, len);
+ } catch (CertificateException e) {
throw convertException(e);
}
}
+ private int readPlaintextDataDirect(ByteBuffer dst, int pos, int len)
+ throws IOException, CertificateException {
+ return ssl.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
+ }
+
+ 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();
+ }
+
+ // Read the data to the direct buffer.
+ int bytesToRead = min(len, buffer.remaining());
+ int bytesRead = readPlaintextDataDirect(buffer, 0, bytesToRead);
+ if (bytesRead > 0) {
+ // Copy the data to the heap buffer.
+ buffer.position(bytesRead);
+ buffer.flip();
+ dst.put(buffer);
+ }
+
+ return bytesRead;
+ } finally {
+ if (allocatedBuffer != null) {
+ // Release the buffer back to the pool.
+ allocatedBuffer.release();
+ }
+ }
+ }
+
private SSLException convertException(Throwable e) {
if (e instanceof SSLHandshakeException || !handshakeFinished) {
return SSLUtils.toSSLHandshakeException(e);
@@ -968,27 +1099,78 @@
private int writeEncryptedData(final ByteBuffer src, int len) throws SSLException {
try {
final int pos = src.position();
- final int netWrote;
+ final int bytesWritten;
if (src.isDirect()) {
- long addr = NativeCrypto.getDirectBufferAddress(src) + pos;
- netWrote = NativeCrypto.ENGINE_SSL_write_BIO_direct(
- sslNativePointer, networkBio, addr, len, this);
+ bytesWritten = writeEncryptedDataDirect(src, pos, len);
} else {
- ByteBuffer heapSrc = toHeapBuffer(src, len);
- netWrote = NativeCrypto.ENGINE_SSL_write_BIO_heap(sslNativePointer, networkBio,
- heapSrc.array(), heapSrc.arrayOffset() + heapSrc.position(), len, this);
+ bytesWritten = writeEncryptedDataHeap(src, pos, len);
}
- if (netWrote >= 0) {
- src.position(pos + netWrote);
+ if (bytesWritten > 0) {
+ src.position(pos + bytesWritten);
}
- return netWrote;
+ return bytesWritten;
} catch (IOException e) {
throw new SSLException(e);
}
}
+ private int writeEncryptedDataDirect(ByteBuffer src, int pos, int len) throws IOException {
+ return networkBio.writeDirectByteBuffer(directByteBufferAddress(src, pos), len);
+ }
+
+ private int writeEncryptedDataHeap(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 encrypted packets.
+ buffer = getOrCreateLazyDirectBuffer();
+ }
+
+ int limit = src.limit();
+ int bytesToCopy = min(min(limit - pos, len), buffer.remaining());
+ src.limit(pos + bytesToCopy);
+ buffer.put(src);
+ // Restore the original limit.
+ src.limit(limit);
+
+ // Reset the original position on the source buffer.
+ src.position(pos);
+
+ int bytesWritten = writeEncryptedDataDirect(buffer, 0, bytesToCopy);
+
+ // Restore the original position.
+ src.position(pos);
+
+ return bytesWritten;
+ } finally {
+ if (allocatedBuffer != null) {
+ // Release the buffer back to the pool.
+ allocatedBuffer.release();
+ }
+ }
+ }
+
+ 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;
+ }
+
private SSLEngineResult readPendingBytesFromBIO(ByteBuffer dst, int bytesConsumed,
int bytesProduced, SSLEngineResult.HandshakeStatus status) throws SSLException {
try {
@@ -1033,47 +1215,67 @@
*/
private int readEncryptedData(final ByteBuffer dst, final int pending) throws SSLException {
try {
- int bioRead = 0;
+ int bytesRead = 0;
+ final int pos = dst.position();
if (dst.remaining() >= pending) {
- final int pos = dst.position();
final int limit = dst.limit();
- final int len = Math.min(pending, limit - pos);
+ final int len = min(pending, limit - pos);
if (dst.isDirect()) {
- long addr = NativeCrypto.getDirectBufferAddress(dst) + pos;
- bioRead = NativeCrypto.ENGINE_SSL_read_BIO_direct(
- sslNativePointer, networkBio, addr, len, this);
- if (bioRead > 0) {
- dst.position(pos + bioRead);
- return bioRead;
- }
- } else if (dst.hasArray()) {
- bioRead = NativeCrypto.ENGINE_SSL_read_BIO_heap(sslNativePointer, networkBio,
- dst.array(), dst.arrayOffset() + pos, pending, this);
- if (bioRead > 0) {
- dst.position(pos + bioRead);
- return bioRead;
+ bytesRead = readEncryptedDataDirect(dst, pos, len);
+ // Need to update the position on the dst buffer.
+ if (bytesRead > 0) {
+ dst.position(pos + bytesRead);
}
} else {
- byte[] data = new byte[len];
- bioRead = NativeCrypto.ENGINE_SSL_read_BIO_heap(
- sslNativePointer, networkBio, data, 0, pending, this);
- if (bioRead > 0) {
- dst.put(data, 0, bioRead);
- return bioRead;
- }
+ // The heap method will update the position on the dst buffer automatically.
+ bytesRead = readEncryptedDataHeap(dst, pos, len);
}
}
- return bioRead;
+
+ return bytesRead;
} catch (Exception e) {
throw convertException(e);
}
}
+ private int readEncryptedDataDirect(ByteBuffer dst, int pos, int len) throws IOException {
+ return networkBio.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
+ }
+
+ private int readEncryptedDataHeap(ByteBuffer dst, 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 encrypted packets.
+ buffer = getOrCreateLazyDirectBuffer();
+ }
+
+ int bytesToRead = min(len, buffer.remaining());
+ int bytesRead = readEncryptedDataDirect(buffer, pos, bytesToRead);
+ if (bytesRead > 0) {
+ buffer.position(bytesRead);
+ buffer.flip();
+ dst.put(buffer);
+ }
+
+ return bytesRead;
+ } finally {
+ if (allocatedBuffer != null) {
+ // Release the buffer back to the pool.
+ allocatedBuffer.release();
+ }
+ }
+ }
+
private SSLEngineResult.HandshakeStatus mayFinishHandshake(
SSLEngineResult.HandshakeStatus status) throws SSLException {
- if (!handshakeFinished
- && status
- == NOT_HANDSHAKING /*|| engineState == EngineState.HANDSHAKE_COMPLETED)*/) {
+ if (!handshakeFinished && status == NOT_HANDSHAKING) {
// If the status was NOT_HANDSHAKING and we not finished the handshake we need to call
// SSL_do_handshake() again
return handshake();
@@ -1087,10 +1289,10 @@
}
private SSLEngineResult.Status getEngineStatus() {
- switch (engineState) {
- case CLOSED_INBOUND:
- case CLOSED_OUTBOUND:
- case CLOSED:
+ switch (state) {
+ case STATE_CLOSED_INBOUND:
+ case STATE_CLOSED_OUTBOUND:
+ case STATE_CLOSED:
return CLOSED;
default:
return OK;
@@ -1102,18 +1304,10 @@
closeInbound();
}
- private SSLEngineResult sslReadErrorResult(int err, int bytesConsumed, int bytesProduced)
- throws SSLException {
- if (!handshakeFinished && pendingOutboundEncryptedBytes() > 0) {
- return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced);
- }
- throw shutdownWithError(NativeCrypto.SSL_get_error_string(err));
- }
-
private SSLException shutdownWithError(String err) {
// There was an internal error -- shutdown
shutdown();
- if (getHandshakeStatusInternal() == HandshakeStatus.FINISHED) {
+ if (!handshakeFinished) {
return new SSLException(err);
}
return new SSLHandshakeException(err);
@@ -1138,25 +1332,25 @@
}
@Override
- public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst)
+ public SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer dst)
throws SSLException {
- checkNotNull(srcs, "srcs");
- checkNotNull(dst, "dst");
- checkIndex(srcs.length, offset, length, "srcs");
+ checkArgument(srcs != null, "srcs is null");
+ checkArgument(dst != null, "dst is null");
+ checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, srcs.length);
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
synchronized (stateLock) {
- switch (engineState) {
- case MODE_SET:
+ switch (state) {
+ case STATE_MODE_SET:
// Begin the handshake implicitly.
beginHandshakeInternal();
break;
- case CLOSED_OUTBOUND:
- case CLOSED:
+ case STATE_CLOSED_OUTBOUND:
+ case STATE_CLOSED:
return new SSLEngineResult(Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
- case NEW:
+ case STATE_NEW:
throw new IllegalStateException(
"Client/server mode must be set before calling wrap");
default:
@@ -1172,15 +1366,15 @@
return NEED_UNWRAP_OK;
}
- if (engineState == EngineState.CLOSED) {
+ if (state == STATE_CLOSED) {
return NEED_UNWRAP_CLOSED;
}
// NEED_WRAP - just fall through to perform the wrap.
}
int srcsLen = 0;
- final int endOffset = offset + length;
- for (int i = offset; i < endOffset; ++i) {
+ final int endOffset = srcsOffset + srcsLength;
+ for (int i = srcsOffset; i < endOffset; ++i) {
final ByteBuffer src = srcs[i];
if (src == null) {
throw new IllegalArgumentException("srcs[" + i + "] is null");
@@ -1207,14 +1401,14 @@
int bytesProduced = 0;
int bytesConsumed = 0;
loop:
- for (int i = offset; i < endOffset; ++i) {
+ for (int i = srcsOffset; i < endOffset; ++i) {
final ByteBuffer src = srcs[i];
- checkNotNull(src, "srcs[%d] is null", i);
+ checkArgument(src != null, "srcs[%d] is null", i);
while (src.hasRemaining()) {
final SSLEngineResult pendingNetResult;
// Write plaintext application data to the SSL engine
- int result = writePlaintextData(src,
- Math.min(src.remaining(), SSL3_RT_MAX_PLAIN_LENGTH - bytesConsumed));
+ int result = writePlaintextData(
+ src, min(src.remaining(), SSL3_RT_MAX_PLAIN_LENGTH - bytesConsumed));
if (result > 0) {
bytesConsumed += result;
@@ -1232,7 +1426,7 @@
break loop;
}
} else {
- int sslError = NativeCrypto.SSL_get_error(sslNativePointer, result);
+ int sslError = ssl.getError(result);
switch (sslError) {
case SSL_ERROR_ZERO_RETURN:
// This means the connection was shutdown correctly, close inbound
@@ -1305,37 +1499,65 @@
@Override
public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
- return sslParameters.clientPSKKeyRequested(identityHint, identity, key, this);
+ return ssl.clientPSKKeyRequested(identityHint, identity, key);
}
@Override
public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
- return sslParameters.serverPSKKeyRequested(identityHint, identity, key, this);
+ return ssl.serverPSKKeyRequested(identityHint, identity, key);
}
@Override
public void onSSLStateChange(int type, int val) {
synchronized (stateLock) {
switch (type) {
- case SSL_CB_HANDSHAKE_START:
+ case SSL_CB_HANDSHAKE_START: {
// For clients, this will allow the NEED_UNWRAP status to be
// returned.
- engineState = EngineState.HANDSHAKE_STARTED;
+ state = STATE_HANDSHAKE_STARTED;
break;
- case SSL_CB_HANDSHAKE_DONE:
- if (engineState != EngineState.HANDSHAKE_STARTED
- && engineState != EngineState.READY_HANDSHAKE_CUT_THROUGH) {
+ }
+ case SSL_CB_HANDSHAKE_DONE: {
+ if (state != STATE_HANDSHAKE_STARTED
+ && state != STATE_READY_HANDSHAKE_CUT_THROUGH) {
throw new IllegalStateException(
- "Completed handshake while in mode " + engineState);
+ "Completed handshake while in mode " + state);
}
- engineState = EngineState.HANDSHAKE_COMPLETED;
+ state = STATE_HANDSHAKE_COMPLETED;
break;
-
+ }
}
}
}
@Override
+ public void onNewSessionEstablished(long sslSessionNativePtr) {
+ try {
+ // Increment the reference count to "take ownership" of the session resource.
+ NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
+
+ // Create a native reference which will release the SSL_SESSION in its finalizer.
+ // This constructor will only throw if the native pointer passed in is NULL, which
+ // BoringSSL guarantees will not happen.
+ NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
+
+ SslSessionWrapper sessionWrapper = SslSessionWrapper.newInstance(ref, sslSession);
+
+ // Cache the newly established session.
+ AbstractSessionContext ctx = sessionContext();
+ ctx.cacheSession(sessionWrapper);
+ } catch (Exception ignored) {
+ // Ignore.
+ }
+ }
+
+ @Override
+ public long serverSessionRequested(byte[] id) {
+ // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
+ return 0;
+ }
+
+ @Override
public void verifyCertificateChain(long[] certRefs, String authMethod)
throws CertificateException {
try {
@@ -1349,13 +1571,8 @@
OpenSSLX509Certificate[] peerCertChain =
OpenSSLX509Certificate.createCertChain(certRefs);
- byte[] ocspData = NativeCrypto.SSL_get_ocsp_response(sslNativePointer);
- byte[] tlsSctData = NativeCrypto.SSL_get_signed_cert_timestamp_list(sslNativePointer);
-
- // Used for verifyCertificateChain callback
- handshakeSession = new OpenSSLSessionImpl(
- NativeCrypto.SSL_get1_session(sslNativePointer), null, peerCertChain, ocspData,
- tlsSctData, getSniHostname(), getPeerPort(), null);
+ // Update the peer information on the session.
+ sslSession.onPeerCertificatesReceived(getPeerHost(), getPeerPort(), peerCertChain);
if (getUseClientMode()) {
Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
@@ -1367,21 +1584,18 @@
throw e;
} catch (Exception e) {
throw new CertificateException(e);
- } finally {
- handshakeSession = null;
}
}
@Override
public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
throws CertificateEncodingException, SSLException {
- sslParameters.chooseClientCertificate(
- keyTypeBytes, asn1DerEncodedPrincipals, sslNativePointer, this);
+ ssl.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals);
}
private void shutdown() {
try {
- NativeCrypto.ENGINE_SSL_shutdown(sslNativePointer, this);
+ ssl.shutdown();
} catch (IOException ignored) {
// TODO: The RI ignores close failures in SSLSocket, but need to
// investigate whether it does for SSLEngine.
@@ -1397,13 +1611,10 @@
}
private void free() {
- if (sslNativePointer == 0) {
- return;
+ if (!ssl.isClosed()) {
+ ssl.close();
+ networkBio.close();
}
- NativeCrypto.SSL_free(sslNativePointer);
- NativeCrypto.BIO_free_all(networkBio);
- sslNativePointer = 0;
- networkBio = 0;
}
@Override
@@ -1415,12 +1626,6 @@
}
}
- /* @Override */
- @SuppressWarnings("MissingOverride") // For compilation with Java 6.
- public SSLSession getHandshakeSession() {
- return handshakeSession;
- }
-
@Override
public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
if (keyManager instanceof X509ExtendedKeyManager) {
@@ -1479,31 +1684,20 @@
}
/**
+ * Sets the list of ALPN protocols.
+ *
+ * @param alpnProtocols the list of ALPN protocols
+ */
+ void setAlpnProtocols(byte[] alpnProtocols) {
+ sslParameters.setAlpnProtocols(alpnProtocols);
+ }
+
+ /**
* Returns the protocol agreed upon by client and server, or {@code null} if no protocol was
* agreed upon.
*/
byte[] getAlpnSelectedProtocol() {
- return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer);
- }
-
- private ByteBuffer toHeapBuffer(ByteBuffer buffer, int len) {
- if (buffer.hasArray()) {
- return buffer;
- }
-
- // Need to copy to a heap buffer.
- final ByteBuffer heapBuffer = ByteBuffer.allocate(len);
- final int pos = buffer.position();
- final int limit = buffer.limit();
- buffer.limit(pos + len);
- try {
- heapBuffer.put(buffer);
- heapBuffer.flip();
- return heapBuffer;
- } finally {
- buffer.limit(limit);
- buffer.position(pos);
- }
+ return ssl.getAlpnSelectedProtocol();
}
private ByteBuffer[] singleSrcBuffer(ByteBuffer src) {
@@ -1524,18 +1718,11 @@
singleDstBuffer[0] = null;
}
- private static void checkIndex(int arrayLength, int offset, int length, String arrayName) {
- if ((offset | length) < 0 || offset + length > arrayLength) {
- throw new IndexOutOfBoundsException("offset: " + offset + ", length: " + length
- + " (expected: offset <= offset + length <= " + arrayName + ".length ("
- + arrayLength + "))");
- }
+ private ClientSessionContext clientSessionContext() {
+ return sslParameters.getClientSessionContext();
}
- private static <T> T checkNotNull(T obj, String fmt, Object... args) {
- if (obj == null) {
- throw new IllegalArgumentException(String.format(fmt, args));
- }
- return obj;
+ private AbstractSessionContext sessionContext() {
+ return sslParameters.getSessionContext();
}
}
diff --git a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java
new file mode 100644
index 0000000..f40efdd
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2016 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 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;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_READY;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.security.PrivateKey;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Implements crypto handling by delegating to {@link ConscryptEngine}.
+ */
+final class ConscryptEngineSocket extends OpenSSLSocketImpl {
+ private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
+
+ private final ConscryptEngine engine;
+ private final Object stateLock = new Object();
+ private final Object handshakeLock = new Object();
+
+ private SSLOutputStream out;
+ private SSLInputStream in;
+
+ // @GuardedBy("stateLock");
+ private int state = STATE_NEW;
+
+ ConscryptEngineSocket(SSLParametersImpl sslParameters) throws IOException {
+ engine = newEngine(sslParameters, this);
+ }
+
+ ConscryptEngineSocket(String hostname, int port, SSLParametersImpl sslParameters)
+ throws IOException {
+ super(hostname, port);
+ engine = newEngine(sslParameters, this);
+ }
+
+ ConscryptEngineSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
+ throws IOException {
+ super(address, port);
+ engine = newEngine(sslParameters, this);
+ }
+
+ ConscryptEngineSocket(String hostname, int port, InetAddress clientAddress, int clientPort,
+ SSLParametersImpl sslParameters) throws IOException {
+ super(hostname, port, clientAddress, clientPort);
+ engine = newEngine(sslParameters, this);
+ }
+
+ ConscryptEngineSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort,
+ SSLParametersImpl sslParameters) throws IOException {
+ super(address, port, clientAddress, clientPort);
+ engine = newEngine(sslParameters, this);
+ }
+
+ ConscryptEngineSocket(Socket socket, String hostname, int port, boolean autoClose,
+ SSLParametersImpl sslParameters) throws IOException {
+ super(socket, hostname, port, autoClose);
+ engine = newEngine(sslParameters, this);
+ }
+
+ private static ConscryptEngine newEngine(
+ SSLParametersImpl sslParameters, final ConscryptEngineSocket socket) {
+ ConscryptEngine engine = new ConscryptEngine(sslParameters, socket.peerInfoProvider());
+
+ // When the handshake completes, notify any listeners.
+ engine.setHandshakeListener(new HandshakeListener() {
+ /**
+ * Protected by {@code stateLock}
+ */
+ @Override
+ public void onHandshakeFinished() {
+ // Just call the outer class method.
+ socket.onHandshakeFinished();
+ }
+ });
+
+ // Transition the engine state to MODE_SET
+ engine.setUseClientMode(sslParameters.getUseClientMode());
+ return engine;
+ }
+
+ @Override
+ public SSLParameters getSSLParameters() {
+ return engine.getSSLParameters();
+ }
+
+ @Override
+ public void setSSLParameters(SSLParameters sslParameters) {
+ engine.setSSLParameters(sslParameters);
+ }
+
+ @Override
+ public void startHandshake() throws IOException {
+ checkOpen();
+
+ if (isHandshakeFinished()) {
+ // TODO(nmittler): Handle renegotiation.
+ return;
+ }
+
+ try {
+ synchronized (handshakeLock) {
+ // Only lock stateLock when we begin the handshake. This is done so that we don't
+ // hold the stateLock when we invoke the handshake completion listeners.
+ synchronized (stateLock) {
+ // Initialize the handshake if we haven't already.
+ if (state == STATE_NEW) {
+ state = STATE_HANDSHAKE_STARTED;
+ engine.beginHandshake();
+ in = new SSLInputStream();
+ out = new SSLOutputStream();
+ } else {
+ // We've either started the handshake already or have been closed.
+ // Do nothing in both cases.
+ return;
+ }
+ }
+
+ boolean finished = false;
+ while (!finished) {
+ switch (engine.getHandshakeStatus()) {
+ case NEED_UNWRAP:
+ if (in.readInternal(EmptyArray.BYTE, 0, 0) < 0) {
+ // Can't complete the handshake due to EOF.
+ throw SSLUtils.toSSLHandshakeException(new EOFException());
+ }
+ break;
+ case NEED_WRAP: {
+ out.writeInternal(EMPTY_BUFFER);
+ // Always flush handshake frames immediately.
+ out.flushInternal();
+ break;
+ }
+ case NEED_TASK:
+ // Should never get here, since our engine never provides tasks.
+ throw new IllegalStateException("Engine tasks are unsupported");
+ case NOT_HANDSHAKING:
+ case FINISHED:
+ // Handshake is complete.
+ finished = true;
+ break;
+ default: {
+ throw new IllegalStateException(
+ "Unknown handshake status: " + engine.getHandshakeStatus());
+ }
+ }
+ }
+ }
+ } catch (SSLException e) {
+ close();
+ throw e;
+ } catch (IOException e) {
+ close();
+ throw e;
+ } catch (Exception e) {
+ close();
+ // Convert anything else to a handshake exception.
+ throw SSLUtils.toSSLHandshakeException(e);
+ }
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ checkOpen();
+
+ // Block waiting for a handshake without a lock held. It's possible that the socket
+ // is closed at this point. If that happens, we'll still return the input stream but
+ // all reads on it will throw.
+ waitForHandshake();
+ return in;
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ checkOpen();
+
+ // Block waiting for a handshake without a lock held. It's possible that the socket
+ // is closed at this point. If that happens, we'll still return the input stream but
+ // all reads on it will throw.
+ waitForHandshake();
+
+ return out;
+ }
+
+ @Override
+ public SSLSession getHandshakeSession() {
+ return engine.handshakeSession();
+ }
+
+ @Override
+ public SSLSession getSession() {
+ SSLSession session = engine.getSession();
+ if (SSLNullSession.isNullSession(session)) {
+ boolean handshakeCompleted = false;
+ try {
+ if (isConnected()) {
+ waitForHandshake();
+ handshakeCompleted = true;
+ }
+ } catch (IOException e) {
+ // Fall through.
+ }
+
+ if (!handshakeCompleted) {
+ // Return an invalid session with invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+ return session;
+ }
+ session = engine.getSession();
+ }
+ return session;
+ }
+
+ @Override
+ SSLSession getActiveSession() {
+ return engine.getSession();
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ return engine.getEnableSessionCreation();
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean flag) {
+ engine.setEnableSessionCreation(flag);
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return engine.getSupportedCipherSuites();
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ return engine.getEnabledCipherSuites();
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] suites) {
+ engine.setEnabledCipherSuites(suites);
+ }
+
+ @Override
+ public String[] getSupportedProtocols() {
+ return engine.getSupportedProtocols();
+ }
+
+ @Override
+ public String[] getEnabledProtocols() {
+ return engine.getEnabledProtocols();
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ engine.setEnabledProtocols(protocols);
+ }
+
+ /**
+ * This method enables Server Name Indication
+ *
+ * @param hostname the desired SNI hostname, or null to disable
+ */
+ @Override
+ public void setHostname(String hostname) {
+ engine.setHostname(hostname);
+ super.setHostname(hostname);
+ }
+
+ @Override
+ public void setUseSessionTickets(boolean useSessionTickets) {
+ engine.setUseSessionTickets(useSessionTickets);
+ }
+
+ @Override
+ public void setChannelIdEnabled(boolean enabled) {
+ engine.setChannelIdEnabled(enabled);
+ }
+
+ @Override
+ public byte[] getChannelId() throws SSLException {
+ return engine.getChannelId();
+ }
+
+ @Override
+ public void setChannelIdPrivateKey(PrivateKey privateKey) {
+ engine.setChannelIdPrivateKey(privateKey);
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ return engine.getUseClientMode();
+ }
+
+ @Override
+ public void setUseClientMode(boolean mode) {
+ engine.setUseClientMode(mode);
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ return engine.getWantClientAuth();
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ return engine.getNeedClientAuth();
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean need) {
+ engine.setNeedClientAuth(need);
+ }
+
+ @Override
+ public void setWantClientAuth(boolean want) {
+ engine.setWantClientAuth(want);
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public void close() throws IOException {
+ // TODO: Close SSL sockets using a background thread so they close gracefully.
+
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ // close() has already been called, so do nothing and return.
+ return;
+ }
+
+ state = STATE_CLOSED;
+
+ stateLock.notifyAll();
+ }
+
+ // Close the underlying socket.
+ super.close();
+
+ // Close the engine.
+ engine.closeInbound();
+ engine.closeOutbound();
+ }
+
+ @Override
+ public byte[] getAlpnSelectedProtocol() {
+ return engine.getAlpnSelectedProtocol();
+ }
+
+ @Override
+ public void setAlpnProtocols(byte[] alpnProtocols) {
+ engine.setAlpnProtocols(alpnProtocols);
+ }
+
+ @Override
+ public void setAlpnProtocols(String[] alpnProtocols) {
+ engine.setAlpnProtocols(alpnProtocols);
+ }
+
+ private boolean isHandshakeFinished() {
+ return state >= STATE_READY_HANDSHAKE_CUT_THROUGH;
+ }
+
+ private void onHandshakeFinished() {
+ boolean notify = false;
+ synchronized (stateLock) {
+ if (state != STATE_CLOSED) {
+ if (state == STATE_HANDSHAKE_STARTED) {
+ state = STATE_READY_HANDSHAKE_CUT_THROUGH;
+ } else if (state == STATE_HANDSHAKE_COMPLETED) {
+ state = STATE_READY;
+ }
+
+ // Unblock threads that are waiting for our state to transition
+ // into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH.
+ stateLock.notifyAll();
+ notify = true;
+ }
+ }
+
+ if (notify) {
+ notifyHandshakeCompletedListeners();
+ }
+ }
+
+ /**
+ * Waits for the handshake to complete.
+ */
+ private void waitForHandshake() throws IOException {
+ startHandshake();
+
+ synchronized (stateLock) {
+ while (state != STATE_READY && state != STATE_READY_HANDSHAKE_CUT_THROUGH
+ && state != STATE_CLOSED) {
+ try {
+ stateLock.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Interrupted waiting for handshake", e);
+ }
+ }
+
+ if (state == STATE_CLOSED) {
+ throw new SocketException("Socket is closed");
+ }
+ }
+ }
+
+ private OutputStream getUnderlyingOutputStream() throws IOException {
+ return super.getOutputStream();
+ }
+
+ private InputStream getUnderlyingInputStream() throws IOException {
+ return super.getInputStream();
+ }
+
+ private SocketChannel getUnderlyingChannel() throws IOException {
+ return super.getChannel();
+ }
+
+ /**
+ * Wrap bytes written to the underlying socket.
+ */
+ private final class SSLOutputStream extends OutputStream {
+ private final Object writeLock = new Object();
+ private ByteBuffer target;
+ private OutputStream socketOutputStream;
+ private SocketChannel socketChannel;
+
+ SSLOutputStream() {}
+
+ @Override
+ public void close() throws IOException {
+ ConscryptEngineSocket.this.close();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ startHandshake();
+ synchronized (writeLock) {
+ write(new byte[] {(byte) b});
+ }
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ startHandshake();
+ synchronized (writeLock) {
+ writeInternal(ByteBuffer.wrap(b));
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ startHandshake();
+ synchronized (writeLock) {
+ writeInternal(ByteBuffer.wrap(b, off, len));
+ }
+ }
+
+ private void writeInternal(ByteBuffer buffer) throws IOException {
+ Platform.blockGuardOnNetwork();
+ checkOpen();
+ init();
+
+ // Need to loop through at least once to enable handshaking where no application
+ // bytes are
+ // processed.
+ int len = buffer.remaining();
+ SSLEngineResult engineResult;
+ do {
+ target.clear();
+ engineResult = engine.wrap(buffer, target);
+ if (engineResult.getStatus() != OK) {
+ throw new SSLException("Unexpected engine result " + engineResult.getStatus());
+ }
+ if (target.position() != engineResult.bytesProduced()) {
+ throw new SSLException("Engine bytesProduced " + engineResult.bytesProduced()
+ + " does not match bytes written " + target.position());
+ }
+ len -= engineResult.bytesConsumed();
+ if (len != buffer.remaining()) {
+ throw new SSLException("Engine did not read the correct number of bytes");
+ }
+
+ target.flip();
+
+ // Write the data to the socket.
+ writeToSocket();
+ } while (len > 0);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ startHandshake();
+ synchronized (writeLock) {
+ flushInternal();
+ }
+ }
+
+ private void flushInternal() throws IOException {
+ checkOpen();
+ init();
+ socketOutputStream.flush();
+ }
+
+ private void init() throws IOException {
+ if (socketOutputStream == null) {
+ socketOutputStream = getUnderlyingOutputStream();
+ socketChannel = getUnderlyingChannel();
+ if (socketChannel != null) {
+ // Optimization. Using direct buffers wherever possible to avoid passing
+ // arrays to JNI.
+ target = ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize());
+ } else {
+ target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
+ }
+ }
+ }
+
+ private void writeToSocket() throws IOException {
+ // Write the data to the socket.
+ if (socketChannel != null) {
+ // Loop until all of the data is written to the channel. Typically,
+ // SocketChannel writes will return only after all bytes are written,
+ // so we won't really loop here.
+ while (target.hasRemaining()) {
+ socketChannel.write(target);
+ }
+ } else {
+ // Target is a heap buffer.
+ socketOutputStream.write(target.array(), 0, target.limit());
+ }
+ }
+ }
+
+ /**
+ * Unwrap bytes read from the underlying socket.
+ */
+ private final class SSLInputStream extends InputStream {
+ private final Object readLock = new Object();
+ private final byte[] singleByte = new byte[1];
+ private final ByteBuffer fromEngine;
+ private ByteBuffer fromSocket;
+ private InputStream socketInputStream;
+ private SocketChannel socketChannel;
+
+ SSLInputStream() {
+ fromEngine = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize());
+ // Initially fromEngine.remaining() == 0.
+ fromEngine.flip();
+ }
+
+ @Override
+ public void close() throws IOException {
+ ConscryptEngineSocket.this.close();
+ }
+
+ @Override
+ public int read() throws IOException {
+ startHandshake();
+ synchronized (readLock) {
+ // Handle returning of -1 if EOF is reached.
+ int count = read(singleByte, 0, 1);
+ if (count == -1) {
+ // Handle EOF.
+ return -1;
+ }
+ if (count != 1) {
+ throw new SSLException("read incorrect number of bytes " + count);
+ }
+ return (int) singleByte[0];
+ }
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ startHandshake();
+ synchronized (readLock) {
+ return read(b, 0, b.length);
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ startHandshake();
+ synchronized (readLock) {
+ return readInternal(b, off, len);
+ }
+ }
+
+ @Override
+ public int available() throws IOException {
+ startHandshake();
+ synchronized (readLock) {
+ init();
+ return fromEngine.remaining()
+ + (fromSocket.hasRemaining() || socketInputStream.available() > 0 ? 1 : 0);
+ }
+ }
+
+ private int readInternal(byte[] b, int off, int len) throws IOException {
+ Platform.blockGuardOnNetwork();
+ checkOpen();
+
+ // Make sure the input stream has been created.
+ init();
+
+ for (;;) {
+ // Serve any remaining data from the engine first.
+ if (fromEngine.remaining() > 0) {
+ int readFromEngine = Math.min(fromEngine.remaining(), len);
+ fromEngine.get(b, off, readFromEngine);
+ return readFromEngine;
+ }
+
+ // Try to unwrap any data already in the socket buffer.
+ boolean needMoreDataFromSocket = true;
+
+ // Unwrap the unencrypted bytes into the engine buffer.
+ fromSocket.flip();
+ fromEngine.clear();
+ SSLEngineResult engineResult = engine.unwrap(fromSocket, fromEngine);
+ // Shift any remaining data to the beginning of the buffer so that
+ // we can accommodate the next full packet. After this is called,
+ // limit will be restored to capacity and position will point just
+ // past the end of the data.
+ fromSocket.compact();
+ fromEngine.flip();
+
+ switch (engineResult.getStatus()) {
+ case BUFFER_UNDERFLOW: {
+ if (engineResult.bytesProduced() == 0) {
+ // Need to read more data from the socket.
+ break;
+ }
+ // Also serve the data that was produced.
+ needMoreDataFromSocket = false;
+ break;
+ }
+ case OK: {
+ // We processed the entire packet successfully.
+ needMoreDataFromSocket = false;
+ break;
+ }
+ case CLOSED: {
+ // EOF
+ return -1;
+ }
+ default: {
+ // Anything else is an error.
+ throw new SSLException(
+ "Unexpected engine result " + engineResult.getStatus());
+ }
+ }
+
+ if (!needMoreDataFromSocket && engineResult.bytesProduced() == 0) {
+ // Read successfully, but produced no data. Possibly part of a
+ // handshake.
+ return 0;
+ }
+
+ // Read more data from the socket.
+ if (needMoreDataFromSocket && readFromSocket() == -1) {
+ // Failed to read the next encrypted packet before reaching EOF.
+ return -1;
+ }
+
+ // Continue the loop and return the data from the engine buffer.
+ }
+ }
+
+ private void init() throws IOException {
+ if (socketInputStream == null) {
+ socketInputStream = getUnderlyingInputStream();
+ socketChannel = getUnderlyingChannel();
+ if (socketChannel != null) {
+ fromSocket =
+ ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize());
+ } else {
+ fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
+ }
+ }
+ }
+
+ private int readFromSocket() throws IOException {
+ if (socketChannel != null) {
+ return socketChannel.read(fromSocket);
+ }
+ // Read directly to the underlying array and increment the buffer position if
+ // appropriate.
+ int read = socketInputStream.read(
+ fromSocket.array(), fromSocket.position(), fromSocket.remaining());
+ if (read > 0) {
+ fromSocket.position(fromSocket.position() + read);
+ }
+ return read;
+ }
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
new file mode 100644
index 0000000..3f800c5
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 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 static org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_READY;
+import static org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECParameterSpec;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import org.conscrypt.NativeRef.SSL_SESSION;
+
+/**
+ * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
+ * <p>
+ * Extensions to SSLSocket include:
+ * <ul>
+ * <li>handshake timeout
+ * <li>session tickets
+ * <li>Server Name Indication
+ * </ul>
+ */
+final class ConscryptFileDescriptorSocket extends OpenSSLSocketImpl
+ implements NativeCrypto.SSLHandshakeCallbacks, SSLParametersImpl.AliasChooser,
+ SSLParametersImpl.PSKCallbacks {
+ private static final boolean DBG_STATE = false;
+
+ /**
+ * Protects handshakeStarted and handshakeCompleted.
+ */
+ private final Object stateLock = new Object();
+
+ // @GuardedBy("stateLock");
+ private int state = STATE_NEW;
+
+ /**
+ * Protected by synchronizing on stateLock. Starts as 0, set by
+ * startHandshake, reset to 0 on close.
+ */
+ // @GuardedBy("stateLock");
+ private final SslWrapper ssl;
+
+ /**
+ * Protected by synchronizing on stateLock. Starts as null, set by
+ * getInputStream.
+ */
+ // @GuardedBy("stateLock");
+ private SSLInputStream is;
+
+ /**
+ * Protected by synchronizing on stateLock. Starts as null, set by
+ * getInputStream.
+ */
+ // @GuardedBy("stateLock");
+ private SSLOutputStream os;
+
+ private final SSLParametersImpl sslParameters;
+
+ /*
+ * A CloseGuard object on Android. On other platforms, this is nothing.
+ */
+ private final Object guard = Platform.closeGuardGet();
+
+ /**
+ * Private key for the TLS Channel ID extension. This field is client-side
+ * only. Set during startHandshake.
+ */
+ private OpenSSLKey channelIdPrivateKey;
+
+ private final ActiveSession sslSession;
+
+ private int writeTimeoutMilliseconds = 0;
+ private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite
+
+ ConscryptFileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException {
+ this.sslParameters = sslParameters;
+ this.ssl = newSsl(sslParameters, this);
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ ConscryptFileDescriptorSocket(String hostname, int port, SSLParametersImpl sslParameters)
+ throws IOException {
+ super(hostname, port);
+ this.sslParameters = sslParameters;
+ this.ssl = newSsl(sslParameters, this);
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ ConscryptFileDescriptorSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
+ throws IOException {
+ super(address, port);
+ this.sslParameters = sslParameters;
+ this.ssl = newSsl(sslParameters, this);
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ ConscryptFileDescriptorSocket(String hostname, int port, InetAddress clientAddress,
+ int clientPort, SSLParametersImpl sslParameters) throws IOException {
+ super(hostname, port, clientAddress, clientPort);
+ this.sslParameters = sslParameters;
+ this.ssl = newSsl(sslParameters, this);
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ ConscryptFileDescriptorSocket(InetAddress address, int port, InetAddress clientAddress,
+ int clientPort, SSLParametersImpl sslParameters) throws IOException {
+ super(address, port, clientAddress, clientPort);
+ this.sslParameters = sslParameters;
+ this.ssl = newSsl(sslParameters, this);
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ ConscryptFileDescriptorSocket(Socket socket, String hostname, int port, boolean autoClose,
+ SSLParametersImpl sslParameters) throws IOException {
+ super(socket, hostname, port, autoClose);
+ this.sslParameters = sslParameters;
+ this.ssl = newSsl(sslParameters, this);
+ sslSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+ }
+
+ private static SslWrapper newSsl(SSLParametersImpl sslParameters,
+ ConscryptFileDescriptorSocket engine) {
+ try {
+ return SslWrapper.newInstance(sslParameters, engine, engine, engine);
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Starts a TLS/SSL handshake on this connection using some native methods
+ * from the OpenSSL library. It can negotiate new encryption keys, change
+ * cipher suites, or initiate a new session. The certificate chain is
+ * verified if the correspondent property in java.Security is set. All
+ * listeners are notified at the end of the TLS/SSL handshake.
+ */
+ @Override
+ public void startHandshake() throws IOException {
+ checkOpen();
+ synchronized (stateLock) {
+ if (state == STATE_NEW) {
+ state = STATE_HANDSHAKE_STARTED;
+ } else {
+ // We've either started the handshake already or have been closed.
+ // Do nothing in both cases.
+ return;
+ }
+ }
+
+ boolean releaseResources = true;
+ try {
+ Platform.closeGuardOpen(guard, "close");
+
+ // Prepare the SSL object for the handshake.
+ ssl.initialize(getHostname(), channelIdPrivateKey);
+
+ // For clients, offer to resume a previously cached session to avoid the
+ // full TLS handshake.
+ if (getUseClientMode()) {
+ SslSessionWrapper cachedSession = clientSessionContext().getCachedSession(
+ getHostnameOrIP(), getPort(), sslParameters);
+ if (cachedSession != null) {
+ cachedSession.offerToResume(ssl);
+ }
+ }
+
+ // Temporarily use a different timeout for the handshake process
+ int savedReadTimeoutMilliseconds = getSoTimeout();
+ int savedWriteTimeoutMilliseconds = getSoWriteTimeout();
+ if (handshakeTimeoutMilliseconds >= 0) {
+ setSoTimeout(handshakeTimeoutMilliseconds);
+ setSoWriteTimeout(handshakeTimeoutMilliseconds);
+ }
+
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ return;
+ }
+ }
+
+ try {
+ ssl.doHandshake(Platform.getFileDescriptor(socket), getSoTimeout());
+ } catch (CertificateException e) {
+ SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
+ wrapper.initCause(e);
+ throw wrapper;
+ } catch (SSLException e) {
+ // Swallow this exception if it's thrown as the result of an interruption.
+ //
+ // TODO: SSL_read and SSL_write return -1 when interrupted, but SSL_do_handshake
+ // will throw the last sslError that it saw before sslSelect, usually SSL_WANT_READ
+ // (or WANT_WRITE). Catching that exception here doesn't seem much worse than
+ // changing the native code to return a "special" native pointer value when that
+ // happens.
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ return;
+ }
+ }
+
+ // Write CCS errors to EventLog
+ String message = e.getMessage();
+ // Must match error string of SSL_R_UNEXPECTED_CCS
+ if (message.contains("unexpected CCS")) {
+ String logMessage =
+ String.format("ssl_unexpected_ccs: host=%s", getHostnameOrIP());
+ Platform.logEvent(logMessage);
+ }
+
+ throw e;
+ }
+
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ return;
+ }
+ }
+
+ // Restore the original timeout now that the handshake is complete
+ if (handshakeTimeoutMilliseconds >= 0) {
+ setSoTimeout(savedReadTimeoutMilliseconds);
+ setSoWriteTimeout(savedWriteTimeoutMilliseconds);
+ }
+
+ synchronized (stateLock) {
+ releaseResources = (state == STATE_CLOSED);
+
+ if (state == STATE_HANDSHAKE_STARTED) {
+ state = STATE_READY_HANDSHAKE_CUT_THROUGH;
+ } else {
+ state = STATE_READY;
+ }
+
+ if (!releaseResources) {
+ // Unblock threads that are waiting for our state to transition
+ // into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH.
+ stateLock.notifyAll();
+ }
+ }
+ } catch (SSLProtocolException e) {
+ throw(SSLHandshakeException) new SSLHandshakeException("Handshake failed").initCause(e);
+ } finally {
+ // on exceptional exit, treat the socket as closed
+ if (releaseResources) {
+ synchronized (stateLock) {
+ // Mark the socket as closed since we might have reached this as
+ // a result on an exception thrown by the handshake process.
+ //
+ // The state will already be set to closed if we reach this as a result of
+ // an early return or an interruption due to a concurrent call to close().
+ state = STATE_CLOSED;
+ stateLock.notifyAll();
+ }
+
+ try {
+ shutdownAndFreeSslNative();
+ } catch (IOException ignored) {
+ // Ignored.
+ }
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
+ public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+ throws CertificateEncodingException, SSLException {
+ ssl.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals);
+ }
+
+ @Override
+ @SuppressWarnings("unused") // used by native psk_client_callback
+ public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+ return ssl.clientPSKKeyRequested(identityHint, identity, key);
+ }
+
+ @Override
+ @SuppressWarnings("unused") // used by native psk_server_callback
+ public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+ return ssl.serverPSKKeyRequested(identityHint, identity, key);
+ }
+
+ @Override
+ @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
+ public void onSSLStateChange(int type, int val) {
+ if (type != NativeConstants.SSL_CB_HANDSHAKE_DONE) {
+ // We only care about successful completion.
+ return;
+ }
+
+ // The handshake has completed successfully ...
+
+ // Update the session from the current state of the SSL object.
+ sslSession.onSessionEstablished(getHostnameOrIP(), getPort());
+
+ // First, update the state.
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ // Someone called "close" but the handshake hasn't been interrupted yet.
+ return;
+ }
+
+ // Now that we've fixed up our state, we can tell waiting threads that
+ // we're ready.
+ state = STATE_READY;
+ }
+
+ // Let listeners know we are finally done
+ notifyHandshakeCompletedListeners();
+
+ synchronized (stateLock) {
+ // Notify all threads waiting for the handshake to complete.
+ stateLock.notifyAll();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / new_session_callback
+ public void onNewSessionEstablished(long sslSessionNativePtr) {
+ try {
+ // Increment the reference count to "take ownership" of the session resource.
+ NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
+
+ // Create a native reference which will release the SSL_SESSION in its finalizer.
+ // This constructor will only throw if the native pointer passed in is NULL, which
+ // BoringSSL guarantees will not happen.
+ NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
+
+ SslSessionWrapper sessionWrapper = SslSessionWrapper.newInstance(ref, sslSession);
+
+ // Cache the newly established session.
+ AbstractSessionContext ctx = sessionContext();
+ ctx.cacheSession(sessionWrapper);
+ } catch (Exception ignored) {
+ // Ignore.
+ }
+ }
+
+ @Override
+ public long serverSessionRequested(byte[] id) {
+ // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
+ return 0;
+ }
+
+ @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
+ @Override
+ public void verifyCertificateChain(long[] certRefs, String authMethod)
+ throws CertificateException {
+ try {
+ X509TrustManager x509tm = sslParameters.getX509TrustManager();
+ if (x509tm == null) {
+ throw new CertificateException("No X.509 TrustManager");
+ }
+ if (certRefs == null || certRefs.length == 0) {
+ throw new SSLException("Peer sent no certificate");
+ }
+ OpenSSLX509Certificate[] peerCertChain =
+ OpenSSLX509Certificate.createCertChain(certRefs);
+
+ // Update the peer information on the session.
+ sslSession.onPeerCertificatesReceived(getHostnameOrIP(), getPort(), peerCertChain);
+
+ if (getUseClientMode()) {
+ Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
+ } else {
+ String authType = peerCertChain[0].getPublicKey().getAlgorithm();
+ Platform.checkClientTrusted(x509tm, peerCertChain, authType, this);
+ }
+ } catch (CertificateException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CertificateException(e);
+ }
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ checkOpen();
+
+ InputStream returnVal;
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ throw new SocketException("Socket is closed.");
+ }
+
+ if (is == null) {
+ is = new SSLInputStream();
+ }
+
+ returnVal = is;
+ }
+
+ // Block waiting for a handshake without a lock held. It's possible that the socket
+ // is closed at this point. If that happens, we'll still return the input stream but
+ // all reads on it will throw.
+ waitForHandshake();
+ return returnVal;
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ checkOpen();
+
+ OutputStream returnVal;
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ throw new SocketException("Socket is closed.");
+ }
+
+ if (os == null) {
+ os = new SSLOutputStream();
+ }
+
+ returnVal = os;
+ }
+
+ // Block waiting for a handshake without a lock held. It's possible that the socket
+ // is closed at this point. If that happens, we'll still return the output stream but
+ // all writes on it will throw.
+ waitForHandshake();
+ return returnVal;
+ }
+
+ private void assertReadableOrWriteableState() {
+ if (state == STATE_READY || state == STATE_READY_HANDSHAKE_CUT_THROUGH) {
+ return;
+ }
+
+ throw new AssertionError("Invalid state: " + state);
+ }
+
+ private void waitForHandshake() throws IOException {
+ startHandshake();
+
+ synchronized (stateLock) {
+ while (state != STATE_READY &&
+ state != STATE_READY_HANDSHAKE_CUT_THROUGH &&
+ state != STATE_CLOSED) {
+ try {
+ stateLock.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Interrupted waiting for handshake", e);
+ }
+ }
+
+ if (state == STATE_CLOSED) {
+ throw new SocketException("Socket is closed");
+ }
+ }
+ }
+
+ /**
+ * This inner class provides input data stream functionality
+ * for the OpenSSL native implementation. It is used to
+ * read data received via SSL protocol.
+ */
+ private class SSLInputStream extends InputStream {
+ /**
+ * OpenSSL only lets one thread read at a time, so this is used to
+ * make sure we serialize callers of SSL_read. Thread is already
+ * expected to have completed handshaking.
+ */
+ private final Object readLock = new Object();
+
+ SSLInputStream() {
+ }
+
+ /**
+ * Reads one byte. If there is no data in the underlying buffer,
+ * this operation can block until the data will be
+ * available.
+ */
+ @Override
+ public int read() throws IOException {
+ byte[] buffer = new byte[1];
+ int result = read(buffer, 0, 1);
+ return (result != -1) ? buffer[0] & 0xff : -1;
+ }
+
+ /**
+ * Method acts as described in spec for superclass.
+ * @see java.io.InputStream#read(byte[],int,int)
+ */
+ @Override
+ public int read(byte[] buf, int offset, int byteCount) throws IOException {
+ Platform.blockGuardOnNetwork();
+
+ checkOpen();
+ ArrayUtils.checkOffsetAndCount(buf.length, offset, byteCount);
+ if (byteCount == 0) {
+ return 0;
+ }
+
+ synchronized (readLock) {
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ throw new SocketException("socket is closed");
+ }
+
+ if (DBG_STATE) {
+ assertReadableOrWriteableState();
+ }
+ }
+
+ int ret = ssl.read(
+ Platform.getFileDescriptor(socket), buf, offset, byteCount, getSoTimeout());
+ if (ret == -1) {
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ throw new SocketException("socket is closed");
+ }
+ }
+ }
+ return ret;
+ }
+ }
+
+ void awaitPendingOps() {
+ if (DBG_STATE) {
+ synchronized (stateLock) {
+ if (state != STATE_CLOSED) {
+ throw new AssertionError("State is: " + state);
+ }
+ }
+ }
+
+ synchronized (readLock) {}
+ }
+ }
+
+ /**
+ * This inner class provides output data stream functionality
+ * for the OpenSSL native implementation. It is used to
+ * write data according to the encryption parameters given in SSL context.
+ */
+ private class SSLOutputStream extends OutputStream {
+ /**
+ * OpenSSL only lets one thread write at a time, so this is used
+ * to make sure we serialize callers of SSL_write. Thread is
+ * already expected to have completed handshaking.
+ */
+ private final Object writeLock = new Object();
+
+ SSLOutputStream() {
+ }
+
+ /**
+ * Method acts as described in spec for superclass.
+ * @see java.io.OutputStream#write(int)
+ */
+ @Override
+ public void write(int oneByte) throws IOException {
+ byte[] buffer = new byte[1];
+ buffer[0] = (byte) (oneByte & 0xff);
+ write(buffer);
+ }
+
+ /**
+ * Method acts as described in spec for superclass.
+ * @see java.io.OutputStream#write(byte[],int,int)
+ */
+ @Override
+ public void write(byte[] buf, int offset, int byteCount) throws IOException {
+ Platform.blockGuardOnNetwork();
+ checkOpen();
+ ArrayUtils.checkOffsetAndCount(buf.length, offset, byteCount);
+ if (byteCount == 0) {
+ return;
+ }
+
+ synchronized (writeLock) {
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ throw new SocketException("socket is closed");
+ }
+
+ if (DBG_STATE) {
+ assertReadableOrWriteableState();
+ }
+ }
+
+ ssl.write(Platform.getFileDescriptor(socket), buf, offset, byteCount,
+ writeTimeoutMilliseconds);
+
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ throw new SocketException("socket is closed");
+ }
+ }
+ }
+ }
+
+ void awaitPendingOps() {
+ if (DBG_STATE) {
+ synchronized (stateLock) {
+ if (state != STATE_CLOSED) {
+ throw new AssertionError("State is: " + state);
+ }
+ }
+ }
+
+ synchronized (writeLock) {}
+ }
+ }
+
+ @Override
+ public SSLSession getSession() {
+ boolean handshakeCompleted = false;
+ synchronized (stateLock) {
+ try {
+ handshakeCompleted = state >= STATE_READY;
+ if (!handshakeCompleted && isConnected()) {
+ waitForHandshake();
+ handshakeCompleted = true;
+ }
+ } catch (IOException e) {
+ // Fall through.
+ }
+ }
+
+ if (!handshakeCompleted) {
+ // return an invalid session with
+ // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+ return SSLNullSession.getNullSession();
+ }
+
+ return Platform.wrapSSLSession(sslSession);
+ }
+
+ @Override
+ SSLSession getActiveSession() {
+ return sslSession;
+ }
+
+ @Override
+ public SSLSession getHandshakeSession() {
+ synchronized (stateLock) {
+ return state >= STATE_HANDSHAKE_STARTED && state < STATE_READY ? sslSession : null;
+ }
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ return sslParameters.getEnableSessionCreation();
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean flag) {
+ sslParameters.setEnableSessionCreation(flag);
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return NativeCrypto.getSupportedCipherSuites();
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ return sslParameters.getEnabledCipherSuites();
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] suites) {
+ sslParameters.setEnabledCipherSuites(suites);
+ }
+
+ @Override
+ public String[] getSupportedProtocols() {
+ return NativeCrypto.getSupportedProtocols();
+ }
+
+ @Override
+ public String[] getEnabledProtocols() {
+ return sslParameters.getEnabledProtocols();
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ sslParameters.setEnabledProtocols(protocols);
+ }
+
+ /**
+ * This method enables session ticket support.
+ *
+ * @param useSessionTickets True to enable session tickets
+ */
+ @Override
+ public void setUseSessionTickets(boolean useSessionTickets) {
+ sslParameters.setUseSessionTickets(useSessionTickets);
+ }
+
+ /**
+ * This method enables Server Name Indication
+ *
+ * @param hostname the desired SNI hostname, or null to disable
+ */
+ @Override
+ public void setHostname(String hostname) {
+ sslParameters.setUseSni(hostname != null);
+ super.setHostname(hostname);
+ }
+
+ /**
+ * Enables/disables TLS Channel ID for this server socket.
+ *
+ * <p>This method needs to be invoked before the handshake starts.
+ *
+ * @throws IllegalStateException if this is a client socket or if the handshake has already
+ * started.
+ */
+ @Override
+ public void setChannelIdEnabled(boolean enabled) {
+ if (getUseClientMode()) {
+ throw new IllegalStateException("Client mode");
+ }
+
+ synchronized (stateLock) {
+ if (state != STATE_NEW) {
+ throw new IllegalStateException(
+ "Could not enable/disable Channel ID after the initial handshake has"
+ + " begun.");
+ }
+ }
+ sslParameters.channelIdEnabled = enabled;
+ }
+
+ /**
+ * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
+ * handshake completes.
+ *
+ * @return channel ID or {@code null} if not available.
+ *
+ * @throws IllegalStateException if this is a client socket or if the handshake has not yet
+ * completed.
+ * @throws SSLException if channel ID is available but could not be obtained.
+ */
+ @Override
+ public byte[] getChannelId() throws SSLException {
+ if (getUseClientMode()) {
+ throw new IllegalStateException("Client mode");
+ }
+
+ synchronized (stateLock) {
+ if (state != STATE_READY) {
+ throw new IllegalStateException(
+ "Channel ID is only available after handshake completes");
+ }
+ }
+ return ssl.getTlsChannelId();
+ }
+
+ /**
+ * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
+ *
+ * <p>This method needs to be invoked before the handshake starts.
+ *
+ * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
+ * TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
+ * P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
+ *
+ * @throws IllegalStateException if this is a server socket or if the handshake has already
+ * started.
+ */
+ @Override
+ public void setChannelIdPrivateKey(PrivateKey privateKey) {
+ if (!getUseClientMode()) {
+ throw new IllegalStateException("Server mode");
+ }
+
+ synchronized (stateLock) {
+ if (state != STATE_NEW) {
+ throw new IllegalStateException(
+ "Could not change Channel ID private key after the initial handshake has"
+ + " begun.");
+ }
+ }
+
+ if (privateKey == null) {
+ sslParameters.channelIdEnabled = false;
+ channelIdPrivateKey = null;
+ } else {
+ sslParameters.channelIdEnabled = true;
+ try {
+ ECParameterSpec ecParams = null;
+ if (privateKey instanceof ECKey) {
+ ecParams = ((ECKey) privateKey).getParams();
+ }
+ if (ecParams == null) {
+ // Assume this is a P-256 key, as specified in the contract of this method.
+ ecParams =
+ OpenSSLECGroupContext.getCurveByName("prime256v1").getECParameterSpec();
+ }
+ channelIdPrivateKey =
+ OpenSSLKey.fromECPrivateKeyForTLSStackOnly(privateKey, ecParams);
+ } catch (InvalidKeyException e) {
+ // Will have error in startHandshake
+ }
+ }
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ return sslParameters.getUseClientMode();
+ }
+
+ @Override
+ public void setUseClientMode(boolean mode) {
+ synchronized (stateLock) {
+ if (state != STATE_NEW) {
+ throw new IllegalArgumentException(
+ "Could not change the mode after the initial handshake has begun.");
+ }
+ }
+ sslParameters.setUseClientMode(mode);
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ return sslParameters.getWantClientAuth();
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ return sslParameters.getNeedClientAuth();
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean need) {
+ sslParameters.setNeedClientAuth(need);
+ }
+
+ @Override
+ public void setWantClientAuth(boolean want) {
+ sslParameters.setWantClientAuth(want);
+ }
+
+ /**
+ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+ */
+ @Override
+ public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+ this.writeTimeoutMilliseconds = writeTimeoutMilliseconds;
+
+ Platform.setSocketWriteTimeout(this, writeTimeoutMilliseconds);
+ }
+
+ /**
+ * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+ */
+ @Override
+ public int getSoWriteTimeout() throws SocketException {
+ return writeTimeoutMilliseconds;
+ }
+
+ /**
+ * Set the handshake timeout on this socket. This timeout is specified in
+ * milliseconds and will be used only during the handshake process.
+ */
+ @Override
+ public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+ this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds;
+ }
+
+ @Override
+ @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+ public void close() throws IOException {
+ // TODO: Close SSL sockets using a background thread so they close gracefully.
+
+ SSLInputStream sslInputStream;
+ SSLOutputStream sslOutputStream;
+
+ synchronized (stateLock) {
+ if (state == STATE_CLOSED) {
+ // close() has already been called, so do nothing and return.
+ return;
+ }
+
+ int oldState = state;
+ state = STATE_CLOSED;
+
+ if (oldState == STATE_NEW) {
+ // The handshake hasn't been started yet, so there's no OpenSSL related
+ // state to clean up. We still need to close the underlying socket if
+ // we're wrapping it and were asked to autoClose.
+ free();
+ closeUnderlyingSocket();
+
+ stateLock.notifyAll();
+ return;
+ }
+
+ if (oldState != STATE_READY && oldState != STATE_READY_HANDSHAKE_CUT_THROUGH) {
+ // If we're in these states, we still haven't returned from startHandshake.
+ // We call SSL_interrupt so that we can interrupt SSL_do_handshake and then
+ // set the state to STATE_CLOSED. startHandshake will handle all cleanup
+ // after SSL_do_handshake returns, so we don't have anything to do here.
+ ssl.interrupt();
+
+ stateLock.notifyAll();
+ return;
+ }
+
+ stateLock.notifyAll();
+ // We've already returned from startHandshake, so we potentially have
+ // input and output streams to clean up.
+ sslInputStream = is;
+ sslOutputStream = os;
+ }
+
+ // Don't bother interrupting unless we have something to interrupt.
+ if (sslInputStream != null || sslOutputStream != null) {
+ ssl.interrupt();
+ }
+
+ // Wait for the input and output streams to finish any reads they have in
+ // progress. If there are no reads in progress at this point, future reads will
+ // throw because state == STATE_CLOSED
+ if (sslInputStream != null) {
+ sslInputStream.awaitPendingOps();
+ }
+ if (sslOutputStream != null) {
+ sslOutputStream.awaitPendingOps();
+ }
+
+ shutdownAndFreeSslNative();
+ }
+
+ private void shutdownAndFreeSslNative() throws IOException {
+ try {
+ Platform.blockGuardOnNetwork();
+ ssl.shutdown(Platform.getFileDescriptor(socket));
+ } catch (IOException ignored) {
+ /*
+ * Note that although close() can throw
+ * IOException, the RI does not throw if there
+ * is problem sending a "close notify" which
+ * can happen if the underlying socket is closed.
+ */
+ } finally {
+ free();
+ closeUnderlyingSocket();
+ }
+ }
+
+ private void closeUnderlyingSocket() throws IOException {
+ super.close();
+ }
+
+ private void free() {
+ if (!ssl.isClosed()) {
+ ssl.close();
+ Platform.closeGuardClose(guard);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ /*
+ * Just worry about our own state. Notably we do not try and
+ * close anything. The SocketImpl, either our own
+ * PlainSocketImpl, or the Socket we are wrapping, will do
+ * that. This might mean we do not properly SSL_shutdown, but
+ * if you want to do that, properly close the socket yourself.
+ *
+ * The reason why we don't try to SSL_shutdown, is that there
+ * can be a race between finalizers where the PlainSocketImpl
+ * finalizer runs first and closes the socket. However, in the
+ * meanwhile, the underlying file descriptor could be reused
+ * for another purpose. If we call SSL_shutdown, the
+ * underlying socket BIOs still have the old file descriptor
+ * and will write the close notify to some unsuspecting
+ * reader.
+ */
+ if (guard != null) {
+ Platform.closeGuardWarnIfOpen(guard);
+ }
+ free();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Returns the protocol agreed upon by client and server, or {@code null} if
+ * no protocol was agreed upon.
+ */
+ @Override
+ public byte[] getAlpnSelectedProtocol() {
+ return ssl.getAlpnSelectedProtocol();
+ }
+
+ /**
+ * Sets the list of ALPN protocols. This method internally converts the protocols to their
+ * wire-format form.
+ *
+ * @param alpnProtocols the list of ALPN protocols
+ * @see #setAlpnProtocols(byte[])
+ */
+ @Override
+ public void setAlpnProtocols(String[] alpnProtocols) {
+ sslParameters.setAlpnProtocols(alpnProtocols);
+ }
+
+ /**
+ * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of
+ * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings).
+ * Requires that all strings be encoded with US-ASCII.
+ *
+ * @param alpnProtocols the encoded form of the ALPN protocol list
+ * @see #setAlpnProtocols(String[])
+ */
+ @Override
+ public void setAlpnProtocols(byte[] alpnProtocols) {
+ sslParameters.setAlpnProtocols(alpnProtocols);
+ }
+
+ @Override
+ public SSLParameters getSSLParameters() {
+ SSLParameters params = super.getSSLParameters();
+ Platform.getSSLParameters(params, sslParameters, this);
+ return params;
+ }
+
+ @Override
+ public void setSSLParameters(SSLParameters p) {
+ super.setSSLParameters(p);
+ Platform.setSSLParameters(p, sslParameters, this);
+ }
+
+ @Override
+ public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
+ return keyManager.chooseServerAlias(keyType, null, this);
+ }
+
+ @Override
+ public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
+ String[] keyTypes) {
+ return keyManager.chooseClientAlias(keyTypes, null, this);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+ public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
+ return keyManager.chooseServerKeyIdentityHint(this);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+ public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
+ return keyManager.chooseClientKeyIdentity(identityHint, this);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+ public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
+ return keyManager.getKey(identityHint, identity, this);
+ }
+
+ private ClientSessionContext clientSessionContext() {
+ return sslParameters.getClientSessionContext();
+ }
+
+ private AbstractSessionContext sessionContext() {
+ return sslParameters.getSessionContext();
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java b/common/src/main/java/org/conscrypt/ConscryptServerSocket.java
similarity index 82%
rename from common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
rename to common/src/main/java/org/conscrypt/ConscryptServerSocket.java
index a7e3654..80a758f 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
+++ b/common/src/main/java/org/conscrypt/ConscryptServerSocket.java
@@ -19,32 +19,33 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
+import javax.net.ssl.SSLServerSocket;
/**
* BoringSSL-based implementation of server sockets.
*/
-final class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
+final class ConscryptServerSocket extends SSLServerSocket {
private final SSLParametersImpl sslParameters;
private boolean channelIdEnabled;
private boolean useEngineSocket;
- OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException {
+ ConscryptServerSocket(SSLParametersImpl sslParameters) throws IOException {
this.sslParameters = sslParameters;
}
- OpenSSLServerSocketImpl(int port, SSLParametersImpl sslParameters)
+ ConscryptServerSocket(int port, SSLParametersImpl sslParameters)
throws IOException {
super(port);
this.sslParameters = sslParameters;
}
- OpenSSLServerSocketImpl(int port, int backlog, SSLParametersImpl sslParameters)
+ ConscryptServerSocket(int port, int backlog, SSLParametersImpl sslParameters)
throws IOException {
super(port, backlog);
this.sslParameters = sslParameters;
}
- OpenSSLServerSocketImpl(int port,
+ ConscryptServerSocket(int port,
int backlog,
InetAddress iAddress,
SSLParametersImpl sslParameters)
@@ -56,7 +57,7 @@
/**
* Configures the socket to be created for this instance.
*/
- OpenSSLServerSocketImpl setUseEngineSocket(boolean useEngineSocket) {
+ ConscryptServerSocket setUseEngineSocket(boolean useEngineSocket) {
this.useEngineSocket = useEngineSocket;
return this;
}
@@ -174,21 +175,15 @@
@Override
public Socket accept() throws IOException {
+ final AbstractConscryptSocket socket;
if (useEngineSocket) {
- Socket rawSocket = new Socket();
- implAccept(rawSocket);
-
- // Enable channel ID.
- OpenSSLEngineSocketImpl socket =
- new OpenSSLEngineSocketImpl(rawSocket, null, -1, true, sslParameters);
- socket.setChannelIdEnabled(channelIdEnabled);
- socket.startHandshake();
- return socket;
+ socket = new ConscryptEngineSocket(sslParameters);
} else {
- OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters);
- socket.setChannelIdEnabled(channelIdEnabled);
- implAccept(socket);
- return socket;
+ socket = new ConscryptFileDescriptorSocket(sslParameters);
}
+
+ socket.setChannelIdEnabled(channelIdEnabled);
+ implAccept(socket);
+ return socket;
}
}
diff --git a/common/src/main/java/org/conscrypt/DESEDESecretKeyFactory.java b/common/src/main/java/org/conscrypt/DESEDESecretKeyFactory.java
index 487e013..0c79294 100644
--- a/common/src/main/java/org/conscrypt/DESEDESecretKeyFactory.java
+++ b/common/src/main/java/org/conscrypt/DESEDESecretKeyFactory.java
@@ -38,7 +38,16 @@
throw new InvalidKeySpecException("Null KeySpec");
}
if (keySpec instanceof SecretKeySpec) {
- return (SecretKey) keySpec;
+ SecretKeySpec key = (SecretKeySpec) keySpec;
+ try {
+ if (!DESedeKeySpec.isParityAdjusted(key.getEncoded(), 0)) {
+ throw new InvalidKeySpecException(
+ "SecretKeySpec is not a parity-adjusted DESEDE key");
+ }
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
+ }
+ return key;
} else if (keySpec instanceof DESedeKeySpec) {
DESedeKeySpec desKeySpec = (DESedeKeySpec) keySpec;
return new SecretKeySpec(desKeySpec.getKey(), "DESEDE");
@@ -49,12 +58,19 @@
}
@Override
- protected KeySpec engineGetKeySpec(SecretKey secretKey, Class<?> aClass)
- throws InvalidKeySpecException {
+ protected KeySpec engineGetKeySpec(SecretKey secretKey,
+ @SuppressWarnings("rawtypes") Class aClass) throws InvalidKeySpecException {
if (secretKey == null) {
throw new InvalidKeySpecException("Null SecretKey");
}
if (aClass == SecretKeySpec.class) {
+ try {
+ if (!DESedeKeySpec.isParityAdjusted(secretKey.getEncoded(), 0)) {
+ throw new InvalidKeySpecException("SecretKey is not a parity-adjusted DESEDE key");
+ }
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
+ }
if (secretKey instanceof SecretKeySpec) {
return (KeySpec) secretKey;
} else {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java b/common/src/main/java/org/conscrypt/DelegatingExtendedSSLSession.java
similarity index 81%
rename from common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java
rename to common/src/main/java/org/conscrypt/DelegatingExtendedSSLSession.java
index dead13d..e0114fd 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java
+++ b/common/src/main/java/org/conscrypt/DelegatingExtendedSSLSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * 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.
@@ -30,44 +30,46 @@
* Implementation of the ExtendedSSLSession class for OpenSSL. Uses a delegate to maintain backward
* compatibility with previous versions of Android which don't have ExtendedSSLSession.
*/
-final class OpenSSLExtendedSessionImpl extends ExtendedSSLSession {
- private final AbstractOpenSSLSession delegate;
+final class DelegatingExtendedSSLSession extends ExtendedSSLSession {
+ // TODO: use BoringSSL API to actually fetch the real data
+ private static final String[] LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS = new String[] {
+ "SHA512withRSA",
+ "SHA512withECDSA",
+ "SHA384withRSA",
+ "SHA384withECDSA",
+ "SHA256withRSA",
+ "SHA256withECDSA",
+ "SHA224withRSA",
+ "SHA224withECDSA",
+ "SHA1withRSA",
+ "SHA1withECDSA",
+ };
+ // TODO: use BoringSSL API to actually fetch the real data
+ private static final String[] PEER_SUPPORTED_SIGNATURE_ALGORITHMS = new String[] {
+ "SHA1withRSA",
+ "SHA1withECDSA"
+ };
- OpenSSLExtendedSessionImpl(AbstractOpenSSLSession delegate) {
+ private final ActiveSession delegate;
+
+ DelegatingExtendedSSLSession(ActiveSession delegate) {
this.delegate = delegate;
}
- AbstractOpenSSLSession getDelegate() {
+ ActiveSession getDelegate() {
return delegate;
}
/* @Override */
@SuppressWarnings("MissingOverride") // For Android backward-compatibility.
public String[] getLocalSupportedSignatureAlgorithms() {
- // From src/ssl/t1_lib.c tls12_sigalgs
- // TODO: use BoringSSL API to actually fetch the real data
- return new String[] {
- "SHA512withRSA",
- "SHA512withECDSA",
- "SHA384withRSA",
- "SHA384withECDSA",
- "SHA256withRSA",
- "SHA256withECDSA",
- "SHA224withRSA",
- "SHA224withECDSA",
- "SHA1withRSA",
- "SHA1withECDSA",
- };
+ return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
}
/* @Override */
@SuppressWarnings("MissingOverride") // For Android backward-compatibility.
public String[] getPeerSupportedSignatureAlgorithms() {
- // TODO: use BoringSSL API to actually fetch the real data
- return new String[] {
- "SHA1withRSA",
- "SHA1withECDSA",
- };
+ return PEER_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
}
/* @Override */
diff --git a/common/src/main/java/org/conscrypt/GCMParameters.java b/common/src/main/java/org/conscrypt/GCMParameters.java
index d3c95bd..a47ab8c 100644
--- a/common/src/main/java/org/conscrypt/GCMParameters.java
+++ b/common/src/main/java/org/conscrypt/GCMParameters.java
@@ -16,17 +16,34 @@
package org.conscrypt;
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
/**
* GCM parameters used during an ciphering operation with {@link OpenSSLCipher}.
- * This class exists solely for backward compatibility with Android versions
- * that did not have the {@code GCMParameterSpec} class.
+ * This class is used internally for backward compatibility with Android versions
+ * that did not have the {@code GCMParameterSpec} class, in addition to being the
+ * implementation of the GCM AlgorithmParameters implementation.
+ * <p>
+ * The only supported encoding format is ASN.1, as specified in RFC 5084 section 3.2.
+ *
+ * @hide
*/
-final class GCMParameters {
+@Internal
+public final class GCMParameters extends AlgorithmParametersSpi {
+
+ // The default value (in bits) for TLEN in the GCM ASN.1 module
+ private static final int DEFAULT_TLEN = 96;
+
/** The tag length in bits. */
- private final int tLen;
+ private int tLen;
/** Actually the nonce value for the GCM operation. */
- private final byte[] iv;
+ private byte[] iv;
+
+ public GCMParameters() { }
GCMParameters(int tLen, byte[] iv) {
this.tLen = tLen;
@@ -46,4 +63,92 @@
byte[] getIV() {
return iv;
}
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+ throws InvalidParameterSpecException {
+ GCMParameters params = Platform.fromGCMParameterSpec(algorithmParameterSpec);
+ if (params == null) {
+ throw new InvalidParameterSpecException("Only GCMParameterSpec is supported");
+ }
+ this.tLen = params.tLen;
+ this.iv = params.iv;
+ }
+
+ @Override
+ protected void engineInit(byte[] bytes) throws IOException {
+ long readRef = 0;
+ long seqRef = 0;
+ try {
+ readRef = NativeCrypto.asn1_read_init(bytes);
+ seqRef = NativeCrypto.asn1_read_sequence(readRef);
+ byte[] newIv = NativeCrypto.asn1_read_octetstring(seqRef);
+ int newTlen = DEFAULT_TLEN;
+ if (!NativeCrypto.asn1_read_is_empty(seqRef)) {
+ newTlen = 8 * (int) NativeCrypto.asn1_read_uint64(seqRef);
+ }
+ if (!NativeCrypto.asn1_read_is_empty(seqRef)
+ || !NativeCrypto.asn1_read_is_empty(readRef)) {
+ throw new IOException("Error reading ASN.1 encoding");
+ }
+ this.iv = newIv;
+ this.tLen = newTlen;
+ } finally {
+ NativeCrypto.asn1_read_free(seqRef);
+ NativeCrypto.asn1_read_free(readRef);
+ }
+ }
+
+ @Override
+ protected void engineInit(byte[] bytes, String format) throws IOException {
+ if ((format == null) || format.equals("ASN.1")) {
+ engineInit(bytes);
+ } else {
+ throw new IOException("Unsupported format: " + format);
+ }
+ }
+
+ @Override
+ protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
+ throws InvalidParameterSpecException {
+ if ((aClass != null) && aClass.getName().equals("javax.crypto.spec.GCMParameterSpec")) {
+ return aClass.cast(Platform.toGCMParameterSpec(tLen, iv));
+ } else {
+ throw new InvalidParameterSpecException("Unsupported class: " + aClass);
+ }
+ }
+
+ @Override
+ protected byte[] engineGetEncoded() throws IOException {
+ long cbbRef = 0;
+ long seqRef = 0;
+ try {
+ cbbRef = NativeCrypto.asn1_write_init();
+ seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
+ NativeCrypto.asn1_write_octetstring(seqRef, this.iv);
+ if (this.tLen != DEFAULT_TLEN) {
+ NativeCrypto.asn1_write_uint64(seqRef, this.tLen / 8);
+ }
+ return NativeCrypto.asn1_write_finish(cbbRef);
+ } catch (IOException e) {
+ NativeCrypto.asn1_write_cleanup(cbbRef);
+ throw e;
+ } finally {
+ NativeCrypto.asn1_write_free(seqRef);
+ NativeCrypto.asn1_write_free(cbbRef);
+ }
+ }
+
+ @Override
+ protected byte[] engineGetEncoded(String format) throws IOException {
+ if ((format == null) || format.equals("ASN.1")) {
+ return engineGetEncoded();
+ }
+ throw new IOException("Unsupported format: " + format);
+ }
+
+ @Override
+ protected String engineToString() {
+ return "Conscrypt GCM AlgorithmParameters";
+ }
}
diff --git a/common/src/main/java/org/conscrypt/KeyGeneratorImpl.java b/common/src/main/java/org/conscrypt/KeyGeneratorImpl.java
index 1223b13..c1b1261 100644
--- a/common/src/main/java/org/conscrypt/KeyGeneratorImpl.java
+++ b/common/src/main/java/org/conscrypt/KeyGeneratorImpl.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 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 java.security.InvalidAlgorithmParameterException;
@@ -126,7 +141,7 @@
// Set the parity bit for each byte
for (int i = 0; i < keyData.length; i++) {
if (Integer.bitCount(keyData[i]) % 2 == 0) {
- keyData[i] ^= 1;
+ keyData[i] = (byte) (keyData[i] ^ 1);
}
}
if (keyBytes == 14) {
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java
index b68c594..5b25748 100644
--- a/common/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -32,11 +32,8 @@
import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.Calendar;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
@@ -59,13 +56,17 @@
private native static void clinit();
+ /**
+ * Does nothing. Just for forcing static initialization.
+ */
+ static void checkAvailability() {
+ }
+
// --- DSA/RSA public/private key handling functions -----------------------
static native long EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q,
byte[] dmp1, byte[] dmq1, byte[] iqmp);
- static native int EVP_PKEY_size(NativeRef.EVP_PKEY pkey);
-
static native int EVP_PKEY_type(NativeRef.EVP_PKEY pkey);
static native String EVP_PKEY_print_public(NativeRef.EVP_PKEY pkeyRef);
@@ -76,13 +77,13 @@
static native int EVP_PKEY_cmp(NativeRef.EVP_PKEY pkey1, NativeRef.EVP_PKEY pkey2);
- static native byte[] i2d_PKCS8_PRIV_KEY_INFO(NativeRef.EVP_PKEY pkey);
+ static native byte[] EVP_marshal_private_key(NativeRef.EVP_PKEY pkey);
- static native long d2i_PKCS8_PRIV_KEY_INFO(byte[] data);
+ static native long EVP_parse_private_key(byte[] data);
- static native byte[] i2d_PUBKEY(NativeRef.EVP_PKEY pkey);
+ static native byte[] EVP_marshal_public_key(NativeRef.EVP_PKEY pkey);
- static native long d2i_PUBKEY(byte[] data);
+ static native long EVP_parse_public_key(byte[] data);
static native long PEM_read_bio_PUBKEY(long bioCtx);
@@ -118,10 +119,6 @@
*/
static native byte[][] get_RSA_private_params(NativeRef.EVP_PKEY rsa);
- static native byte[] i2d_RSAPublicKey(NativeRef.EVP_PKEY rsa);
-
- static native byte[] i2d_RSAPrivateKey(NativeRef.EVP_PKEY rsa);
-
// --- EC functions --------------------------
static native long EVP_PKEY_new_EC_KEY(
@@ -180,8 +177,6 @@
static native int EVP_MD_size(long evp_md_const);
- static native int EVP_MD_block_size(long evp_md_const);
-
// --- Message digest context functions --------------
static native long EVP_MD_CTX_create();
@@ -294,8 +289,6 @@
static native int EVP_AEAD_nonce_length(long evpAead);
- static native int EVP_AEAD_max_tag_len(long evpAead);
-
static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes, byte[] out,
int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad)
throws BadPaddingException;
@@ -322,14 +315,6 @@
static native void RAND_bytes(byte[] output);
- // --- ASN.1 objects -------------------------------------------------------
-
- static native int OBJ_txt2nid(String oid);
-
- static native String OBJ_txt2nid_longName(String oid);
-
- static native String OBJ_txt2nid_oid(String oid);
-
// --- X509_NAME -----------------------------------------------------------
static int X509_NAME_hash(X500Principal principal) {
@@ -350,8 +335,6 @@
}
}
- static native String X509_NAME_print_ex(long x509nameCtx, long flags);
-
// --- X509 ----------------------------------------------------------------
/** Used to request get_X509_GENERAL_NAME_stack get the "altname" field. */
@@ -536,17 +519,97 @@
static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal);
+ // --- ASN1 Encoding -------------------------------------------------------
+
+ /**
+ * Allocates and returns an opaque reference to an object that can be used with other
+ * asn1_read_* functions to read the ASN.1-encoded data in val. The returned object must
+ * be freed after use by calling asn1_read_free.
+ */
+ static native long asn1_read_init(byte[] val);
+
+ /**
+ * Allocates and returns an opaque reference to an object that can be used with other
+ * asn1_read_* functions to read the ASN.1 sequence pointed to by cbsRef. The returned
+ * object must be freed after use by calling asn1_read_free.
+ */
+ static native long asn1_read_sequence(long cbsRef) throws IOException;
+
+ /**
+ * Returns the contents of an ASN.1 octet string from the given reference.
+ */
+ static native byte[] asn1_read_octetstring(long cbsRef) throws IOException;
+
+ /**
+ * Returns an ASN.1 integer from the given reference. If the integer doesn't fit
+ * in a uint64, this method will throw an IOException.
+ */
+ static native long asn1_read_uint64(long cbsRef) throws IOException;
+
+ /**
+ * Returns whether or not the given reference has been read completely.
+ */
+ static native boolean asn1_read_is_empty(long cbsRef);
+
+ /**
+ * Frees any resources associated with the given reference. After calling, the reference
+ * must not be used again. This may be called with a zero reference, in which case nothing
+ * will be done.
+ */
+ static native void asn1_read_free(long cbsRef);
+
+ /**
+ * Allocates and returns an opaque reference to an object that can be used with other
+ * asn1_write_* functions to write ASN.1-encoded data. The returned object must be finalized
+ * after use by calling either asn1_write_finish or asn1_write_cleanup, and its resources
+ * must be freed by calling asn1_write_free.
+ */
+ static native long asn1_write_init() throws IOException;
+
+ /**
+ * Allocates and returns an opaque reference to an object that can be used with other
+ * asn1_write_* functions to write an ASN.1 sequence into the given reference. The returned
+ * reference may only be used until the next call on the parent reference. The returned
+ * object must be freed after use by calling asn1_write_free.
+ */
+ static native long asn1_write_sequence(long cbbRef) throws IOException;
+
+ /**
+ * Writes the given data into the given reference as an ASN.1-encoded octet string.
+ */
+ static native void asn1_write_octetstring(long cbbRef, byte[] data) throws IOException;
+
+ /**
+ * Writes the given value into the given reference as an ASN.1-encoded integer.
+ */
+ static native void asn1_write_uint64(long cbbRef, long value) throws IOException;
+
+ /**
+ * Completes any in-progress operations and returns the ASN.1-encoded data. Either this
+ * or asn1_write_cleanup must be called on any reference returned from asn1_write_init
+ * before it is freed.
+ */
+ static native byte[] asn1_write_finish(long cbbRef) throws IOException;
+
+ /**
+ * Cleans up intermediate state in the given reference. Either this or asn1_write_finish
+ * must be called on any reference returned from asn1_write_init before it is freed.
+ */
+ static native void asn1_write_cleanup(long cbbRef);
+
+ /**
+ * Frees resources associated with the given reference. After calling, the reference
+ * must not be used again. This may be called with a zero reference, in which case nothing
+ * will be done.
+ */
+ static native void asn1_write_free(long cbbRef);
+
// --- BIO stream creation -------------------------------------------------
static native long create_BIO_InputStream(OpenSSLBIOInputStream is, boolean isFinite);
static native long create_BIO_OutputStream(OutputStream os);
- static native int BIO_read(long bioRef, byte[] buffer);
-
- static native void BIO_write(long bioRef, byte[] buffer, int offset, int length)
- throws IOException;
-
static native void BIO_free_all(long bioRef);
// --- SSL handling --------------------------------------------------------
@@ -556,26 +619,12 @@
private static final String SUPPORTED_PROTOCOL_TLSV1_1 = "TLSv1.1";
private static final String SUPPORTED_PROTOCOL_TLSV1_2 = "TLSv1.2";
- // STANDARD_TO_OPENSSL_CIPHER_SUITES is a map from OpenSSL-style
- // cipher-suite names to the standard name for the same (i.e. the name that
- // is registered with IANA).
- static final Map<String, String> OPENSSL_TO_STANDARD_CIPHER_SUITES =
- new HashMap<String, String>();
-
- // STANDARD_TO_OPENSSL_CIPHER_SUITES is a map from "standard" cipher suite
- // names (i.e. the names that are registered with IANA) to the
- // OpenSSL-style name for the same.
- static final Map<String, String> STANDARD_TO_OPENSSL_CIPHER_SUITES =
- new LinkedHashMap<String, String>();
-
- // SUPPORTED_CIPHER_SUITES_SET contains all the cipher suites supported by
- // OpenSSL, named using "standard" (as opposed to OpenSSL-style) names.
+ // SUPPORTED_CIPHER_SUITES_SET contains all the supported cipher suites, using their Java names.
static final Set<String> SUPPORTED_CIPHER_SUITES_SET = new HashSet<String>();
- private static void add(String openssl, String standard) {
- OPENSSL_TO_STANDARD_CIPHER_SUITES.put(openssl, standard);
- STANDARD_TO_OPENSSL_CIPHER_SUITES.put(standard, openssl);
- }
+ // SUPPORTED_LEGACY_CIPHER_SUITES_SET contains all the supported cipher suites using the legacy
+ // OpenSSL-style names.
+ static final Set<String> SUPPORTED_LEGACY_CIPHER_SUITES_SET = new HashSet<String>();
/**
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV is RFC 5746's renegotiation
@@ -598,6 +647,21 @@
*/
static final String TLS_EMPTY_RENEGOTIATION_INFO_SCSV = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
+ static String cipherSuiteToJava(String cipherSuite) {
+ // For historical reasons, Java uses a different name for TLS_RSA_WITH_3DES_EDE_CBC_SHA.
+ if ("TLS_RSA_WITH_3DES_EDE_CBC_SHA".equals(cipherSuite)) {
+ return "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
+ }
+ return cipherSuite;
+ }
+
+ static String cipherSuiteFromJava(String javaCipherSuite) {
+ if ("SSL_RSA_WITH_3DES_EDE_CBC_SHA".equals(javaCipherSuite)) {
+ return "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
+ }
+ return javaCipherSuite;
+ }
+
/**
* TLS_FALLBACK_SCSV is from
* https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
@@ -606,101 +670,26 @@
*/
static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
- static {
- add("ADH-AES128-GCM-SHA256", "TLS_DH_anon_WITH_AES_128_GCM_SHA256");
- add("ADH-AES128-SHA256", "TLS_DH_anon_WITH_AES_128_CBC_SHA256");
- add("ADH-AES128-SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA");
- add("ADH-AES256-GCM-SHA384", "TLS_DH_anon_WITH_AES_256_GCM_SHA384");
- add("ADH-AES256-SHA256", "TLS_DH_anon_WITH_AES_256_CBC_SHA256");
- add("ADH-AES256-SHA", "TLS_DH_anon_WITH_AES_256_CBC_SHA");
- add("ADH-DES-CBC3-SHA", "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA");
- add("ADH-DES-CBC-SHA", "SSL_DH_anon_WITH_DES_CBC_SHA");
- add("AECDH-AES128-SHA", "TLS_ECDH_anon_WITH_AES_128_CBC_SHA");
- add("AECDH-AES256-SHA", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA");
- add("AECDH-DES-CBC3-SHA", "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA");
- add("AECDH-NULL-SHA", "TLS_ECDH_anon_WITH_NULL_SHA");
- add("AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256");
- add("AES128-SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256");
- add("AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA");
- add("AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384");
- add("AES256-SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256");
- add("AES256-SHA", "TLS_RSA_WITH_AES_256_CBC_SHA");
- add("DES-CBC3-SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
- add("DES-CBC-SHA", "SSL_RSA_WITH_DES_CBC_SHA");
- add("ECDH-ECDSA-AES128-GCM-SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256");
- add("ECDH-ECDSA-AES128-SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256");
- add("ECDH-ECDSA-AES128-SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA");
- add("ECDH-ECDSA-AES256-GCM-SHA384", "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384");
- add("ECDH-ECDSA-AES256-SHA384", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384");
- add("ECDH-ECDSA-AES256-SHA", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA");
- add("ECDH-ECDSA-DES-CBC3-SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA");
- add("ECDH-ECDSA-NULL-SHA", "TLS_ECDH_ECDSA_WITH_NULL_SHA");
- add("ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
- add("ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
- add("ECDHE-ECDSA-AES128-SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
- add("ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
- add("ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
- add("ECDHE-ECDSA-AES256-SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
- add("ECDHE-ECDSA-CHACHA20-POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305");
- add("ECDHE-ECDSA-CHACHA20-POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
- add("ECDHE-ECDSA-DES-CBC3-SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA");
- add("ECDHE-ECDSA-NULL-SHA", "TLS_ECDHE_ECDSA_WITH_NULL_SHA");
- add("ECDHE-PSK-AES128-CBC-SHA", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA");
- add("ECDHE-PSK-AES128-GCM-SHA256", "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256");
- add("ECDHE-PSK-AES256-CBC-SHA", "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA");
- add("ECDHE-PSK-AES256-GCM-SHA384", "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384");
- add("ECDHE-PSK-CHACHA20-POLY1305", "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256");
- add("ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
- add("ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");
- add("ECDHE-RSA-AES128-SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
- add("ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
- add("ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
- add("ECDHE-RSA-AES256-SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
- add("ECDHE-RSA-CHACHA20-POLY1305", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305");
- add("ECDHE-RSA-CHACHA20-POLY1305", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
- add("ECDHE-RSA-DES-CBC3-SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA");
- add("ECDHE-RSA-NULL-SHA", "TLS_ECDHE_RSA_WITH_NULL_SHA");
- add("ECDH-RSA-AES128-GCM-SHA256", "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256");
- add("ECDH-RSA-AES128-SHA256", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256");
- add("ECDH-RSA-AES128-SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA");
- add("ECDH-RSA-AES256-GCM-SHA384", "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384");
- add("ECDH-RSA-AES256-SHA384", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384");
- add("ECDH-RSA-AES256-SHA", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA");
- add("ECDH-RSA-DES-CBC3-SHA", "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA");
- add("ECDH-RSA-NULL-SHA", "TLS_ECDH_RSA_WITH_NULL_SHA");
- add("EXP-ADH-DES-CBC-SHA", "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
- add("EXP-DES-CBC-SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA");
- add("NULL-MD5", "SSL_RSA_WITH_NULL_MD5");
- add("NULL-SHA256", "TLS_RSA_WITH_NULL_SHA256");
- add("NULL-SHA", "SSL_RSA_WITH_NULL_SHA");
- add("PSK-3DES-EDE-CBC-SHA", "TLS_PSK_WITH_3DES_EDE_CBC_SHA");
- add("PSK-AES128-CBC-SHA", "TLS_PSK_WITH_AES_128_CBC_SHA");
- add("PSK-AES256-CBC-SHA", "TLS_PSK_WITH_AES_256_CBC_SHA");
-
- // Signaling Cipher Suite Value for secure renegotiation handled as special case.
- // add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", null);
-
- // Similarly, the fallback SCSV is handled as a special case.
- // add("TLS_FALLBACK_SCSV", null);
- }
-
private static final String[] SUPPORTED_CIPHER_SUITES;
static {
- String[] allOpenSSLCipherSuites = get_cipher_names("ALL:!DHE");
+ String[] allCipherSuites = get_cipher_names("ALL:!DHE");
- int size = allOpenSSLCipherSuites.length;
- SUPPORTED_CIPHER_SUITES = new String[size + 2];
- for (int i = 0; i < size; i++) {
- String standardName = OPENSSL_TO_STANDARD_CIPHER_SUITES.get(allOpenSSLCipherSuites[i]);
- if (standardName == null) {
- throw new IllegalArgumentException("Unknown cipher suite supported by native code: "
- + allOpenSSLCipherSuites[i]);
- }
- SUPPORTED_CIPHER_SUITES[i] = standardName;
- SUPPORTED_CIPHER_SUITES_SET.add(standardName);
+ // get_cipher_names returns an array where even indices are the standard name and odd
+ // indices are the OpenSSL name.
+ int size = allCipherSuites.length;
+ if (size % 2 != 0) {
+ throw new IllegalArgumentException("Invalid cipher list returned by get_cipher_names");
}
- SUPPORTED_CIPHER_SUITES[size] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
- SUPPORTED_CIPHER_SUITES[size + 1] = TLS_FALLBACK_SCSV;
+ SUPPORTED_CIPHER_SUITES = new String[size / 2 + 2];
+ for (int i = 0; i < size; i += 2) {
+ String cipherSuite = cipherSuiteToJava(allCipherSuites[i]);
+ SUPPORTED_CIPHER_SUITES[i / 2] = cipherSuite;
+ SUPPORTED_CIPHER_SUITES_SET.add(cipherSuite);
+
+ SUPPORTED_LEGACY_CIPHER_SUITES_SET.add(allCipherSuites[i + 1]);
+ }
+ SUPPORTED_CIPHER_SUITES[size / 2] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
+ SUPPORTED_CIPHER_SUITES[size / 2 + 1] = TLS_FALLBACK_SCSV;
}
/**
@@ -782,6 +771,8 @@
static native void SSL_CTX_set_session_id_context(long ssl_ctx, byte[] sid_ctx);
+ static native long SSL_CTX_set_timeout(long ssl_ctx, long seconds);
+
static native long SSL_new(long ssl_ctx) throws SSLException;
static native void SSL_enable_tls_channel_id(long ssl) throws SSLException;
@@ -798,14 +789,8 @@
static native void SSL_set_client_CA_list(long ssl, byte[][] asn1DerEncodedX500Principals);
- static native long SSL_get_mode(long ssl);
-
static native long SSL_set_mode(long ssl, long mode);
- static native long SSL_clear_mode(long ssl, long mode);
-
- static native long SSL_get_options(long ssl);
-
static native long SSL_set_options(long ssl, long options);
static native long SSL_clear_options(long ssl, long options);
@@ -830,25 +815,29 @@
/** Protocols to enable by default when "TLSv1.2" is requested. */
static final String[] TLSV12_PROTOCOLS = new String[] {
- SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+ SUPPORTED_PROTOCOL_TLSV1,
+ SUPPORTED_PROTOCOL_TLSV1_1,
+ SUPPORTED_PROTOCOL_TLSV1_2,
};
/** Protocols to enable by default when "TLSv1.1" is requested. */
static final String[] TLSV11_PROTOCOLS = new String[] {
- SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+ SUPPORTED_PROTOCOL_TLSV1,
+ SUPPORTED_PROTOCOL_TLSV1_1,
+ SUPPORTED_PROTOCOL_TLSV1_2,
};
/** Protocols to enable by default when "TLSv1" is requested. */
static final String[] TLSV1_PROTOCOLS = new String[] {
- SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+ SUPPORTED_PROTOCOL_TLSV1,
+ SUPPORTED_PROTOCOL_TLSV1_1,
+ SUPPORTED_PROTOCOL_TLSV1_2,
};
static final String[] DEFAULT_PROTOCOLS = TLSV12_PROTOCOLS;
static String[] getSupportedProtocols() {
- return new String[] {
- SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
- };
+ return TLSV12_PROTOCOLS.clone();
}
static void setEnabledProtocols(long ssl, String[] protocols) {
@@ -922,9 +911,7 @@
SSL_set_mode(ssl, NativeConstants.SSL_MODE_SEND_FALLBACK_SCSV);
continue;
}
- String openssl = STANDARD_TO_OPENSSL_CIPHER_SUITES.get(cipherSuite);
- String cs = (openssl == null) ? cipherSuite : openssl;
- opensslSuites.add(cs);
+ opensslSuites.add(cipherSuiteFromJava(cipherSuite));
}
SSL_set_cipher_lists(ssl, opensslSuites.toArray(new String[opensslSuites.size()]));
}
@@ -935,26 +922,24 @@
}
// makes sure all suites are valid, throwing on error
for (int i = 0; i < cipherSuites.length; i++) {
- String cipherSuite = cipherSuites[i];
- if (cipherSuite == null) {
+ if (cipherSuites[i] == null) {
throw new IllegalArgumentException("cipherSuites[" + i + "] == null");
}
- if (cipherSuite.equals(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
- || cipherSuite.equals(TLS_FALLBACK_SCSV)) {
+ if (cipherSuites[i].equals(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ || cipherSuites[i].equals(TLS_FALLBACK_SCSV)) {
continue;
}
- if (SUPPORTED_CIPHER_SUITES_SET.contains(cipherSuite)) {
+ if (SUPPORTED_CIPHER_SUITES_SET.contains(cipherSuites[i])) {
continue;
}
// For backwards compatibility, it's allowed for |cipherSuite| to
// be an OpenSSL-style cipher-suite name.
- String standardName = OPENSSL_TO_STANDARD_CIPHER_SUITES.get(cipherSuite);
- if (standardName != null && SUPPORTED_CIPHER_SUITES_SET.contains(standardName)) {
+ if (SUPPORTED_LEGACY_CIPHER_SUITES_SET.contains(cipherSuites[i])) {
// TODO log warning about using backward compatability
continue;
}
- throw new IllegalArgumentException("cipherSuite " + cipherSuite + " is not supported.");
+ throw new IllegalArgumentException("cipherSuite " + cipherSuites[i] + " is not supported.");
}
return cipherSuites;
}
@@ -996,11 +981,9 @@
long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc, int timeoutMillis)
throws SSLException, SocketTimeoutException, CertificateException;
- /**
- * Currently only intended for forcing renegotiation for testing.
- * Not used within OpenSSLSocketImpl.
- */
- static native void SSL_renegotiate(long sslNativePointer) throws SSLException;
+ public static native String SSL_get_current_cipher(long sslNativePointer);
+
+ public static native String SSL_get_version(long sslNativePointer);
/**
* Returns the local X509 certificate references. Must X509_free when done.
@@ -1041,11 +1024,21 @@
static native long SSL_SESSION_get_time(long sslSessionNativePointer);
+ static native long SSL_get_time(long sslNativePointer);
+
+ static native long SSL_set_timeout(long sslNativePointer, long millis);
+
+ static native long SSL_get_timeout(long sslNativePointer);
+
+ static native long SSL_SESSION_get_timeout(long sslSessionNativePointer);
+
+ static native byte[] SSL_session_id(long sslNativePointer);
+
static native String SSL_SESSION_get_version(long sslSessionNativePointer);
static native String SSL_SESSION_cipher(long sslSessionNativePointer);
- static native String get_SSL_SESSION_tlsext_hostname(long sslSessionNativePointer);
+ static native void SSL_SESSION_up_ref(long sslSessionNativePointer);
static native void SSL_SESSION_free(long sslSessionNativePointer);
@@ -1116,9 +1109,26 @@
* Called when SSL state changes. This could be handshake completion.
*/
void onSSLStateChange(int type, int val);
- }
- static native long ERR_peek_last_error();
+ /**
+ * Called when a new session has been established and may be added to the session cache.
+ * The callee is responsible for incrementing the reference count on the returned session.
+ */
+ void onNewSessionEstablished(long sslSessionNativePtr);
+
+ /**
+ * Called for servers where TLS < 1.3 (TLS 1.3 uses session tickets rather than
+ * application session caches).
+ *
+ * <p/>Looks up the session by ID in the application's session cache. If a valid session
+ * is returned, this callback is responsible for incrementing the reference count (and any
+ * required synchronization).
+ *
+ * @param id the ID of the session to find.
+ * @return the cached session or {@code 0} if no session was found matching the given ID.
+ */
+ long serverSessionRequested(byte[] id);
+ }
static native String SSL_CIPHER_get_kx_name(long cipherAddress);
@@ -1150,10 +1160,6 @@
static native int SSL_pending_written_bytes_in_BIO(long bio);
- static native long SSL_get0_session(long ssl);
-
- static native long SSL_get1_session(long ssl);
-
/**
* Returns the maximum overhead, in bytes, of sealing a record with SSL.
*/
@@ -1166,35 +1172,62 @@
long sslNativePointer, boolean clientMode, byte[] alpnProtocols) throws IOException;
/**
- * Variant of the {@link #SSL_do_handshake} used by {@link OpenSSLEngineImpl}. This version
- * does not lock and does no error preprocessing.
+ * Variant of the {@link #SSL_do_handshake} used by {@link ConscryptEngine}. This differs
+ * slightly from the raw BoringSSL API in that it returns the SSL error code from the
+ * operation, rather than the return value from {@code SSL_do_handshake}. This is done in
+ * order to allow to properly handle SSL errors and propagate useful exceptions.
+ *
+ * @return Returns the SSL error code for the operation when the error was {@code
+ * SSL_ERROR_NONE}, {@code SSL_ERROR_WANT_READ}, or {@code SSL_ERROR_WANT_WRITE}.
+ * @throws IOException when the error code is anything except those returned by this method.
*/
- static native int ENGINE_SSL_do_handshake(long ssl, SSLHandshakeCallbacks shc);
+ static native int ENGINE_SSL_do_handshake(long ssl, SSLHandshakeCallbacks shc)
+ throws IOException;
/**
* Variant of the {@link #SSL_read} for a direct {@link java.nio.ByteBuffer} used by {@link
- * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
+ * ConscryptEngine}.
+ *
+ * @return if positive, represents the number of bytes read into the given buffer.
+ * Returns {@code -SSL_ERROR_WANT_READ} if more data is needed. Returns
+ * {@code -SSL_ERROR_WANT_WRITE} if data needs to be written out to flush the BIO.
+ *
+ * @throws java.io.InterruptedIOException if the read was interrupted.
+ * @throws java.io.EOFException if the end of stream has been reached.
+ * @throws CertificateException if the application's certificate verification callback failed.
+ * Only occurs during handshake processing.
+ * @throws SSLException if any other error occurs.
*/
static native int ENGINE_SSL_read_direct(long sslNativePointer, long address, int length,
- SSLHandshakeCallbacks shc) throws IOException;
+ SSLHandshakeCallbacks shc) throws IOException, CertificateException;
/**
* Variant of the {@link #SSL_read} for a heap {@link java.nio.ByteBuffer} used by {@link
- * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
+ * ConscryptEngine}.
+ *
+ * @return if positive, represents the number of bytes read into the given buffer.
+ * Returns {@code -SSL_ERROR_WANT_READ} if more data is needed. Returns
+ * {@code -SSL_ERROR_WANT_WRITE} if data needs to be written out to flush the BIO.
+ *
+ * @throws java.io.InterruptedIOException if the read was interrupted.
+ * @throws java.io.EOFException if the end of stream has been reached.
+ * @throws CertificateException if the application's certificate verification callback failed.
+ * Only occurs during handshake processing.
+ * @throws SSLException if any other error occurs.
*/
static native int ENGINE_SSL_read_heap(long sslNativePointer, byte[] destJava, int destOffset,
- int destLength, SSLHandshakeCallbacks shc) throws IOException;
+ int destLength, SSLHandshakeCallbacks shc) throws IOException, CertificateException;
/**
* Variant of the {@link #SSL_write} for a direct {@link java.nio.ByteBuffer} used by {@link
- * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
+ * ConscryptEngine}. This version does not lock or and does no error pre-processing.
*/
static native int ENGINE_SSL_write_direct(long sslNativePointer, long address, int length,
SSLHandshakeCallbacks shc) throws IOException;
/**
* Variant of the {@link #SSL_write} for a heap {@link java.nio.ByteBuffer} used by {@link
- * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
+ * ConscryptEngine}. This version does not lock or and does no error pre-processing.
*/
static native int ENGINE_SSL_write_heap(long sslNativePointer, byte[] sourceJava,
int sourceOffset, int sourceLength, SSLHandshakeCallbacks shc) throws IOException;
@@ -1224,9 +1257,21 @@
int destOffset, int destLength, SSLHandshakeCallbacks shc) throws IOException;
/**
- * Variant of the {@link #SSL_shutdown} used by {@link OpenSSLEngineImpl}. This version does not
+ * Variant of the {@link #SSL_shutdown} used by {@link ConscryptEngine}. This version does not
* lock.
*/
static native void ENGINE_SSL_shutdown(long sslNativePointer, SSLHandshakeCallbacks shc)
throws IOException;
+
+ /**
+ * Used for testing only.
+ */
+ static native int BIO_read(long bioRef, byte[] buffer);
+ static native void BIO_write(long bioRef, byte[] buffer, int offset, int length)
+ throws IOException;
+ static native long ERR_peek_last_error();
+ static native long SSL_clear_mode(long ssl, long mode);
+ static native long SSL_get_mode(long ssl);
+ static native long SSL_get_options(long ssl);
+ static native long SSL_get1_session(long ssl);
}
diff --git a/common/src/main/java/org/conscrypt/NativeRef.java b/common/src/main/java/org/conscrypt/NativeRef.java
index ccc7cc0..62e9bd6 100644
--- a/common/src/main/java/org/conscrypt/NativeRef.java
+++ b/common/src/main/java/org/conscrypt/NativeRef.java
@@ -23,12 +23,12 @@
abstract class NativeRef {
final long context;
- NativeRef(long ctx) {
- if (ctx == 0) {
- throw new NullPointerException("ctx == 0");
+ NativeRef(long context) {
+ if (context == 0) {
+ throw new NullPointerException("context == 0");
}
- this.context = ctx;
+ this.context = context;
}
@Override
@@ -45,108 +45,104 @@
return (int) context;
}
- static class EC_GROUP extends NativeRef {
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (context != 0) {
+ doFree(context);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ abstract void doFree(long context);
+
+ static final class EC_GROUP extends NativeRef {
EC_GROUP(long ctx) {
super(ctx);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.EC_GROUP_clear_free(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.EC_GROUP_clear_free(context);
}
}
- static class EC_POINT extends NativeRef {
- EC_POINT(long ctx) {
- super(ctx);
+ static final class EC_POINT extends NativeRef {
+ EC_POINT(long nativePointer) {
+ super(nativePointer);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.EC_POINT_clear_free(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.EC_POINT_clear_free(context);
}
}
- static class EVP_CIPHER_CTX extends NativeRef {
- EVP_CIPHER_CTX(long ctx) {
- super(ctx);
+ static final class EVP_CIPHER_CTX extends NativeRef {
+ EVP_CIPHER_CTX(long nativePointer) {
+ super(nativePointer);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.EVP_CIPHER_CTX_free(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.EVP_CIPHER_CTX_free(context);
}
}
- static class EVP_MD_CTX extends NativeRef {
- EVP_MD_CTX(long ctx) {
- super(ctx);
+ static final class EVP_MD_CTX extends NativeRef {
+ EVP_MD_CTX(long nativePointer) {
+ super(nativePointer);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.EVP_MD_CTX_destroy(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.EVP_MD_CTX_destroy(context);
}
}
- static class EVP_PKEY extends NativeRef {
- EVP_PKEY(long ctx) {
- super(ctx);
+ static final class EVP_PKEY extends NativeRef {
+ EVP_PKEY(long nativePointer) {
+ super(nativePointer);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.EVP_PKEY_free(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.EVP_PKEY_free(context);
}
}
- static class EVP_PKEY_CTX extends NativeRef {
- EVP_PKEY_CTX(long ctx) {
- super(ctx);
+ static final class EVP_PKEY_CTX extends NativeRef {
+ EVP_PKEY_CTX(long nativePointer) {
+ super(nativePointer);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.EVP_PKEY_CTX_free(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.EVP_PKEY_CTX_free(context);
}
}
- static class HMAC_CTX extends NativeRef {
- HMAC_CTX(long ctx) {
- super(ctx);
+ static final class HMAC_CTX extends NativeRef {
+ HMAC_CTX(long nativePointer) {
+ super(nativePointer);
}
@Override
- protected void finalize() throws Throwable {
- try {
- NativeCrypto.HMAC_CTX_free(context);
- } finally {
- super.finalize();
- }
+ void doFree(long context) {
+ NativeCrypto.HMAC_CTX_free(context);
+ }
+ }
+
+ static final class SSL_SESSION extends NativeRef {
+ SSL_SESSION(long nativePointer) {
+ super(nativePointer);
+ }
+
+ @Override
+ void doFree(long context) {
+ NativeCrypto.SSL_SESSION_free(context);
}
}
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java b/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
index dac03b7..c975c33 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
@@ -23,6 +23,8 @@
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateCrtKey;
@@ -199,6 +201,10 @@
usingPrivateKey = false;
this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
} else {
+ if (null == key) {
+ throw new InvalidKeyException("RSA private or public key is null");
+ }
+
throw new InvalidKeyException("Need RSA private or public key");
}
@@ -481,6 +487,21 @@
}
@Override
+ void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+ if (!(key instanceof PublicKey)) {
+ throw new InvalidKeyException("Only public keys may be used to encrypt");
+ }
+ } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+ if (!(key instanceof PrivateKey)) {
+ throw new InvalidKeyException("Only private keys may be used to decrypt");
+ }
+ }
+ super.engineInitInternal(opmode, key, spec);
+ }
+
+ @Override
void doCryptoInit(AlgorithmParameterSpec spec)
throws InvalidAlgorithmParameterException {
pkeyCtx = new NativeRef.EVP_PKEY_CTX(encrypting
diff --git a/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java b/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java
index 74050e3..143f481 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java
@@ -30,11 +30,12 @@
/**
* OpenSSL-backed SSLContext service provider interface.
*
+ * <p>Public to allow contruction via the provider framework.
+ *
* @hide
*/
@Internal
public abstract class OpenSSLContextImpl extends SSLContextSpi {
-
/**
* The default SSLContextImpl for use with
* SSLContext.getInstance("Default"). Protected by the
@@ -97,8 +98,8 @@
@Override
public void engineInit(KeyManager[] kms, TrustManager[] tms, SecureRandom sr)
throws KeyManagementException {
- sslParameters = new SSLParametersImpl(kms, tms, sr, clientSessionContext,
- serverSessionContext, algorithms);
+ sslParameters = new SSLParametersImpl(
+ kms, tms, sr, clientSessionContext, serverSessionContext, algorithms);
}
@Override
@@ -124,7 +125,7 @@
}
SSLParametersImpl p = (SSLParametersImpl) sslParameters.clone();
p.setUseClientMode(false);
- return new OpenSSLEngineImpl(host, port, p);
+ return new ConscryptEngine(host, port, p);
}
@Override
@@ -134,7 +135,7 @@
}
SSLParametersImpl p = (SSLParametersImpl) sslParameters.clone();
p.setUseClientMode(false);
- return new OpenSSLEngineImpl(p);
+ return new ConscryptEngine(p);
}
@Override
@@ -147,18 +148,27 @@
return clientSessionContext;
}
+ /**
+ * Public to allow construction via the provider framework.
+ */
public static final class TLSv12 extends OpenSSLContextImpl {
public TLSv12() {
super(NativeCrypto.TLSV12_PROTOCOLS);
}
}
+ /**
+ * Public to allow construction via the provider framework.
+ */
public static final class TLSv11 extends OpenSSLContextImpl {
public TLSv11() {
super(NativeCrypto.TLSV11_PROTOCOLS);
}
}
+ /**
+ * Public to allow construction via the provider framework.
+ */
public static final class TLSv1 extends OpenSSLContextImpl {
public TLSv1() {
super(NativeCrypto.TLSV1_PROTOCOLS);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java b/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
index 7ac70dd..71abafa 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
@@ -36,12 +36,10 @@
}
static OpenSSLECGroupContext getCurveByName(String curveName) {
- // Workaround for OpenSSL not supporting SECG names for NIST P-192 and P-256
- // (aka ANSI X9.62 prime192v1 and prime256v1) curve names.
+ // Workaround for OpenSSL not supporting SECG names for NIST P-256 (aka
+ // ANSI X9.62 prime256v1).
if ("secp256r1".equals(curveName)) {
curveName = "prime256v1";
- } else if ("secp192r1".equals(curveName)) {
- curveName = "prime192v1";
}
final long ctx = NativeCrypto.EC_GROUP_new_by_curve_name(curveName);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
index c0e1654..a466812 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
@@ -161,7 +161,7 @@
@Override
public byte[] getEncoded() {
- return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef());
+ return NativeCrypto.EVP_marshal_private_key(key.getNativeRef());
}
@Override
@@ -214,7 +214,7 @@
@Override
public int hashCode() {
- return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef()));
+ return Arrays.hashCode(NativeCrypto.EVP_marshal_private_key(key.getNativeRef()));
}
@Override
@@ -231,7 +231,7 @@
byte[] encoded = (byte[]) stream.readObject();
- key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
+ key = new OpenSSLKey(NativeCrypto.EVP_parse_private_key(encoded));
group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java b/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
index 6e32810..792a282 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
@@ -87,7 +87,7 @@
@Override
public byte[] getEncoded() {
- return NativeCrypto.i2d_PUBKEY(key.getNativeRef());
+ return NativeCrypto.EVP_marshal_public_key(key.getNativeRef());
}
@Override
@@ -143,7 +143,7 @@
@Override
public int hashCode() {
- return Arrays.hashCode(NativeCrypto.i2d_PUBKEY(key.getNativeRef()));
+ return Arrays.hashCode(NativeCrypto.EVP_marshal_public_key(key.getNativeRef()));
}
@Override
@@ -156,7 +156,7 @@
byte[] encoded = (byte[]) stream.readObject();
- key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(encoded));
+ key = new OpenSSLKey(NativeCrypto.EVP_parse_public_key(encoded));
group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java
deleted file mode 100644
index 7e711d8..0000000
--- a/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
- * Copyright 2016 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 javax.net.ssl.SSLEngineResult.Status.OK;
-
-import java.io.EOFException;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.security.PrivateKey;
-import java.security.cert.CertificateException;
-import javax.crypto.SecretKey;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.X509KeyManager;
-import javax.security.auth.x500.X500Principal;
-
-/**
- * Implements crypto handling by delegating to OpenSSLEngine. Used for socket implementations
- * that are not backed by a real OS socket.
- */
-final class OpenSSLEngineSocketImpl extends OpenSSLSocketImplWrapper {
- private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
-
- private final OpenSSLEngineImpl engine;
- private final Socket socket;
- private final OutputStreamWrapper outputStreamWrapper;
- private final InputStreamWrapper inputStreamWrapper;
- private boolean handshakeComplete;
-
- OpenSSLEngineSocketImpl(SSLParametersImpl sslParameters) throws IOException {
- this(new Socket(), null, -1, false, sslParameters);
- }
-
- OpenSSLEngineSocketImpl(String host, int port, SSLParametersImpl sslParameters)
- throws IOException {
- this(new Socket(host, port), host, port, false, sslParameters);
- }
-
- OpenSSLEngineSocketImpl(String host, int port, InetAddress clientAddress, int clientPort,
- SSLParametersImpl sslParameters) throws IOException {
- this(new Socket(host, port, clientAddress, clientPort), host, port, false, sslParameters);
- }
-
- OpenSSLEngineSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
- throws IOException {
- this(new Socket(address, port), null, port, false, sslParameters);
- }
-
- OpenSSLEngineSocketImpl(InetAddress address, int port, InetAddress clientAddress,
- int clientPort, SSLParametersImpl sslParameters) throws IOException {
- this(new Socket(address, port, clientAddress, clientPort), null, port, false,
- sslParameters);
- }
-
- OpenSSLEngineSocketImpl(Socket socket, String hostname, int port, boolean autoClose,
- SSLParametersImpl sslParameters) throws IOException {
- super(socket, hostname, port, autoClose, sslParameters);
- this.socket = socket;
- engine = new OpenSSLEngineImpl(hostname, port, sslParameters);
-
- // When the handshake completes, notify any listeners.
- engine.setHandshakeListener(new HandshakeListener() {
- @Override
- public void onHandshakeFinished() {
- if (!handshakeComplete) {
- handshakeComplete = true;
- OpenSSLEngineSocketImpl.this.notifyHandshakeCompletedListeners();
- }
- }
- });
- outputStreamWrapper = new OutputStreamWrapper();
- inputStreamWrapper = new InputStreamWrapper();
- engine.setUseClientMode(sslParameters.getUseClientMode());
- }
-
- @Override
- public void startHandshake() throws IOException {
- try {
- // Trigger the handshake
- boolean beginHandshakeCalled = false;
- while (!handshakeComplete) {
- switch (engine.getHandshakeStatus()) {
- case NOT_HANDSHAKING: {
- if (!beginHandshakeCalled) {
- beginHandshakeCalled = true;
- engine.beginHandshake();
- break;
- }
- break;
- }
- case FINISHED: {
- return;
- }
- case NEED_WRAP: {
- outputStreamWrapper.write(EMPTY_BUFFER);
- break;
- }
- case NEED_UNWRAP: {
- if (inputStreamWrapper.read(EmptyArray.BYTE) == -1) {
- // Can't complete the handshake due to EOF.
- throw SSLUtils.toSSLHandshakeException(new EOFException());
- }
- break;
- }
- case NEED_TASK: {
- throw new IllegalStateException("OpenSSLEngineImpl returned NEED_TASK");
- }
- default: {
- break;
- }
- }
- }
- } catch (Exception e) {
- close();
- throw SSLUtils.toSSLHandshakeException(e);
- }
- }
-
- @Override
- public void onSSLStateChange(int type, int val) {
- throw new AssertionError("Should be handled by engine");
- }
-
- @Override
- public void verifyCertificateChain(long[] certRefs, String authMethod)
- throws CertificateException {
- throw new AssertionError("Should be handled by engine");
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- return inputStreamWrapper;
- }
-
- @Override
- public OutputStream getOutputStream() throws IOException {
- return outputStreamWrapper;
- }
-
- @Override
- public SSLSession getSession() {
- return engine.getSession();
- }
-
- @Override
- public boolean getEnableSessionCreation() {
- return super.getEnableSessionCreation();
- }
-
- @Override
- public void setEnableSessionCreation(boolean flag) {
- super.setEnableSessionCreation(flag);
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return super.getSupportedCipherSuites();
- }
-
- @Override
- public String[] getEnabledCipherSuites() {
- return super.getEnabledCipherSuites();
- }
-
- @Override
- public void setEnabledCipherSuites(String[] suites) {
- super.setEnabledCipherSuites(suites);
- }
-
- @Override
- public String[] getSupportedProtocols() {
- return super.getSupportedProtocols();
- }
-
- @Override
- public String[] getEnabledProtocols() {
- return super.getEnabledProtocols();
- }
-
- @Override
- public void setEnabledProtocols(String[] protocols) {
- super.setEnabledProtocols(protocols);
- }
-
- @Override
- public void setUseSessionTickets(boolean useSessionTickets) {
- super.setUseSessionTickets(useSessionTickets);
- }
-
- @Override
- public void setHostname(String hostname) {
- super.setHostname(hostname);
- }
-
- @Override
- public void setChannelIdEnabled(boolean enabled) {
- super.setChannelIdEnabled(enabled);
- }
-
- @Override
- public byte[] getChannelId() throws SSLException {
- return super.getChannelId();
- }
-
- @Override
- public void setChannelIdPrivateKey(PrivateKey privateKey) {
- super.setChannelIdPrivateKey(privateKey);
- }
-
- @Override
- public boolean getUseClientMode() {
- return super.getUseClientMode();
- }
-
- @Override
- public void setUseClientMode(boolean mode) {
- engine.setUseClientMode(mode);
- }
-
- @Override
- public boolean getWantClientAuth() {
- return super.getWantClientAuth();
- }
-
- @Override
- public boolean getNeedClientAuth() {
- return super.getNeedClientAuth();
- }
-
- @Override
- public void setNeedClientAuth(boolean need) {
- super.setNeedClientAuth(need);
- }
-
- @Override
- public void setWantClientAuth(boolean want) {
- super.setWantClientAuth(want);
- }
-
- @Override
- public void sendUrgentData(int data) throws IOException {
- super.sendUrgentData(data);
- }
-
- @Override
- public void setOOBInline(boolean on) throws SocketException {
- super.setOOBInline(on);
- }
-
- @Override
- public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
- throw new UnsupportedOperationException("Not supported");
- }
-
- @Override
- public int getSoWriteTimeout() throws SocketException {
- return 0;
- }
-
- @Override
- public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
- throw new UnsupportedOperationException("Not supported");
- }
-
- @Override
- public synchronized void close() throws IOException {
- // Closing Socket.
- engine.closeInbound();
- engine.closeOutbound();
- socket.close();
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- }
-
- @Override
- public SocketChannel getChannel() {
- return super.getChannel();
- }
-
- @Override
- public FileDescriptor getFileDescriptor$() {
- throw new UnsupportedOperationException("Not supported");
- }
-
- @Override
- public byte[] getNpnSelectedProtocol() {
- return null;
- }
-
- @Override
- public byte[] getAlpnSelectedProtocol() {
- return engine.getAlpnSelectedProtocol();
- }
-
- @Override
- public void setNpnProtocols(byte[] npnProtocols) {
- super.setNpnProtocols(npnProtocols);
- }
-
- @Override
- public void setAlpnProtocols(byte[] alpnProtocols) {
- super.setAlpnProtocols(alpnProtocols);
- }
-
- @Override
- public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
- return engine.chooseServerAlias(keyManager, keyType);
- }
-
- @Override
- public String chooseClientAlias(
- X509KeyManager keyManager, X500Principal[] issuers, String[] keyTypes) {
- return engine.chooseClientAlias(keyManager, issuers, keyTypes);
- }
-
- @Override
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
- return engine.chooseServerPSKIdentityHint(keyManager);
- }
-
- @Override
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
- return engine.chooseClientPSKIdentity(keyManager, identityHint);
- }
-
- @Override
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
- return engine.getPSKKey(keyManager, identityHint, identity);
- }
-
- /**
- * Wrap bytes written to the underlying socket.
- */
- private final class OutputStreamWrapper extends OutputStream {
- private final Object stateLock = new Object();
- private ByteBuffer target;
- private OutputStream socketOutputStream;
- private SocketChannel socketChannel;
-
- OutputStreamWrapper() {}
-
- @Override
- public void write(int b) throws IOException {
- write(new byte[] {(byte) b});
- }
-
- @Override
- public void write(byte[] b) throws IOException {
- write(ByteBuffer.wrap(b));
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- write(ByteBuffer.wrap(b, off, len));
- }
-
- private void write(ByteBuffer buffer) throws IOException {
- synchronized (stateLock) {
- try {
- init();
-
- // Need to loop through at least once to enable handshaking where no application
- // bytes are
- // processed.
- int len = buffer.remaining();
- SSLEngineResult engineResult;
- do {
- target.clear();
- engineResult = engine.wrap(buffer, target);
- if (engineResult.getStatus() != OK) {
- throw new SSLException(
- "Unexpected engine result " + engineResult.getStatus());
- }
- if (target.position() != engineResult.bytesProduced()) {
- throw new SSLException("Engine bytesProduced "
- + engineResult.bytesProduced()
- + " does not match bytes written " + target.position());
- }
- len -= engineResult.bytesConsumed();
- if (len != buffer.remaining()) {
- throw new SSLException(
- "Engine did not read the correct number of bytes");
- }
-
- target.flip();
-
- // Write the data to the socket.
- if (socketChannel != null) {
- // Loop until all of the data is written to the channel. Typically,
- // SocketChannel writes will return only after all bytes are written,
- // so we won't really loop here.
- while (target.hasRemaining()) {
- socketChannel.write(target);
- }
- } else {
- // Target is a heap buffer.
- socketOutputStream.write(target.array(), 0, target.limit());
- }
- } while (len > 0);
- } catch (IOException e) {
- throw e;
- } catch (RuntimeException e) {
- throw e;
- }
- }
- }
-
- @Override
- public void flush() throws IOException {
- synchronized (stateLock) {
- init();
- socketOutputStream.flush();
- }
- }
-
- @Override
- public void close() throws IOException {
- socket.close();
- }
-
- private void init() throws IOException {
- if (socketOutputStream == null) {
- socketOutputStream = socket.getOutputStream();
- socketChannel = socket.getChannel();
- if (socketChannel != null) {
- // Optimization. Using direct buffers wherever possible to avoid passing
- // arrays to JNI.
- target = ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize());
- } else {
- target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
- }
- }
- }
- }
-
- /**
- * Unwrap bytes read from the underlying socket.
- */
- private final class InputStreamWrapper extends InputStream {
- private final Object stateLock = new Object();
- private final byte[] singleByte = new byte[1];
- private final ByteBuffer fromEngine;
- private ByteBuffer fromSocket;
- private InputStream socketInputStream;
- private SocketChannel socketChannel;
-
- InputStreamWrapper() {
- fromEngine = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize());
- // Initially fromEngine.remaining() == 0.
- fromEngine.flip();
- }
-
- @Override
- public int read() throws IOException {
- synchronized (stateLock) {
- // Handle returning of -1 if EOF is reached.
- int count = read(singleByte, 0, 1);
- if (count == -1) {
- // Handle EOF.
- return -1;
- }
- if (count != 1) {
- throw new SSLException("read incorrect number of bytes " + count);
- }
- return (int) singleByte[0];
- }
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- synchronized (stateLock) {
- try {
- // Make sure the input stream has been created.
- init();
-
- for (;;) {
- // Serve any remaining data from the engine first.
- if (fromEngine.remaining() > 0) {
- int readFromEngine = Math.min(fromEngine.remaining(), len);
- fromEngine.get(b, off, readFromEngine);
- return readFromEngine;
- }
-
- // Try to unwrap any data already in the socket buffer.
- boolean needMoreData = true;
- if (fromSocket.position() > 0) {
- // Unwrap the unencrypted bytes into the engine buffer.
- fromSocket.flip();
- fromEngine.clear();
- SSLEngineResult engineResult = engine.unwrap(fromSocket, fromEngine);
-
- // Shift any remaining data to the beginning of the buffer so that
- // we can accommodate the next full packet. After this is called,
- // limit will be restored to capacity and position will point just
- // past the end of the data.
- fromSocket.compact();
- fromEngine.flip();
-
- switch (engineResult.getStatus()) {
- case BUFFER_UNDERFLOW: {
- if (engineResult.bytesProduced() == 0) {
- // Need to read more data from the socket.
- break;
- }
- // Also serve the data that was produced.
- needMoreData = false;
- break;
- }
- case OK: {
- // We processed the entire packet successfully.
- needMoreData = false;
- break;
- }
- case CLOSED: {
- // EOF
- return -1;
- }
- default: {
- // Anything else is an error.
- throw new SSLException(
- "Unexpected engine result " + engineResult.getStatus());
- }
- }
-
- if (!needMoreData && engineResult.bytesProduced() == 0) {
- // Read successfully, but produced no data. Possibly part of a
- // handshake.
- return 0;
- }
- }
-
- // Read more data from the socket.
- if (needMoreData && readFromSocket() == -1) {
- // Failed to read the next encrypted packet before reaching EOF.
- return -1;
- }
-
- // Continue the loop and return the data from the engine buffer.
- }
- } catch (IOException e) {
- throw e;
- } catch (RuntimeException e) {
- throw e;
- }
- }
- }
-
- private void init() throws IOException {
- if (socketInputStream == null) {
- socketInputStream = socket.getInputStream();
- socketChannel = socket.getChannel();
- if (socketChannel != null) {
- fromSocket =
- ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize());
- } else {
- fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
- }
- }
- }
-
- private int readFromSocket() throws IOException {
- if (socketChannel != null) {
- return socketChannel.read(fromSocket);
- }
- // Read directly to the underlying array and increment the buffer position if
- // appropriate.
- int read = socketInputStream.read(
- fromSocket.array(), fromSocket.position(), fromSocket.remaining());
- if (read > 0) {
- fromSocket.position(fromSocket.position() + read);
- }
- return read;
- }
- }
-}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLKey.java b/common/src/main/java/org/conscrypt/OpenSSLKey.java
index 1ca0b2c..54af772 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLKey.java
@@ -73,7 +73,7 @@
throw new InvalidKeyException("Key encoding is null");
}
- return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(key.getEncoded()));
+ return new OpenSSLKey(NativeCrypto.EVP_parse_private_key(key.getEncoded()));
}
/**
@@ -177,7 +177,7 @@
if (encoded == null) {
return null;
}
- return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
+ return new OpenSSLKey(NativeCrypto.EVP_parse_private_key(encoded));
}
/**
@@ -222,7 +222,7 @@
}
try {
- return new OpenSSLKey(NativeCrypto.d2i_PUBKEY(key.getEncoded()));
+ return new OpenSSLKey(NativeCrypto.EVP_parse_public_key(key.getEncoded()));
} catch (Exception e) {
throw new InvalidKeyException(e);
}
@@ -267,7 +267,7 @@
final OpenSSLKey key;
try {
- key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
+ key = new OpenSSLKey(NativeCrypto.EVP_parse_public_key(x509KeySpec.getEncoded()));
} catch (Exception e) {
throw new InvalidKeySpecException(e);
}
@@ -300,7 +300,7 @@
final OpenSSLKey key;
try {
- key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
+ key = new OpenSSLKey(NativeCrypto.EVP_parse_private_key(pkcs8KeySpec.getEncoded()));
} catch (Exception e) {
throw new InvalidKeySpecException(e);
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
index 1c23df1..1b83c8d 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -72,6 +72,12 @@
put("SSLContext.TLSv1.2", tls12SSLContext);
put("SSLContext.Default", PREFIX + "DefaultSSLContextImpl");
+ /* === AlgorithmParameters === */
+ put("AlgorithmParameters.GCM", PREFIX + "GCMParameters");
+ put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.6", "GCM");
+ put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.26", "GCM");
+ put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.46", "GCM");
+
/* === Message Digests === */
put("MessageDigest.SHA-1", PREFIX + "OpenSSLMessageDigestJDK$SHA1");
put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
@@ -382,17 +388,15 @@
put("Alg.Alias.Cipher.1.2.840.113549.3.4", "ARC4");
put("Alg.Alias.Cipher.OID.1.2.840.113549.3.4", "ARC4");
- if (NativeConstants.HAS_EVP_AEAD) {
- putSymmetricCipherImplClass("AES/GCM/NoPadding", "OpenSSLCipher$EVP_AEAD$AES$GCM");
- put("Alg.Alias.Cipher.GCM", "AES/GCM/NoPadding");
- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES/GCM/NoPadding");
- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES/GCM/NoPadding");
- put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES/GCM/NoPadding");
- putSymmetricCipherImplClass(
- "AES_128/GCM/NoPadding", "OpenSSLCipher$EVP_AEAD$AES$GCM$AES_128");
- putSymmetricCipherImplClass(
- "AES_256/GCM/NoPadding", "OpenSSLCipher$EVP_AEAD$AES$GCM$AES_256");
- }
+ putSymmetricCipherImplClass("AES/GCM/NoPadding", "OpenSSLCipher$EVP_AEAD$AES$GCM");
+ put("Alg.Alias.Cipher.GCM", "AES/GCM/NoPadding");
+ put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES/GCM/NoPadding");
+ put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES/GCM/NoPadding");
+ put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES/GCM/NoPadding");
+ putSymmetricCipherImplClass(
+ "AES_128/GCM/NoPadding", "OpenSSLCipher$EVP_AEAD$AES$GCM$AES_128");
+ putSymmetricCipherImplClass(
+ "AES_256/GCM/NoPadding", "OpenSSLCipher$EVP_AEAD$AES$GCM$AES_256");
/* === Mac === */
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
index 259e38e..8c2e4df 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
@@ -198,7 +198,7 @@
@Override
public final byte[] getEncoded() {
- return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef());
+ return NativeCrypto.EVP_marshal_private_key(key.getNativeRef());
}
@Override
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAPublicKey.java b/common/src/main/java/org/conscrypt/OpenSSLRSAPublicKey.java
index 40d878d..ffc672f 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAPublicKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAPublicKey.java
@@ -96,7 +96,7 @@
@Override
public byte[] getEncoded() {
- return NativeCrypto.i2d_PUBKEY(key.getNativeRef());
+ return NativeCrypto.EVP_marshal_public_key(key.getNativeRef());
}
private void ensureReadParams() {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java b/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
index 008e92f..c2507d8 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
@@ -24,6 +24,9 @@
/**
* An implementation of {@link SSLServerSocketFactory} using BoringSSL.
+ *
+ * <p/>This name of this class cannot change in order to maintain backward-compatibility with GMS
+ * core {@code ProviderInstallerImpl}
*/
final class OpenSSLServerSocketFactoryImpl extends SSLServerSocketFactory {
private static boolean useEngineSocketByDefault = SSLUtils.USE_ENGINE_SOCKET_BY_DEFAULT;
@@ -37,8 +40,7 @@
this.sslParameters = SSLParametersImpl.getDefault();
this.sslParameters.setUseClientMode(false);
} catch (KeyManagementException e) {
- instantiationException =
- new IOException("Delayed instantiation exception:");
+ instantiationException = new IOException("Delayed instantiation exception:");
instantiationException.initCause(e);
}
}
@@ -75,26 +77,26 @@
@Override
public ServerSocket createServerSocket() throws IOException {
- return new OpenSSLServerSocketImpl((SSLParametersImpl) sslParameters.clone())
+ return new ConscryptServerSocket((SSLParametersImpl) sslParameters.clone())
.setUseEngineSocket(useEngineSocket);
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
- return new OpenSSLServerSocketImpl(port, (SSLParametersImpl) sslParameters.clone())
+ return new ConscryptServerSocket(port, (SSLParametersImpl) sslParameters.clone())
.setUseEngineSocket(useEngineSocket);
}
@Override
public ServerSocket createServerSocket(int port, int backlog) throws IOException {
- return new OpenSSLServerSocketImpl(port, backlog, (SSLParametersImpl) sslParameters.clone())
+ return new ConscryptServerSocket(port, backlog, (SSLParametersImpl) sslParameters.clone())
.setUseEngineSocket(useEngineSocket);
}
@Override
public ServerSocket createServerSocket(int port, int backlog, InetAddress iAddress)
throws IOException {
- return new OpenSSLServerSocketImpl(
+ return new ConscryptServerSocket(
port, backlog, iAddress, (SSLParametersImpl) sslParameters.clone())
.setUseEngineSocket(useEngineSocket);
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java
deleted file mode 100644
index d94252b..0000000
--- a/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2007 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.io.IOException;
-import java.security.cert.X509Certificate;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSessionBindingEvent;
-import javax.net.ssl.SSLSessionBindingListener;
-
-/**
- * Implementation of the class OpenSSLSessionImpl
- * based on BoringSSL.
- */
-class OpenSSLSessionImpl extends AbstractOpenSSLSession {
- private long creationTime = 0;
- long lastAccessedTime = 0;
- final X509Certificate[] localCertificates;
- final X509Certificate[] peerCertificates;
-
- private final Map<String, Object> values = new HashMap<String, Object>();
- private byte[] peerCertificateOcspData;
- private byte[] peerTlsSctData;
- long sslSessionNativePointer;
- private String peerHost;
- private int peerPort = -1;
- private String cipherSuite;
- private String protocol;
- private byte[] id;
-
- /**
- * Class constructor creates an SSL session context given the appropriate
- * SSL parameters.
- */
- OpenSSLSessionImpl(long sslSessionNativePointer, X509Certificate[] localCertificates,
- X509Certificate[] peerCertificates, byte[] peerCertificateOcspData,
- byte[] peerTlsSctData, String peerHost, int peerPort,
- AbstractSessionContext sessionContext) {
- super(sessionContext);
- this.sslSessionNativePointer = sslSessionNativePointer;
- this.localCertificates = localCertificates;
- this.peerCertificates = peerCertificates;
- this.peerCertificateOcspData = peerCertificateOcspData;
- this.peerTlsSctData = peerTlsSctData;
- this.peerHost = peerHost;
- this.peerPort = peerPort;
- }
-
- /**
- * Constructs a session from a byte[] containing an SSL session serialized with DER encoding.
- * This allows loading of a previously saved OpenSSLSessionImpl.
- *
- * @throws IOException if the serialized session data can not be parsed
- */
- OpenSSLSessionImpl(byte[] derData, String peerHost, int peerPort,
- X509Certificate[] peerCertificates, byte[] peerCertificateOcspData,
- byte[] peerTlsSctData, AbstractSessionContext sessionContext)
- throws IOException {
- this(NativeCrypto.d2i_SSL_SESSION(derData), null, peerCertificates,
- peerCertificateOcspData, peerTlsSctData, peerHost, peerPort, sessionContext);
- }
-
- /**
- * Gets the identifier of the actual SSL session
- * @return array of sessions' identifiers.
- */
- @Override
- public byte[] getId() {
- if (id == null) {
- resetId();
- }
- return id;
- }
-
- /**
- * Reset the id field to the current value found in the native
- * SSL_SESSION. It can change during the lifetime of the session
- * because while a session is created during initial handshake,
- * with handshake_cutthrough, the SSL_do_handshake may return
- * before we have read the session ticket from the server side and
- * therefore have computed no id based on the SHA of the ticket.
- */
- @Override
- void resetId() {
- id = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
- }
-
- /**
- * Get the session object in DER format. This allows saving the session
- * data or sharing it with other processes.
- */
- public byte[] getEncoded() {
- return NativeCrypto.i2d_SSL_SESSION(sslSessionNativePointer);
- }
-
- /**
- * Gets the creation time of the SSL session.
- * @return the session's creation time in milliseconds since the epoch
- */
- @Override
- public long getCreationTime() {
- if (creationTime == 0) {
- creationTime = NativeCrypto.SSL_SESSION_get_time(sslSessionNativePointer);
- }
- return creationTime;
- }
-
- /**
- * Returns the last time this concrete SSL session was accessed. Accessing
- * here is to mean that a new connection with the same SSL context data was
- * established.
- *
- * @return the session's last access time in milliseconds since the epoch
- */
- @Override
- public long getLastAccessedTime() {
- return (lastAccessedTime == 0) ? getCreationTime() : lastAccessedTime;
- }
-
- @Override
- public void setLastAccessedTime(long accessTimeMillis) {
- lastAccessedTime = accessTimeMillis;
- }
-
- @Override
- protected X509Certificate[] getX509LocalCertificates() {
- return localCertificates;
- }
-
- @Override
- protected X509Certificate[] getX509PeerCertificates() throws SSLPeerUnverifiedException {
- if (peerCertificates == null || peerCertificates.length == 0) {
- throw new SSLPeerUnverifiedException("No peer certificates");
- }
- return peerCertificates;
- }
-
- /**
- * The peer's host name used in this SSL session is returned. It is the host
- * name of the client for the server; and that of the server for the client.
- * It is not a reliable way to get a fully qualified host name: it is mainly
- * used internally to implement links for a temporary cache of SSL sessions.
- *
- * @return the host name of the peer, or {@code null} if no information is
- * available.
- */
- @Override
- public String getPeerHost() {
- return peerHost;
- }
-
- /**
- * Returns the peer's port number for the actual SSL session. It is the port
- * number of the client for the server; and that of the server for the
- * client. It is not a reliable way to get a peer's port number: it is
- * mainly used internally to implement links for a temporary cache of SSL
- * sessions.
- *
- * @return the peer's port number, or {@code -1} if no one is available.
- */
- @Override
- public int getPeerPort() {
- return peerPort;
- }
-
- /**
- * Returns a string identifier of the crypto tools used in the actual SSL
- * session. For example AES_256_WITH_MD5.
- */
- @Override
- public String getCipherSuite() {
- if (cipherSuite == null) {
- String name = NativeCrypto.SSL_SESSION_cipher(sslSessionNativePointer);
- cipherSuite = NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.get(name);
- if (cipherSuite == null) {
- cipherSuite = name;
- }
- }
- return cipherSuite;
- }
-
- /**
- * Returns the standard version name of the SSL protocol used in all
- * connections pertaining to this SSL session.
- */
- @Override
- public String getProtocol() {
- if (protocol == null) {
- protocol = NativeCrypto.SSL_SESSION_get_version(sslSessionNativePointer);
- }
- return protocol;
- }
-
- /**
- * Returns the object which is bound to the the input parameter name.
- * This name is a sort of link to the data of the SSL session's application
- * layer, if any exists.
- *
- * @param name the name of the binding to find.
- * @return the value bound to that name, or null if the binding does not
- * exist.
- * @throws IllegalArgumentException if the argument is null.
- */
- @Override
- public Object getValue(String name) {
- if (name == null) {
- throw new IllegalArgumentException("name == null");
- }
- return values.get(name);
- }
-
- /**
- * Returns an array with the names (sort of links) of all the data
- * objects of the application layer bound into the SSL session.
- *
- * @return a non-null (possibly empty) array of names of the data objects
- * bound to this SSL session.
- */
- @Override
- public String[] getValueNames() {
- return values.keySet().toArray(new String[values.size()]);
- }
-
- /**
- * A link (name) with the specified value object of the SSL session's
- * application layer data is created or replaced. If the new (or existing)
- * value object implements the <code>SSLSessionBindingListener</code>
- * interface, that object will be notified in due course.
- *
- * @param name the name of the link (no null are
- * accepted!)
- * @param value data object that shall be bound to
- * name.
- * @throws IllegalArgumentException if one or both argument(s) is null.
- */
- @Override
- public void putValue(String name, Object value) {
- if (name == null || value == null) {
- throw new IllegalArgumentException("name == null || value == null");
- }
- Object old = values.put(name, value);
- if (value instanceof SSLSessionBindingListener) {
- ((SSLSessionBindingListener) value)
- .valueBound(new SSLSessionBindingEvent(this, name));
- }
- if (old instanceof SSLSessionBindingListener) {
- ((SSLSessionBindingListener) old)
- .valueUnbound(new SSLSessionBindingEvent(this, name));
- }
- }
-
- /**
- * Removes a link (name) with the specified value object of the SSL
- * session's application layer data.
- *
- * <p>If the value object implements the <code>SSLSessionBindingListener</code>
- * interface, the object will receive a <code>valueUnbound</code> notification.
- *
- * @param name the name of the link (no null are
- * accepted!)
- * @throws IllegalArgumentException if the argument is null.
- */
- @Override
- public void removeValue(String name) {
- if (name == null) {
- throw new IllegalArgumentException("name == null");
- }
- Object old = values.remove(name);
- if (old instanceof SSLSessionBindingListener) {
- SSLSessionBindingListener listener = (SSLSessionBindingListener) old;
- listener.valueUnbound(new SSLSessionBindingEvent(this, name));
- }
- }
-
- /**
- * Returns the name requested by the SNI extension.
- */
- @Override
- public String getRequestedServerName() {
- return NativeCrypto.get_SSL_SESSION_tlsext_hostname(sslSessionNativePointer);
- }
-
- /**
- * Returns the OCSP stapled response.
- */
- @Override
- public List<byte[]> getStatusResponses() {
- if (peerCertificateOcspData == null) {
- return Collections.<byte[]>emptyList();
- }
-
- return Collections.singletonList(peerCertificateOcspData.clone());
- }
-
- @Override
- public byte[] getTlsSctData() {
- if (peerTlsSctData == null) {
- return null;
- }
- return peerTlsSctData.clone();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- // The constructor can throw an exception if this object is constructed from invalid
- // saved session data.
- if (sslSessionNativePointer != 0) {
- NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
- }
- } finally {
- super.finalize();
- }
- }
-}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java
index 86d18c0..df35818 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawECDSA.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 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 java.io.ByteArrayOutputStream;
@@ -7,9 +22,6 @@
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
/**
* Implements the JDK Signature interface needed for RAW ECDSA signature
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
index 1794d9b..3236caf 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
@@ -19,12 +19,16 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import javax.net.ssl.SSLSocketFactory;
/**
* An implementation of {@link SSLSocketFactory} based on BoringSSL.
+ *
+ * <p/>This name of this class cannot change in order to maintain backward-compatibility with GMS
+ * core {@code ProviderInstallerImpl}
*/
final class OpenSSLSocketFactoryImpl extends SSLSocketFactory {
private static boolean useEngineSocketByDefault = SSLUtils.USE_ENGINE_SOCKET_BY_DEFAULT;
@@ -81,18 +85,20 @@
throw instantiationException;
}
if (useEngineSocket) {
- return new OpenSSLEngineSocketImpl((SSLParametersImpl) sslParameters.clone());
+ return new ConscryptEngineSocket((SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLSocketImpl((SSLParametersImpl) sslParameters.clone());
+ return new ConscryptFileDescriptorSocket((SSLParametersImpl) sslParameters.clone());
}
}
@Override
public Socket createSocket(String hostname, int port) throws IOException, UnknownHostException {
if (useEngineSocket) {
- return new OpenSSLEngineSocketImpl(hostname, port, (SSLParametersImpl) sslParameters.clone());
+ return new ConscryptEngineSocket(
+ hostname, port, (SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLSocketImpl(hostname, port, (SSLParametersImpl) sslParameters.clone());
+ return new ConscryptFileDescriptorSocket(
+ hostname, port, (SSLParametersImpl) sslParameters.clone());
}
}
@@ -100,16 +106,10 @@
public Socket createSocket(String hostname, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
if (useEngineSocket) {
- return new OpenSSLEngineSocketImpl(hostname,
- port,
- localHost,
- localPort,
+ return new ConscryptEngineSocket(hostname, port, localHost, localPort,
(SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLSocketImpl(hostname,
- port,
- localHost,
- localPort,
+ return new ConscryptFileDescriptorSocket(hostname, port, localHost, localPort,
(SSLParametersImpl) sslParameters.clone());
}
}
@@ -117,48 +117,46 @@
@Override
public Socket createSocket(InetAddress address, int port) throws IOException {
if (useEngineSocket) {
- return new OpenSSLEngineSocketImpl(address, port, (SSLParametersImpl) sslParameters.clone());
+ return new ConscryptEngineSocket(
+ address, port, (SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLSocketImpl(address, port, (SSLParametersImpl) sslParameters.clone());
+ return new ConscryptFileDescriptorSocket(
+ address, port, (SSLParametersImpl) sslParameters.clone());
}
}
@Override
- public Socket createSocket(InetAddress address,
- int port,
- InetAddress localAddress,
- int localPort)
- throws IOException {
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+ int localPort) throws IOException {
if (useEngineSocket) {
- return new OpenSSLEngineSocketImpl(address,
- port,
- localAddress,
- localPort,
+ return new ConscryptEngineSocket(address, port, localAddress, localPort,
(SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLSocketImpl(address,
- port,
- localAddress,
- localPort,
+ return new ConscryptFileDescriptorSocket(address, port, localAddress, localPort,
(SSLParametersImpl) sslParameters.clone());
}
}
@Override
- public Socket createSocket(Socket s, String hostname, int port, boolean autoClose)
+ public Socket createSocket(Socket socket, String hostname, int port, boolean autoClose)
throws IOException {
- if (hasFileDescriptor(s) && !useEngineSocket) {
- return new OpenSSLSocketImplWrapper(
- s, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
+ Preconditions.checkNotNull(socket, "socket");
+ if (!socket.isConnected()) {
+ throw new SocketException("Socket is not connected.");
+ }
+
+ if (hasFileDescriptor(socket) && !useEngineSocket) {
+ return new ConscryptFileDescriptorSocket(
+ socket, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLEngineSocketImpl(
- s, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
+ return new ConscryptEngineSocket(
+ socket, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
}
}
private boolean hasFileDescriptor(Socket s) {
try {
- // If socket has a file descriptor we can use OpenSSLSocketImplWrapper directly
+ // If socket has a file descriptor we can use it directly
// otherwise we need to use the engine.
Platform.getFileDescriptor(s);
return true;
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
index b1dd921..b33f9e2 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
@@ -18,1325 +18,114 @@
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.Socket;
-import java.net.SocketAddress;
import java.net.SocketException;
-import java.security.InvalidKeyException;
import java.security.PrivateKey;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.interfaces.ECKey;
-import java.security.spec.ECParameterSpec;
-import java.util.ArrayList;
-import javax.crypto.SecretKey;
-import javax.net.ssl.HandshakeCompletedEvent;
-import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSession;
-import javax.net.ssl.X509KeyManager;
-import javax.net.ssl.X509TrustManager;
-import javax.security.auth.x500.X500Principal;
/**
- * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
- * <p>
- * Extensions to SSLSocket include:
- * <ul>
- * <li>handshake timeout
- * <li>session tickets
- * <li>Server Name Indication
- * </ul>
+ * Public shim allowing us to stay backward-compatible with legacy applications which were using
+ * Conscrypt's extended socket API before the introduction of the {@link Conscrypt} class.
*
* @hide
*/
@Internal
-public class OpenSSLSocketImpl
- extends javax.net.ssl.SSLSocket
- implements NativeCrypto.SSLHandshakeCallbacks, SSLParametersImpl.AliasChooser,
- SSLParametersImpl.PSKCallbacks {
-
- private static final boolean DBG_STATE = false;
-
- /**
- * Protects handshakeStarted and handshakeCompleted.
- */
- private final Object stateLock = new Object();
-
- /**
- * The {@link OpenSSLSocketImpl} object is constructed, but {@link #startHandshake()}
- * has not yet been called.
- */
- private static final int STATE_NEW = 0;
-
- /**
- * {@link #startHandshake()} has been called at least once.
- */
- private static final int STATE_HANDSHAKE_STARTED = 1;
-
- /**
- * {@link HandshakeCompletedListener#handshakeCompleted} has been called, but
- * {@link #startHandshake()} hasn't returned yet.
- */
- private static final int STATE_HANDSHAKE_COMPLETED = 2;
-
- /**
- * {@link #startHandshake()} has completed but
- * {@link HandshakeCompletedListener#handshakeCompleted} hasn't been called. This is expected
- * behaviour in cut-through mode, where SSL_do_handshake returns before the handshake is
- * complete. We can now start writing data to the socket.
- */
- private static final int STATE_READY_HANDSHAKE_CUT_THROUGH = 3;
-
- /**
- * {@link #startHandshake()} has completed and
- * {@link HandshakeCompletedListener#handshakeCompleted} has been called.
- */
- private static final int STATE_READY = 4;
-
- /**
- * {@link #close()} has been called at least once.
- */
- private static final int STATE_CLOSED = 5;
-
- // @GuardedBy("stateLock");
- private int state = STATE_NEW;
-
- /**
- * Protected by synchronizing on stateLock. Starts as 0, set by
- * startHandshake, reset to 0 on close.
- */
- // @GuardedBy("stateLock");
- private long sslNativePointer;
-
- /**
- * Protected by synchronizing on stateLock. Starts as null, set by
- * getInputStream.
- */
- // @GuardedBy("stateLock");
- private SSLInputStream is;
-
- /**
- * Protected by synchronizing on stateLock. Starts as null, set by
- * getInputStream.
- */
- // @GuardedBy("stateLock");
- private SSLOutputStream os;
-
- private final Socket socket;
- private final boolean autoClose;
-
- /**
- * The peer's DNS hostname if it was supplied during creation. Note that
- * this may be a raw IP address, so it should be checked before use with
- * extensions that don't use it like Server Name Indication (SNI).
- */
- private String peerHostname;
-
- /**
- * The peer's port if it was supplied during creation. Should only be set if
- * {@link #peerHostname} is also set.
- */
- private final int peerPort;
-
- private final SSLParametersImpl sslParameters;
-
- /*
- * A CloseGuard object on Android. On other platforms, this is nothing.
- */
- private final Object guard = Platform.closeGuardGet();
-
- private ArrayList<HandshakeCompletedListener> listeners;
-
- /**
- * Private key for the TLS Channel ID extension. This field is client-side
- * only. Set during startHandshake.
- */
- private OpenSSLKey channelIdPrivateKey;
-
- /** Set during startHandshake. */
- private AbstractOpenSSLSession sslSession;
-
- /** Used during handshake callbacks. */
- private AbstractOpenSSLSession handshakeSession;
-
- /**
- * Local cache of timeout to avoid getsockopt on every read and
- * write for non-wrapped sockets. Note that
- * OpenSSLSocketImplWrapper overrides setSoTimeout and
- * getSoTimeout to delegate to the wrapped socket.
- */
- private int readTimeoutMilliseconds = 0;
- private int writeTimeoutMilliseconds = 0;
-
- private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite
-
- OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
- this.socket = this;
- this.peerHostname = null;
- this.peerPort = -1;
- this.autoClose = false;
- this.sslParameters = sslParameters;
+public abstract class OpenSSLSocketImpl extends AbstractConscryptSocket {
+ OpenSSLSocketImpl() throws IOException {
}
- OpenSSLSocketImpl(String hostname, int port, SSLParametersImpl sslParameters)
- throws IOException {
+ OpenSSLSocketImpl(String hostname, int port) throws IOException {
super(hostname, port);
- this.socket = this;
- this.peerHostname = hostname;
- this.peerPort = port;
- this.autoClose = false;
- this.sslParameters = sslParameters;
}
- OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
- throws IOException {
+ OpenSSLSocketImpl(InetAddress address, int port) throws IOException {
super(address, port);
- this.socket = this;
- this.peerHostname = null;
- this.peerPort = -1;
- this.autoClose = false;
- this.sslParameters = sslParameters;
}
-
- OpenSSLSocketImpl(String hostname, int port,
- InetAddress clientAddress, int clientPort,
- SSLParametersImpl sslParameters) throws IOException {
+ OpenSSLSocketImpl(String hostname, int port, InetAddress clientAddress, int clientPort)
+ throws IOException {
super(hostname, port, clientAddress, clientPort);
- this.socket = this;
- this.peerHostname = hostname;
- this.peerPort = port;
- this.autoClose = false;
- this.sslParameters = sslParameters;
}
- OpenSSLSocketImpl(InetAddress address, int port,
- InetAddress clientAddress, int clientPort,
- SSLParametersImpl sslParameters) throws IOException {
+ OpenSSLSocketImpl(InetAddress address, int port, InetAddress clientAddress,
+ int clientPort)
+ throws IOException {
super(address, port, clientAddress, clientPort);
- this.socket = this;
- this.peerHostname = null;
- this.peerPort = -1;
- this.autoClose = false;
- this.sslParameters = sslParameters;
}
- /**
- * Create an SSL socket that wraps another socket. Invoked by
- * OpenSSLSocketImplWrapper constructor.
- */
- OpenSSLSocketImpl(Socket socket, String hostname, int port,
- boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
- this.socket = socket;
- this.peerHostname = hostname;
- this.peerPort = port;
- this.autoClose = autoClose;
- this.sslParameters = sslParameters;
-
- // this.timeout is not set intentionally.
- // OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout
- // to wrapped socket
+ OpenSSLSocketImpl(Socket socket, String hostname, int port, boolean autoClose)
+ throws IOException {
+ super(socket, hostname, port, autoClose);
}
@Override
- public void connect(SocketAddress endpoint) throws IOException {
- connect(endpoint, 0);
- }
-
- /**
- * Try to extract the peer's hostname if it's available from the endpoint address.
- */
- @Override
- public void connect(SocketAddress endpoint, int timeout) throws IOException {
- if (peerHostname == null && endpoint instanceof InetSocketAddress) {
- peerHostname = Platform.getHostStringFromInetSocketAddress(
- (InetSocketAddress) endpoint);
- }
-
- super.connect(endpoint, timeout);
- }
-
- private void checkOpen() throws SocketException {
- if (isClosed()) {
- throw new SocketException("Socket is closed");
- }
- }
-
- /**
- * Starts a TLS/SSL handshake on this connection using some native methods
- * from the OpenSSL library. It can negotiate new encryption keys, change
- * cipher suites, or initiate a new session. The certificate chain is
- * verified if the correspondent property in java.Security is set. All
- * listeners are notified at the end of the TLS/SSL handshake.
- */
- @Override
- public void startHandshake() throws IOException {
- checkOpen();
- synchronized (stateLock) {
- if (state == STATE_NEW) {
- state = STATE_HANDSHAKE_STARTED;
- } else {
- // We've either started the handshake already or have been closed.
- // Do nothing in both cases.
- return;
- }
- }
-
- final boolean client = sslParameters.getUseClientMode();
-
- sslNativePointer = 0;
- boolean releaseResources = true;
- try {
- final AbstractSessionContext sessionContext = sslParameters.getSessionContext();
- sslNativePointer = NativeCrypto.SSL_new(sessionContext.sslCtxNativePointer);
- Platform.closeGuardOpen(guard, "close");
-
- boolean enableSessionCreation = getEnableSessionCreation();
- if (!enableSessionCreation) {
- NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
- enableSessionCreation);
- }
-
- // Allow servers to trigger renegotiation. Some inadvisable server
- // configurations cause them to attempt to renegotiate during
- // certain protocols.
- NativeCrypto.SSL_accept_renegotiations(sslNativePointer);
-
- if (client) {
- NativeCrypto.SSL_set_connect_state(sslNativePointer);
-
- // Configure OCSP and CT extensions for client
- NativeCrypto.SSL_enable_ocsp_stapling(sslNativePointer);
- if (sslParameters.isCTVerificationEnabled(getHostname())) {
- NativeCrypto.SSL_enable_signed_cert_timestamps(sslNativePointer);
- }
- } else {
- NativeCrypto.SSL_set_accept_state(sslNativePointer);
-
- // Configure OCSP for server
- if (sslParameters.getOCSPResponse() != null) {
- NativeCrypto.SSL_enable_ocsp_stapling(sslNativePointer);
- }
- }
-
- final AbstractOpenSSLSession sessionToReuse =
- sslParameters.getSessionToReuse(sslNativePointer, getHostnameOrIP(), getPort());
- sslParameters.setSSLParameters(sslNativePointer, this, this, getHostname());
- sslParameters.setCertificateValidation(sslNativePointer);
- sslParameters.setTlsChannelId(sslNativePointer, channelIdPrivateKey);
-
- // Temporarily use a different timeout for the handshake process
- int savedReadTimeoutMilliseconds = getSoTimeout();
- int savedWriteTimeoutMilliseconds = getSoWriteTimeout();
- if (handshakeTimeoutMilliseconds >= 0) {
- setSoTimeout(handshakeTimeoutMilliseconds);
- setSoWriteTimeout(handshakeTimeoutMilliseconds);
- }
-
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- return;
- }
- }
-
- long sslSessionNativePointer;
- try {
- NativeCrypto.SSL_do_handshake(
- sslNativePointer, Platform.getFileDescriptor(socket), this, getSoTimeout());
- sslSessionNativePointer = NativeCrypto.SSL_get1_session(sslNativePointer);
- } catch (CertificateException e) {
- SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
- wrapper.initCause(e);
- throw wrapper;
- } catch (SSLException e) {
- // Swallow this exception if it's thrown as the result of an interruption.
- //
- // TODO: SSL_read and SSL_write return -1 when interrupted, but SSL_do_handshake
- // will throw the last sslError that it saw before sslSelect, usually SSL_WANT_READ
- // (or WANT_WRITE). Catching that exception here doesn't seem much worse than
- // changing the native code to return a "special" native pointer value when that
- // happens.
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- return;
- }
- }
-
- // Write CCS errors to EventLog
- String message = e.getMessage();
- // Must match error string of SSL_R_UNEXPECTED_CCS
- if (message.contains("unexpected CCS")) {
- String logMessage = String.format("ssl_unexpected_ccs: host=%s",
- getHostnameOrIP());
- Platform.logEvent(logMessage);
- }
-
- throw e;
- }
-
- boolean handshakeCompleted = false;
- synchronized (stateLock) {
- if (state == STATE_HANDSHAKE_COMPLETED) {
- handshakeCompleted = true;
- } else if (state == STATE_CLOSED) {
- return;
- }
- }
-
- sslSession = sslParameters.setupSession(sslSessionNativePointer, sslNativePointer,
- sessionToReuse, getHostnameOrIP(), getPort(), handshakeCompleted);
-
- // Restore the original timeout now that the handshake is complete
- if (handshakeTimeoutMilliseconds >= 0) {
- setSoTimeout(savedReadTimeoutMilliseconds);
- setSoWriteTimeout(savedWriteTimeoutMilliseconds);
- }
-
- // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
- if (handshakeCompleted) {
- notifyHandshakeCompletedListeners();
- }
-
- synchronized (stateLock) {
- releaseResources = (state == STATE_CLOSED);
-
- if (state == STATE_HANDSHAKE_STARTED) {
- state = STATE_READY_HANDSHAKE_CUT_THROUGH;
- } else if (state == STATE_HANDSHAKE_COMPLETED) {
- state = STATE_READY;
- }
-
- if (!releaseResources) {
- // Unblock threads that are waiting for our state to transition
- // into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH.
- stateLock.notifyAll();
- }
- }
- } catch (SSLProtocolException e) {
- throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed")
- .initCause(e);
- } finally {
- // on exceptional exit, treat the socket as closed
- if (releaseResources) {
- synchronized (stateLock) {
- // Mark the socket as closed since we might have reached this as
- // a result on an exception thrown by the handshake process.
- //
- // The state will already be set to closed if we reach this as a result of
- // an early return or an interruption due to a concurrent call to close().
- state = STATE_CLOSED;
- stateLock.notifyAll();
- }
-
- try {
- shutdownAndFreeSslNative();
- } catch (IOException ignored) {
- // Ignored.
- }
- }
- }
- }
-
- /**
- * Returns the hostname that was supplied during socket creation. No DNS resolution is
- * attempted before returning the hostname.
- */
public String getHostname() {
- return peerHostname;
- }
-
- /**
- * For the purposes of an SSLSession, we want a way to represent the supplied hostname
- * or the IP address in a textual representation. We do not want to perform reverse DNS
- * lookups on this address.
- */
- public String getHostnameOrIP() {
- if (peerHostname != null) {
- return peerHostname;
- }
-
- InetAddress peerAddress = getInetAddress();
- if (peerAddress != null) {
- return peerAddress.getHostAddress();
- }
-
- return null;
+ return super.getHostname();
}
@Override
- public int getPort() {
- return peerPort == -1 ? super.getPort() : peerPort;
- }
-
- @Override
- @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
- public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
- throws CertificateEncodingException, SSLException {
- sslParameters.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals,
- sslNativePointer, this);
- }
-
- @Override
- @SuppressWarnings("unused") // used by native psk_client_callback
- public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
- return sslParameters.clientPSKKeyRequested(identityHint, identity, key, this);
- }
-
- @Override
- @SuppressWarnings("unused") // used by native psk_server_callback
- public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
- return sslParameters.serverPSKKeyRequested(identityHint, identity, key, this);
- }
-
- @Override
- @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
- public void onSSLStateChange(int type, int val) {
- if (type != NativeConstants.SSL_CB_HANDSHAKE_DONE) {
- return;
- }
-
- synchronized (stateLock) {
- if (state == STATE_HANDSHAKE_STARTED) {
- // If sslSession is null, the handshake was completed during
- // the call to NativeCrypto.SSL_do_handshake and not during a
- // later read operation. That means we do not need to fix up
- // the SSLSession and session cache or notify
- // HandshakeCompletedListeners, it will be done in
- // startHandshake.
-
- state = STATE_HANDSHAKE_COMPLETED;
- return;
- } else if (state == STATE_READY_HANDSHAKE_CUT_THROUGH) {
- // We've returned from startHandshake, which means we've set a sslSession etc.
- // we need to fix them up, which we'll do outside this lock.
- } else if (state == STATE_CLOSED) {
- // Someone called "close" but the handshake hasn't been interrupted yet.
- return;
- }
- }
-
- // reset session id from the native pointer and update the
- // appropriate cache.
- sslSession.resetId();
- AbstractSessionContext sessionContext =
- (sslParameters.getUseClientMode())
- ? sslParameters.getClientSessionContext()
- : sslParameters.getServerSessionContext();
- sessionContext.putSession(sslSession);
-
- // let listeners know we are finally done
- notifyHandshakeCompletedListeners();
-
- synchronized (stateLock) {
- // Now that we've fixed up our state, we can tell waiting threads that
- // we're ready.
- state = STATE_READY;
- // Notify all threads waiting for the handshake to complete.
- stateLock.notifyAll();
- }
- }
-
- void notifyHandshakeCompletedListeners() {
- if (listeners != null && !listeners.isEmpty()) {
- // notify the listeners
- HandshakeCompletedEvent event =
- new HandshakeCompletedEvent(this, sslSession);
- for (HandshakeCompletedListener listener : listeners) {
- try {
- listener.handshakeCompleted(event);
- } catch (RuntimeException e) {
- // The RI runs the handlers in a separate thread,
- // which we do not. But we try to preserve their
- // behavior of logging a problem and not killing
- // the handshaking thread just because a listener
- // has a problem.
- Thread thread = Thread.currentThread();
- thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
- }
- }
- }
- }
-
- @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
- @Override
- public void verifyCertificateChain(long[] certRefs, String authMethod)
- throws CertificateException {
- try {
- X509TrustManager x509tm = sslParameters.getX509TrustManager();
- if (x509tm == null) {
- throw new CertificateException("No X.509 TrustManager");
- }
- if (certRefs == null || certRefs.length == 0) {
- throw new SSLException("Peer sent no certificate");
- }
- OpenSSLX509Certificate[] peerCertChain =
- OpenSSLX509Certificate.createCertChain(certRefs);
-
- byte[] ocspData = NativeCrypto.SSL_get_ocsp_response(sslNativePointer);
- byte[] tlsSctData = NativeCrypto.SSL_get_signed_cert_timestamp_list(sslNativePointer);
-
- // Used for verifyCertificateChain callback
- handshakeSession = new OpenSSLSessionImpl(
- NativeCrypto.SSL_get1_session(sslNativePointer), null, peerCertChain, ocspData,
- tlsSctData, getHostnameOrIP(), getPort(), null);
-
- boolean client = sslParameters.getUseClientMode();
- if (client) {
- Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
- } else {
- String authType = peerCertChain[0].getPublicKey().getAlgorithm();
- Platform.checkClientTrusted(x509tm, peerCertChain, authType, this);
- }
- } catch (CertificateException e) {
- throw e;
- } catch (Exception e) {
- throw new CertificateException(e);
- } finally {
- // Clear this before notifying handshake completed listeners
- handshakeSession = null;
- }
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- checkOpen();
-
- InputStream returnVal;
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- throw new SocketException("Socket is closed.");
- }
-
- if (is == null) {
- is = new SSLInputStream();
- }
-
- returnVal = is;
- }
-
- // Block waiting for a handshake without a lock held. It's possible that the socket
- // is closed at this point. If that happens, we'll still return the input stream but
- // all reads on it will throw.
- waitForHandshake();
- return returnVal;
- }
-
- @Override
- public OutputStream getOutputStream() throws IOException {
- checkOpen();
-
- OutputStream returnVal;
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- throw new SocketException("Socket is closed.");
- }
-
- if (os == null) {
- os = new SSLOutputStream();
- }
-
- returnVal = os;
- }
-
- // Block waiting for a handshake without a lock held. It's possible that the socket
- // is closed at this point. If that happens, we'll still return the output stream but
- // all writes on it will throw.
- waitForHandshake();
- return returnVal;
- }
-
- private void assertReadableOrWriteableState() {
- if (state == STATE_READY || state == STATE_READY_HANDSHAKE_CUT_THROUGH) {
- return;
- }
-
- throw new AssertionError("Invalid state: " + state);
- }
-
-
- private void waitForHandshake() throws IOException {
- startHandshake();
-
- synchronized (stateLock) {
- while (state != STATE_READY &&
- state != STATE_READY_HANDSHAKE_CUT_THROUGH &&
- state != STATE_CLOSED) {
- try {
- stateLock.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new IOException("Interrupted waiting for handshake", e);
- }
- }
-
- if (state == STATE_CLOSED) {
- throw new SocketException("Socket is closed");
- }
- }
- }
-
- /**
- * This inner class provides input data stream functionality
- * for the OpenSSL native implementation. It is used to
- * read data received via SSL protocol.
- */
- private class SSLInputStream extends InputStream {
- /**
- * OpenSSL only lets one thread read at a time, so this is used to
- * make sure we serialize callers of SSL_read. Thread is already
- * expected to have completed handshaking.
- */
- private final Object readLock = new Object();
-
- SSLInputStream() {
- }
-
- /**
- * Reads one byte. If there is no data in the underlying buffer,
- * this operation can block until the data will be
- * available.
- */
- @Override
- public int read() throws IOException {
- byte[] buffer = new byte[1];
- int result = read(buffer, 0, 1);
- return (result != -1) ? buffer[0] & 0xff : -1;
- }
-
- /**
- * Method acts as described in spec for superclass.
- * @see java.io.InputStream#read(byte[],int,int)
- */
- @Override
- public int read(byte[] buf, int offset, int byteCount) throws IOException {
- Platform.blockGuardOnNetwork();
-
- checkOpen();
- ArrayUtils.checkOffsetAndCount(buf.length, offset, byteCount);
- if (byteCount == 0) {
- return 0;
- }
-
- synchronized (readLock) {
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- throw new SocketException("socket is closed");
- }
-
- if (DBG_STATE) assertReadableOrWriteableState();
- }
-
- return NativeCrypto.SSL_read(sslNativePointer, Platform.getFileDescriptor(socket),
- OpenSSLSocketImpl.this, buf, offset, byteCount, getSoTimeout());
- }
- }
-
- void awaitPendingOps() {
- if (DBG_STATE) {
- synchronized (stateLock) {
- if (state != STATE_CLOSED) throw new AssertionError("State is: " + state);
- }
- }
-
- synchronized (readLock) { }
- }
- }
-
- /**
- * This inner class provides output data stream functionality
- * for the OpenSSL native implementation. It is used to
- * write data according to the encryption parameters given in SSL context.
- */
- private class SSLOutputStream extends OutputStream {
-
- /**
- * OpenSSL only lets one thread write at a time, so this is used
- * to make sure we serialize callers of SSL_write. Thread is
- * already expected to have completed handshaking.
- */
- private final Object writeLock = new Object();
-
- SSLOutputStream() {
- }
-
- /**
- * Method acts as described in spec for superclass.
- * @see java.io.OutputStream#write(int)
- */
- @Override
- public void write(int oneByte) throws IOException {
- byte[] buffer = new byte[1];
- buffer[0] = (byte) (oneByte & 0xff);
- write(buffer);
- }
-
- /**
- * Method acts as described in spec for superclass.
- * @see java.io.OutputStream#write(byte[],int,int)
- */
- @Override
- public void write(byte[] buf, int offset, int byteCount) throws IOException {
- Platform.blockGuardOnNetwork();
- checkOpen();
- ArrayUtils.checkOffsetAndCount(buf.length, offset, byteCount);
- if (byteCount == 0) {
- return;
- }
-
- synchronized (writeLock) {
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- throw new SocketException("socket is closed");
- }
-
- if (DBG_STATE) assertReadableOrWriteableState();
- }
-
- NativeCrypto.SSL_write(sslNativePointer, Platform.getFileDescriptor(socket),
- OpenSSLSocketImpl.this, buf, offset, byteCount, writeTimeoutMilliseconds);
- }
- }
-
-
- void awaitPendingOps() {
- if (DBG_STATE) {
- synchronized (stateLock) {
- if (state != STATE_CLOSED) throw new AssertionError("State is: " + state);
- }
- }
-
- synchronized (writeLock) { }
- }
- }
-
-
- @Override
- public SSLSession getSession() {
- if (sslSession == null) {
- boolean handshakeCompleted = false;
- try {
- if (isConnected()) {
- waitForHandshake();
- handshakeCompleted = true;
- }
- } catch (IOException e) {
- // Fall through.
- }
-
- if (!handshakeCompleted) {
- // return an invalid session with
- // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
- return SSLNullSession.getNullSession();
- }
- }
- return Platform.wrapSSLSession(sslSession);
- }
-
- /* @Override */
- @SuppressWarnings("MissingOverride") // For compilation with Java 6.
- public SSLSession getHandshakeSession() {
- return handshakeSession;
- }
-
- @Override
- public void addHandshakeCompletedListener(
- HandshakeCompletedListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("Provided listener is null");
- }
- if (listeners == null) {
- listeners = new ArrayList<HandshakeCompletedListener>();
- }
- listeners.add(listener);
- }
-
- @Override
- public void removeHandshakeCompletedListener(
- HandshakeCompletedListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("Provided listener is null");
- }
- if (listeners == null) {
- throw new IllegalArgumentException(
- "Provided listener is not registered");
- }
- if (!listeners.remove(listener)) {
- throw new IllegalArgumentException(
- "Provided listener is not registered");
- }
- }
-
- @Override
- public boolean getEnableSessionCreation() {
- return sslParameters.getEnableSessionCreation();
- }
-
- @Override
- public void setEnableSessionCreation(boolean flag) {
- sslParameters.setEnableSessionCreation(flag);
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return NativeCrypto.getSupportedCipherSuites();
- }
-
- @Override
- public String[] getEnabledCipherSuites() {
- return sslParameters.getEnabledCipherSuites();
- }
-
- @Override
- public void setEnabledCipherSuites(String[] suites) {
- sslParameters.setEnabledCipherSuites(suites);
- }
-
- @Override
- public String[] getSupportedProtocols() {
- return NativeCrypto.getSupportedProtocols();
- }
-
- @Override
- public String[] getEnabledProtocols() {
- return sslParameters.getEnabledProtocols();
- }
-
- @Override
- public void setEnabledProtocols(String[] protocols) {
- sslParameters.setEnabledProtocols(protocols);
- }
-
- /**
- * This method enables session ticket support.
- *
- * @param useSessionTickets True to enable session tickets
- */
- public void setUseSessionTickets(boolean useSessionTickets) {
- sslParameters.setUseSessionTickets(useSessionTickets);
- }
-
- /**
- * This method enables Server Name Indication
- *
- * @param hostname the desired SNI hostname, or null to disable
- */
public void setHostname(String hostname) {
- sslParameters.setUseSni(hostname != null);
- peerHostname = hostname;
- }
-
- /**
- * Enables/disables TLS Channel ID for this server socket.
- *
- * <p>This method needs to be invoked before the handshake starts.
- *
- * @throws IllegalStateException if this is a client socket or if the handshake has already
- * started.
- */
- public void setChannelIdEnabled(boolean enabled) {
- if (getUseClientMode()) {
- throw new IllegalStateException("Client mode");
- }
-
- synchronized (stateLock) {
- if (state != STATE_NEW) {
- throw new IllegalStateException(
- "Could not enable/disable Channel ID after the initial handshake has"
- + " begun.");
- }
- }
- sslParameters.channelIdEnabled = enabled;
- }
-
- /**
- * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
- * handshake completes.
- *
- * @return channel ID or {@code null} if not available.
- *
- * @throws IllegalStateException if this is a client socket or if the handshake has not yet
- * completed.
- * @throws SSLException if channel ID is available but could not be obtained.
- */
- public byte[] getChannelId() throws SSLException {
- if (getUseClientMode()) {
- throw new IllegalStateException("Client mode");
- }
-
- synchronized (stateLock) {
- if (state != STATE_READY) {
- throw new IllegalStateException(
- "Channel ID is only available after handshake completes");
- }
- }
- return NativeCrypto.SSL_get_tls_channel_id(sslNativePointer);
- }
-
- /**
- * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
- *
- * <p>This method needs to be invoked before the handshake starts.
- *
- * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
- * TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
- * P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
- *
- * @throws IllegalStateException if this is a server socket or if the handshake has already
- * started.
- */
- public void setChannelIdPrivateKey(PrivateKey privateKey) {
- if (!getUseClientMode()) {
- throw new IllegalStateException("Server mode");
- }
-
- synchronized (stateLock) {
- if (state != STATE_NEW) {
- throw new IllegalStateException(
- "Could not change Channel ID private key after the initial handshake has"
- + " begun.");
- }
- }
-
- if (privateKey == null) {
- sslParameters.channelIdEnabled = false;
- channelIdPrivateKey = null;
- } else {
- sslParameters.channelIdEnabled = true;
- try {
- ECParameterSpec ecParams = null;
- if (privateKey instanceof ECKey) {
- ecParams = ((ECKey) privateKey).getParams();
- }
- if (ecParams == null) {
- // Assume this is a P-256 key, as specified in the contract of this method.
- ecParams =
- OpenSSLECGroupContext.getCurveByName("prime256v1").getECParameterSpec();
- }
- channelIdPrivateKey =
- OpenSSLKey.fromECPrivateKeyForTLSStackOnly(privateKey, ecParams);
- } catch (InvalidKeyException e) {
- // Will have error in startHandshake
- }
- }
+ super.setHostname(hostname);
}
@Override
- public boolean getUseClientMode() {
- return sslParameters.getUseClientMode();
+ public String getHostnameOrIP() {
+ return super.getHostnameOrIP();
}
@Override
- public void setUseClientMode(boolean mode) {
- synchronized (stateLock) {
- if (state != STATE_NEW) {
- throw new IllegalArgumentException(
- "Could not change the mode after the initial handshake has begun.");
- }
- }
- sslParameters.setUseClientMode(mode);
- }
-
- @Override
- public boolean getWantClientAuth() {
- return sslParameters.getWantClientAuth();
- }
-
- @Override
- public boolean getNeedClientAuth() {
- return sslParameters.getNeedClientAuth();
- }
-
- @Override
- public void setNeedClientAuth(boolean need) {
- sslParameters.setNeedClientAuth(need);
- }
-
- @Override
- public void setWantClientAuth(boolean want) {
- sslParameters.setWantClientAuth(want);
- }
-
- @Override
- public void sendUrgentData(int data) throws IOException {
- throw new SocketException("Method sendUrgentData() is not supported.");
- }
-
- @Override
- public void setOOBInline(boolean on) throws SocketException {
- throw new SocketException("Methods sendUrgentData, setOOBInline are not supported.");
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
- if (socket != this) {
- socket.setSoTimeout(readTimeoutMilliseconds);
- } else {
- super.setSoTimeout(readTimeoutMilliseconds);
- }
-
- this.readTimeoutMilliseconds = readTimeoutMilliseconds;
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public int getSoTimeout() throws SocketException {
- return readTimeoutMilliseconds;
- }
-
- /**
- * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
- */
- public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
- this.writeTimeoutMilliseconds = writeTimeoutMilliseconds;
-
- Platform.setSocketWriteTimeout(this, writeTimeoutMilliseconds);
- }
-
- /**
- * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
- */
- public int getSoWriteTimeout() throws SocketException {
- return writeTimeoutMilliseconds;
- }
-
- /**
- * Set the handshake timeout on this socket. This timeout is specified in
- * milliseconds and will be used only during the handshake process.
- */
- public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
- this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds;
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public void close() throws IOException {
- // TODO: Close SSL sockets using a background thread so they close gracefully.
-
- SSLInputStream sslInputStream;
- SSLOutputStream sslOutputStream;
-
- synchronized (stateLock) {
- if (state == STATE_CLOSED) {
- // close() has already been called, so do nothing and return.
- return;
- }
-
- int oldState = state;
- state = STATE_CLOSED;
-
- if (oldState == STATE_NEW) {
- // The handshake hasn't been started yet, so there's no OpenSSL related
- // state to clean up. We still need to close the underlying socket if
- // we're wrapping it and were asked to autoClose.
- closeUnderlyingSocket();
-
- stateLock.notifyAll();
- return;
- }
-
- if (oldState != STATE_READY && oldState != STATE_READY_HANDSHAKE_CUT_THROUGH) {
- // If we're in these states, we still haven't returned from startHandshake.
- // We call SSL_interrupt so that we can interrupt SSL_do_handshake and then
- // set the state to STATE_CLOSED. startHandshake will handle all cleanup
- // after SSL_do_handshake returns, so we don't have anything to do here.
- NativeCrypto.SSL_interrupt(sslNativePointer);
-
- stateLock.notifyAll();
- return;
- }
-
- stateLock.notifyAll();
- // We've already returned from startHandshake, so we potentially have
- // input and output streams to clean up.
- sslInputStream = is;
- sslOutputStream = os;
- }
-
- // Don't bother interrupting unless we have something to interrupt.
- if (sslInputStream != null || sslOutputStream != null) {
- NativeCrypto.SSL_interrupt(sslNativePointer);
- }
-
- // Wait for the input and output streams to finish any reads they have in
- // progress. If there are no reads in progress at this point, future reads will
- // throw because state == STATE_CLOSED
- if (sslInputStream != null) {
- sslInputStream.awaitPendingOps();
- }
- if (sslOutputStream != null) {
- sslOutputStream.awaitPendingOps();
- }
-
- shutdownAndFreeSslNative();
- }
-
- private void shutdownAndFreeSslNative() throws IOException {
- try {
- Platform.blockGuardOnNetwork();
- NativeCrypto.SSL_shutdown(sslNativePointer, Platform.getFileDescriptor(socket),
- this);
- } catch (IOException ignored) {
- /*
- * Note that although close() can throw
- * IOException, the RI does not throw if there
- * is problem sending a "close notify" which
- * can happen if the underlying socket is closed.
- */
- } finally {
- free();
- closeUnderlyingSocket();
- }
- }
-
- private void closeUnderlyingSocket() throws IOException {
- if (socket != this) {
- if (autoClose && !socket.isClosed()) {
- socket.close();
- }
- } else {
- if (!super.isClosed()) {
- super.close();
- }
- }
- }
-
- private void free() {
- if (sslNativePointer == 0) {
- return;
- }
- NativeCrypto.SSL_free(sslNativePointer);
- sslNativePointer = 0;
- Platform.closeGuardClose(guard);
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- /*
- * Just worry about our own state. Notably we do not try and
- * close anything. The SocketImpl, either our own
- * PlainSocketImpl, or the Socket we are wrapping, will do
- * that. This might mean we do not properly SSL_shutdown, but
- * if you want to do that, properly close the socket yourself.
- *
- * The reason why we don't try to SSL_shutdown, is that there
- * can be a race between finalizers where the PlainSocketImpl
- * finalizer runs first and closes the socket. However, in the
- * meanwhile, the underlying file descriptor could be reused
- * for another purpose. If we call SSL_shutdown, the
- * underlying socket BIOs still have the old file descriptor
- * and will write the close notify to some unsuspecting
- * reader.
- */
- if (guard != null) {
- Platform.closeGuardWarnIfOpen(guard);
- }
- free();
- } finally {
- super.finalize();
- }
- }
-
- /* @Override */
public FileDescriptor getFileDescriptor$() {
- if (socket == this) {
- return Platform.getFileDescriptorFromSSLSocket(this);
- } else {
- return Platform.getFileDescriptor(socket);
- }
- }
-
- /**
- * Returns null always for backward compatibility.
- */
- public byte[] getNpnSelectedProtocol() {
- return null;
- }
-
- /**
- * Returns the protocol agreed upon by client and server, or {@code null} if
- * no protocol was agreed upon.
- */
- public byte[] getAlpnSelectedProtocol() {
- return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer);
- }
-
- /**
- * This method does nothing and is kept for backward compatibility.
- */
- public void setNpnProtocols(byte[] npnProtocols) {
- }
-
- /**
- * Sets the list of ALPN protocols. This method internally converts the protocols to their
- * wire-format form.
- *
- * @param alpnProtocols the list of ALPN protocols
- * @see #setAlpnProtocols(byte[])
- */
- public void setAlpnProtocols(String[] alpnProtocols) {
- sslParameters.setAlpnProtocols(alpnProtocols);
- }
-
- /**
- * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of
- * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings).
- * Requires that all strings be encoded with US-ASCII.
- *
- * @param alpnProtocols the encoded form of the ALPN protocol list
- * @see #setAlpnProtocols(String[])
- */
- public void setAlpnProtocols(byte[] alpnProtocols) {
- sslParameters.setAlpnProtocols(alpnProtocols);
+ return super.getFileDescriptor$();
}
@Override
- public SSLParameters getSSLParameters() {
- SSLParameters params = super.getSSLParameters();
- Platform.getSSLParameters(params, sslParameters, this);
- return params;
+ public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+ super.setSoWriteTimeout(writeTimeoutMilliseconds);
}
@Override
- public void setSSLParameters(SSLParameters p) {
- super.setSSLParameters(p);
- Platform.setSSLParameters(p, sslParameters, this);
+ public int getSoWriteTimeout() throws SocketException {
+ return super.getSoWriteTimeout();
}
@Override
- public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
- return keyManager.chooseServerAlias(keyType, null, this);
+ public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+ super.setHandshakeTimeout(handshakeTimeoutMilliseconds);
}
@Override
- public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
- String[] keyTypes) {
- return keyManager.chooseClientAlias(keyTypes, null, this);
+ public abstract SSLSession getHandshakeSession();
+
+ @Override
+ public abstract void setUseSessionTickets(boolean useSessionTickets);
+
+ @Override
+ public abstract void setChannelIdEnabled(boolean enabled);
+
+ @Override
+ public abstract byte[] getChannelId() throws SSLException;
+
+ @Override
+ public abstract void setChannelIdPrivateKey(PrivateKey privateKey);
+
+ @Override
+ public final byte[] getNpnSelectedProtocol() {
+ return super.getNpnSelectedProtocol();
}
@Override
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
- return keyManager.chooseServerKeyIdentityHint(this);
+ public final void setNpnProtocols(byte[] npnProtocols) {
+ super.setNpnProtocols(npnProtocols);
}
@Override
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
- return keyManager.chooseClientKeyIdentity(identityHint, this);
- }
+ public abstract byte[] getAlpnSelectedProtocol();
@Override
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
- return keyManager.getKey(identityHint, identity, this);
- }
+ public abstract void setAlpnProtocols(String[] alpnProtocols);
+
+ @Override
+ public abstract void setAlpnProtocols(byte[] alpnProtocols);
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java b/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java
deleted file mode 100644
index 848ef7f..0000000
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-
-/**
- * This class wraps the SSL functionality over an existing connected socket.
- */
-class OpenSSLSocketImplWrapper extends OpenSSLSocketImpl {
-
- private Socket socket;
-
- OpenSSLSocketImplWrapper(Socket socket, String hostname, int port,
- boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
- super(socket, hostname, port, autoClose, sslParameters);
- if (!socket.isConnected()) {
- throw new SocketException("Socket is not connected.");
- }
- this.socket = socket;
- }
-
- @Override
- public void connect(SocketAddress sockaddr, int timeout)
- throws IOException {
- socket.connect(sockaddr, timeout);
- }
-
- @Override
- public void connect(SocketAddress sockaddr) throws IOException {
- socket.connect(sockaddr);
- }
-
- @Override
- public void bind(SocketAddress sockaddr) throws IOException {
- socket.bind(sockaddr);
- }
-
- @Override
- public SocketAddress getRemoteSocketAddress() {
- return socket.getRemoteSocketAddress();
- }
-
- @Override
- public SocketAddress getLocalSocketAddress() {
- return socket.getLocalSocketAddress();
- }
-
- @Override
- public InetAddress getLocalAddress() {
- return socket.getLocalAddress();
- }
-
- @Override
- public InetAddress getInetAddress() {
- return socket.getInetAddress();
- }
-
- @Override
- public String toString() {
- return "SSL socket over " + socket.toString();
- }
-
- @Override
- public void setSoLinger(boolean on, int linger) throws SocketException {
- socket.setSoLinger(on, linger);
- }
-
- @Override
- public void setTcpNoDelay(boolean on) throws SocketException {
- socket.setTcpNoDelay(on);
- }
-
- @Override
- public void setReuseAddress(boolean on) throws SocketException {
- socket.setReuseAddress(on);
- }
-
- @Override
- public void setKeepAlive(boolean on) throws SocketException {
- socket.setKeepAlive(on);
- }
-
- @Override
- public void setTrafficClass(int tos) throws SocketException {
- socket.setTrafficClass(tos);
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public void setSendBufferSize(int size) throws SocketException {
- socket.setSendBufferSize(size);
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public void setReceiveBufferSize(int size) throws SocketException {
- socket.setReceiveBufferSize(size);
- }
-
- @Override
- public boolean getTcpNoDelay() throws SocketException {
- return socket.getTcpNoDelay();
- }
-
- @Override
- public boolean getReuseAddress() throws SocketException {
- return socket.getReuseAddress();
- }
-
- @Override
- public boolean getOOBInline() throws SocketException {
- return socket.getOOBInline();
- }
-
- @Override
- public boolean getKeepAlive() throws SocketException {
- return socket.getKeepAlive();
- }
-
- @Override
- public int getTrafficClass() throws SocketException {
- return socket.getTrafficClass();
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public int getSoTimeout() throws SocketException {
- return socket.getSoTimeout();
- }
-
- @Override
- public int getSoLinger() throws SocketException {
- return socket.getSoLinger();
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public int getSendBufferSize() throws SocketException {
- return socket.getSendBufferSize();
- }
-
- @Override
- @SuppressWarnings("UnsynchronizedOverridesSynchronized")
- public int getReceiveBufferSize() throws SocketException {
- return socket.getReceiveBufferSize();
- }
-
- @Override
- public boolean isConnected() {
- return socket.isConnected();
- }
-
- @Override
- public boolean isClosed() {
- return socket.isClosed();
- }
-
- @Override
- public boolean isBound() {
- return socket.isBound();
- }
-
- @Override
- public boolean isOutputShutdown() {
- return socket.isOutputShutdown();
- }
-
- @Override
- public boolean isInputShutdown() {
- return socket.isInputShutdown();
- }
-
- @Override
- public int getPort() {
- return socket.getPort();
- }
-
- @Override
- public int getLocalPort() {
- return socket.getLocalPort();
- }
-}
diff --git a/common/src/main/java/org/conscrypt/PeerInfoProvider.java b/common/src/main/java/org/conscrypt/PeerInfoProvider.java
new file mode 100644
index 0000000..e70f0cd
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/PeerInfoProvider.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 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;
+
+/**
+ * A provider for the peer host and port information.
+ */
+abstract class PeerInfoProvider {
+ private static final PeerInfoProvider NULL_PEER_INFO_PROVIDER = new PeerInfoProvider() {
+ @Override
+ String getHostname() {
+ return null;
+ }
+
+ @Override
+ public String getHostnameOrIP() {
+ return null;
+ }
+
+ @Override
+ public int getPort() {
+ return -1;
+ }
+ };
+
+ /**
+ * Returns the hostname supplied during engine/socket creation. No DNS resolution is
+ * attempted before returning the hostname.
+ */
+ abstract String getHostname();
+
+ /**
+ * This method attempts to create a textual representation of the peer host or IP. Does
+ * not perform a reverse DNS lookup. This is typically used during session creation.
+ */
+ abstract String getHostnameOrIP();
+
+ /**
+ * Gets the port of the peer.
+ */
+ abstract int getPort();
+
+ static PeerInfoProvider nullProvider() {
+ return NULL_PEER_INFO_PROVIDER;
+ }
+
+ static PeerInfoProvider forHostAndPort(final String host, final int port) {
+ return new PeerInfoProvider() {
+ @Override
+ String getHostname() {
+ return host;
+ }
+
+ @Override
+ public String getHostnameOrIP() {
+ return host;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+ };
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/Preconditions.java b/common/src/main/java/org/conscrypt/Preconditions.java
new file mode 100644
index 0000000..39fe712
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/Preconditions.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 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;
+
+/**
+ * Static convenience methods that help a method or constructor check whether it was invoked
+ * correctly (that is, whether its <i>preconditions</i> were met).
+ */
+final class Preconditions {
+ private Preconditions() {}
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @param errorMessage the exception message to use if the check fails.
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ static <T> T checkNotNull(T reference, String errorMessage) {
+ if (reference == null) {
+ throw new NullPointerException(errorMessage);
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures the truth of an expression involving one or more parameters to the calling method.
+ *
+ * @param condition to condition to be tested
+ * @param errorMessage the exception message to use if the check fails.
+ * @throws IllegalArgumentException if the condition is {@code false}
+ */
+ static void checkArgument(boolean condition, String errorMessage) {
+ if (!condition) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving one or more parameters to the calling method.
+ *
+ * @param condition to condition to be tested
+ * @param errorMessageTemplate the format string to be passed to {@link String#format(String,
+ * Object...)}
+ * @param arg the format argument to be passed to {@link String#format(String, Object...)}
+ * @throws IllegalArgumentException if the condition is {@code false}
+ */
+ static void checkArgument(boolean condition, String errorMessageTemplate, Object arg) {
+ if (!condition) {
+ throw new IllegalArgumentException(String.format(errorMessageTemplate, arg));
+ }
+ }
+
+ /**
+ * Ensures that {@code start} and {@code end} specify a valid <i>positions</i> in an array, list
+ * or string of size {@code size}, and are in order. A position index may range from zero to
+ * {@code size}, inclusive.
+ *
+ * @param start a user-supplied index identifying a starting position in an array, list or string
+ * @param end a user-supplied index identifying a ending position in an array, list or string
+ * @param size the size of that array, list or string
+ * @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size},
+ * or if {@code end} is less than {@code start}
+ * @throws IllegalArgumentException if {@code size} is negative
+ */
+ static void checkPositionIndexes(int start, int end, int size) {
+ // Carefully optimized for execution by hotspot (explanatory comment above)
+ if (start < 0 || end < start || end > size) {
+ throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
+ }
+ }
+
+ private static String badPositionIndexes(int start, int end, int size) {
+ if (start < 0 || start > size) {
+ return badPositionIndex(start, size, "start index");
+ }
+ if (end < 0 || end > size) {
+ return badPositionIndex(end, size, "end index");
+ }
+ // end < start
+ return String.format("end index (%s) must not be less than start index (%s)", end, start);
+ }
+
+ private static String badPositionIndex(int index, int size, String desc) {
+ if (index < 0) {
+ return String.format("%s (%s) must not be negative", desc, index);
+ } else if (size < 0) {
+ throw new IllegalArgumentException("negative size: " + size);
+ } else { // index > size
+ return String.format("%s (%s) must not be greater than size (%s)", desc, index, size);
+ }
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/SSLNullSession.java b/common/src/main/java/org/conscrypt/SSLNullSession.java
index 88a0a9c..93650d6 100644
--- a/common/src/main/java/org/conscrypt/SSLNullSession.java
+++ b/common/src/main/java/org/conscrypt/SSLNullSession.java
@@ -28,6 +28,7 @@
* SSLSocket#getSession()} before {@link SSLSocket#startHandshake()} is called.
*/
final class SSLNullSession implements SSLSession, Cloneable {
+ static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
/*
* Holds default instances so class preloading doesn't create an instance of
@@ -46,7 +47,11 @@
return DefaultHolder.NULL_SESSION;
}
- SSLNullSession() {
+ static boolean isNullSession(SSLSession session) {
+ return session == DefaultHolder.NULL_SESSION;
+ }
+
+ private SSLNullSession() {
creationTime = System.currentTimeMillis();
lastAccessedTime = creationTime;
}
@@ -58,7 +63,7 @@
@Override
public String getCipherSuite() {
- return "SSL_NULL_WITH_NULL_NULL";
+ return INVALID_CIPHER;
}
@Override
diff --git a/common/src/main/java/org/conscrypt/SSLParametersImpl.java b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
index 939401e..33e4c78 100644
--- a/common/src/main/java/org/conscrypt/SSLParametersImpl.java
+++ b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
@@ -17,29 +17,17 @@
package org.conscrypt;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.security.InvalidKeyException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
import javax.crypto.SecretKey;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
@@ -61,8 +49,6 @@
private static volatile X509KeyManager defaultX509KeyManager;
// default source of X.509 certificate based authentication trust decisions
private static volatile X509TrustManager defaultX509TrustManager;
- // default source of random numbers
- private static volatile SecureRandom defaultSecureRandom;
// default SSL parameters
private static volatile SSLParametersImpl defaultParameters;
@@ -83,11 +69,11 @@
private SecureRandom secureRandom;
// protocols enabled for SSL connection
- private String[] enabledProtocols;
+ String[] enabledProtocols;
// set to indicate when obsolete protocols are filtered
- private boolean isEnabledProtocolsFiltered;
+ boolean isEnabledProtocolsFiltered;
// cipher suites enabled for SSL connection
- private String[] enabledCipherSuites;
+ String[] enabledCipherSuites;
// if the peer with this parameters tuned to work in client mode
private boolean client_mode = true;
@@ -106,11 +92,11 @@
private boolean ctVerificationEnabled;
// server-side only. SCT and OCSP data to send to clients which request it
- private byte[] sctExtension;
- private byte[] ocspResponse;
+ byte[] sctExtension;
+ byte[] ocspResponse;
- private byte[] alpnProtocols;
- private boolean useSessionTickets;
+ byte[] alpnProtocols;
+ boolean useSessionTickets;
private Boolean useSni;
/**
@@ -189,13 +175,6 @@
}
/**
- * @return server session context
- */
- ServerSessionContext getServerSessionContext() {
- return serverSessionContext;
- }
-
- /**
* @return client session context
*/
ClientSessionContext getClientSessionContext() {
@@ -225,29 +204,6 @@
}
/**
- * @return secure random
- */
- SecureRandom getSecureRandom() {
- if (secureRandom != null) {
- return secureRandom;
- }
- SecureRandom result = defaultSecureRandom;
- if (result == null) {
- // single-check idiom
- defaultSecureRandom = result = new SecureRandom();
- }
- secureRandom = result;
- return secureRandom;
- }
-
- /**
- * @return the secure random member reference, even it is null
- */
- SecureRandom getSecureRandomMember() {
- return secureRandom;
- }
-
- /**
* @return the names of enabled cipher suites
*/
String[] getEnabledCipherSuites() {
@@ -308,6 +264,10 @@
this.alpnProtocols = alpnProtocols;
}
+ byte[] getAlpnProtocols() {
+ return alpnProtocols;
+ }
+
/**
* Tunes the peer holding this parameters to work in client mode.
* @param mode if the peer is configured to work in client mode
@@ -383,7 +343,7 @@
* extension Server Name Indication (SNI).
*/
void setUseSni(boolean flag) {
- useSni = Boolean.valueOf(flag);
+ useSni = flag;
}
/**
@@ -391,17 +351,26 @@
* extension Server Name Indication (SNI).
*/
boolean getUseSni() {
- return useSni != null ? useSni.booleanValue() : isSniEnabledByDefault();
+ return useSni != null ? useSni : isSniEnabledByDefault();
}
+ /**
+ * For testing only.
+ */
void setCTVerificationEnabled(boolean enabled) {
ctVerificationEnabled = enabled;
}
+ /**
+ * For testing only.
+ */
void setSCTExtension(byte[] extension) {
sctExtension = extension;
}
+ /**
+ * For testing only.
+ */
void setOCSPResponse(byte[] response) {
ocspResponse = response;
}
@@ -410,111 +379,6 @@
return ocspResponse;
}
- static byte[][] encodeIssuerX509Principals(X509Certificate[] certificates)
- throws CertificateEncodingException {
- byte[][] principalBytes = new byte[certificates.length][];
- for (int i = 0; i < certificates.length; i++) {
- principalBytes[i] = certificates[i].getIssuerX500Principal().getEncoded();
- }
- return principalBytes;
- }
-
- AbstractOpenSSLSession getSessionToReuse(long sslNativePointer, String hostname, int port)
- throws SSLException {
- OpenSSLSessionImpl sessionToReuse = null;
-
- if (client_mode) {
- // look for client session to reuse
- SSLSession cachedSession = getCachedClientSession(clientSessionContext, hostname, port);
- if (cachedSession != null) {
- // The native pointer is used here, so we have to make sure it's not a delegate
- // SSLSession class.
- cachedSession = Platform.unwrapSSLSession(cachedSession);
-
- if (cachedSession instanceof OpenSSLSessionImpl) {
- sessionToReuse = (OpenSSLSessionImpl) cachedSession;
- NativeCrypto.SSL_set_session(sslNativePointer,
- sessionToReuse.sslSessionNativePointer);
- }
- }
- }
-
- return sessionToReuse;
- }
-
- void setTlsChannelId(long sslNativePointer, OpenSSLKey channelIdPrivateKey)
- throws SSLHandshakeException, SSLException {
- // TLS Channel ID
- if (channelIdEnabled) {
- if (client_mode) {
- // Client-side TLS Channel ID
- if (channelIdPrivateKey == null) {
- throw new SSLHandshakeException("Invalid TLS channel ID key specified");
- }
- NativeCrypto.SSL_set1_tls_channel_id(sslNativePointer,
- channelIdPrivateKey.getNativeRef());
- } else {
- // Server-side TLS Channel ID
- NativeCrypto.SSL_enable_tls_channel_id(sslNativePointer);
- }
- }
- }
-
- void setCertificate(long sslNativePointer, String alias) throws CertificateEncodingException,
- SSLException {
- if (alias == null) {
- return;
- }
- X509KeyManager keyManager = getX509KeyManager();
- if (keyManager == null) {
- return;
- }
- PrivateKey privateKey = keyManager.getPrivateKey(alias);
- if (privateKey == null) {
- return;
- }
- X509Certificate[] certificates = keyManager.getCertificateChain(alias);
- if (certificates == null) {
- return;
- }
- PublicKey publicKey = (certificates.length > 0) ? certificates[0].getPublicKey() : null;
-
- /*
- * Make sure we keep a reference to the OpenSSLX509Certificate by using
- * this array. Otherwise, if they're not OpenSSLX509Certificate
- * instances originally, they may be garbage collected before we
- * complete our JNI calls.
- */
- OpenSSLX509Certificate[] openSslCerts = new OpenSSLX509Certificate[certificates.length];
- long[] x509refs = new long[certificates.length];
- for (int i = 0; i < certificates.length; i++) {
- OpenSSLX509Certificate openSslCert = OpenSSLX509Certificate
- .fromCertificate(certificates[i]);
- openSslCerts[i] = openSslCert;
- x509refs[i] = openSslCert.getContext();
- }
-
- // Note that OpenSSL says to use SSL_use_certificate before
- // SSL_use_PrivateKey.
- NativeCrypto.SSL_use_certificate(sslNativePointer, x509refs);
-
- final OpenSSLKey key;
- try {
- key = OpenSSLKey.fromPrivateKeyForTLSStackOnly(privateKey, publicKey);
- NativeCrypto.SSL_use_PrivateKey(sslNativePointer, key.getNativeRef());
- } catch (InvalidKeyException e) {
- throw new SSLException(e);
- }
-
- // We may not have access to all the information to check the private key
- // if it's a wrapped platform key, so skip this check.
- if (!key.isWrapped()) {
- // Makes sure the set PrivateKey and X509Certificate refer to the same
- // key by comparing the public values.
- NativeCrypto.SSL_check_private_key(sslNativePointer);
- }
- }
-
/**
* This filters {@code obsoleteProtocol} from the list of {@code protocols}
* down to help with app compatibility.
@@ -535,95 +399,6 @@
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- void setSSLParameters(long sslNativePointer, AliasChooser chooser, PSKCallbacks pskCallbacks,
- String sniHostname) throws SSLException, IOException {
- if (enabledProtocols.length == 0 && isEnabledProtocolsFiltered) {
- throw new SSLHandshakeException("No enabled protocols; "
- + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3
- + " is no longer supported and was filtered from the list");
- }
- NativeCrypto.SSL_configure_alpn(sslNativePointer, client_mode, alpnProtocols);
- NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
- NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
-
- // setup server certificates and private keys.
- // clients will receive a call back to request certificates.
- if (!client_mode) {
- Set<String> keyTypes = new HashSet<String>();
- for (long sslCipherNativePointer : NativeCrypto.SSL_get_ciphers(sslNativePointer)) {
- String keyType = getServerX509KeyType(sslCipherNativePointer);
- if (keyType != null) {
- keyTypes.add(keyType);
- }
- }
- X509KeyManager keyManager = getX509KeyManager();
- if (keyManager != null) {
- for (String keyType : keyTypes) {
- try {
- setCertificate(sslNativePointer,
- chooser.chooseServerAlias(x509KeyManager, keyType));
- } catch (CertificateEncodingException e) {
- throw new IOException(e);
- }
- }
- }
-
- NativeCrypto.SSL_set_options(sslNativePointer,
- NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- if (sctExtension != null) {
- NativeCrypto.SSL_set_signed_cert_timestamp_list(sslNativePointer, sctExtension);
- }
-
- if (ocspResponse != null) {
- NativeCrypto.SSL_set_ocsp_response(sslNativePointer, ocspResponse);
- }
- }
-
- enablePSKKeyManagerIfRequested(sslNativePointer, pskCallbacks);
-
- if (useSessionTickets) {
- NativeCrypto.SSL_clear_options(sslNativePointer, NativeConstants.SSL_OP_NO_TICKET);
- }
- if (getUseSni() && AddressUtils.isValidSniHostname(sniHostname)) {
- NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, sniHostname);
- }
-
- // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites
- // with TLSv1 and SSLv3).
- NativeCrypto.SSL_set_mode(sslNativePointer, NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING);
-
- boolean enableSessionCreation = getEnableSessionCreation();
- if (!enableSessionCreation) {
- NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer, enableSessionCreation);
- }
- }
-
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- private void enablePSKKeyManagerIfRequested(long sslNativePointer, PSKCallbacks pskCallbacks)
- throws SSLException {
- // Enable Pre-Shared Key (PSK) key exchange if requested
- PSKKeyManager pskKeyManager = getPSKKeyManager();
- if (pskKeyManager != null) {
- boolean pskEnabled = false;
- for (String enabledCipherSuite : enabledCipherSuites) {
- if ((enabledCipherSuite != null) && (enabledCipherSuite.contains("PSK"))) {
- pskEnabled = true;
- break;
- }
- }
- if (pskEnabled) {
- if (client_mode) {
- NativeCrypto.set_SSL_psk_client_callback_enabled(sslNativePointer, true);
- } else {
- NativeCrypto.set_SSL_psk_server_callback_enabled(sslNativePointer, true);
- String identityHint = pskCallbacks.chooseServerPSKIdentityHint(pskKeyManager);
- NativeCrypto.SSL_use_psk_identity_hint(sslNativePointer, identityHint);
- }
- }
- }
- }
-
/**
* Returns whether Server Name Indication (SNI) is enabled by default for
* sockets. For more information on SNI, see RFC 6066 section 3.
@@ -644,202 +419,6 @@
}
}
- void setCertificateValidation(long sslNativePointer) throws IOException {
- // setup peer certificate verification
- if (!client_mode) {
- // needing client auth takes priority...
- boolean certRequested;
- if (getNeedClientAuth()) {
- NativeCrypto.SSL_set_verify(sslNativePointer,
- NativeCrypto.SSL_VERIFY_PEER
- | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
- certRequested = true;
- // ... over just wanting it...
- } else if (getWantClientAuth()) {
- NativeCrypto.SSL_set_verify(sslNativePointer, NativeCrypto.SSL_VERIFY_PEER);
- certRequested = true;
- // ... and we must disable verification if we don't want client auth.
- } else {
- NativeCrypto.SSL_set_verify(sslNativePointer, NativeCrypto.SSL_VERIFY_NONE);
- certRequested = false;
- }
-
- if (certRequested) {
- X509TrustManager trustManager = getX509TrustManager();
- X509Certificate[] issuers = trustManager.getAcceptedIssuers();
- if (issuers != null && issuers.length != 0) {
- byte[][] issuersBytes;
- try {
- issuersBytes = encodeIssuerX509Principals(issuers);
- } catch (CertificateEncodingException e) {
- throw new IOException("Problem encoding principals", e);
- }
- NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
- }
- }
- }
- }
-
- AbstractOpenSSLSession setupSession(long sslSessionNativePointer, long sslNativePointer,
- final AbstractOpenSSLSession sessionToReuse, String hostname, int port,
- boolean handshakeCompleted) throws IOException {
- AbstractOpenSSLSession sslSession = null;
- if (sessionToReuse != null && NativeCrypto.SSL_session_reused(sslNativePointer)) {
- sslSession = sessionToReuse;
- sslSession.setLastAccessedTime(System.currentTimeMillis());
- NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
- } else {
- if (!getEnableSessionCreation()) {
- // Should have been prevented by
- // NativeCrypto.SSL_set_session_creation_enabled
- throw new IllegalStateException("SSL Session may not be created");
- }
- X509Certificate[] localCertificates = OpenSSLX509Certificate.createCertChain(
- NativeCrypto.SSL_get_certificate(sslNativePointer));
- X509Certificate[] peerCertificates = OpenSSLX509Certificate.createCertChain(
- NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
- byte[] ocspData = NativeCrypto.SSL_get_ocsp_response(sslNativePointer);
- byte[] tlsSctData = NativeCrypto.SSL_get_signed_cert_timestamp_list(sslNativePointer);
- sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
- peerCertificates, ocspData, tlsSctData, hostname, port, getSessionContext());
- // if not, putSession later in handshakeCompleted() callback
- if (handshakeCompleted) {
- getSessionContext().putSession(sslSession);
- }
- }
- return sslSession;
- }
-
- void chooseClientCertificate(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals,
- long sslNativePointer, AliasChooser chooser) throws SSLException,
- CertificateEncodingException {
- Set<String> keyTypesSet = getSupportedClientKeyTypes(keyTypeBytes);
- String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]);
-
- X500Principal[] issuers;
- if (asn1DerEncodedPrincipals == null) {
- issuers = null;
- } else {
- issuers = new X500Principal[asn1DerEncodedPrincipals.length];
- for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
- issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
- }
- }
- X509KeyManager keyManager = getX509KeyManager();
- String alias = (keyManager != null) ? chooser.chooseClientAlias(keyManager, issuers,
- keyTypes) : null;
- setCertificate(sslNativePointer, alias);
- }
-
- /**
- * @see NativeCrypto.SSLHandshakeCallbacks#clientPSKKeyRequested(String, byte[], byte[])
- */
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- int clientPSKKeyRequested(
- String identityHint, byte[] identityBytesOut, byte[] key, PSKCallbacks pskCallbacks) {
- PSKKeyManager pskKeyManager = getPSKKeyManager();
- if (pskKeyManager == null) {
- return 0;
- }
-
- String identity = pskCallbacks.chooseClientPSKIdentity(pskKeyManager, identityHint);
- // Store identity in NULL-terminated modified UTF-8 representation into ientityBytesOut
- byte[] identityBytes;
- if (identity == null) {
- identity = "";
- identityBytes = EmptyArray.BYTE;
- } else if (identity.isEmpty()) {
- identityBytes = EmptyArray.BYTE;
- } else {
- try {
- identityBytes = identity.getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("UTF-8 encoding not supported", e);
- }
- }
- if (identityBytes.length + 1 > identityBytesOut.length) {
- // Insufficient space in the output buffer
- return 0;
- }
- if (identityBytes.length > 0) {
- System.arraycopy(identityBytes, 0, identityBytesOut, 0, identityBytes.length);
- }
- identityBytesOut[identityBytes.length] = 0;
-
- SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
- byte[] secretKeyBytes = secretKey.getEncoded();
- if (secretKeyBytes == null) {
- return 0;
- } else if (secretKeyBytes.length > key.length) {
- // Insufficient space in the output buffer
- return 0;
- }
- System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
- return secretKeyBytes.length;
- }
-
- /**
- * @see NativeCrypto.SSLHandshakeCallbacks#serverPSKKeyRequested(String, String, byte[])
- */
- @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
- int serverPSKKeyRequested(
- String identityHint, String identity, byte[] key, PSKCallbacks pskCallbacks) {
- PSKKeyManager pskKeyManager = getPSKKeyManager();
- if (pskKeyManager == null) {
- return 0;
- }
- SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
- byte[] secretKeyBytes = secretKey.getEncoded();
- if (secretKeyBytes == null) {
- return 0;
- } else if (secretKeyBytes.length > key.length) {
- return 0;
- }
- System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
- return secretKeyBytes.length;
- }
-
- /**
- * Gets the suitable session reference from the session cache container.
- */
- SSLSession getCachedClientSession(ClientSessionContext sessionContext, String hostName,
- int port) {
- if (hostName == null) {
- return null;
- }
-
- SSLSession session = sessionContext.getSession(hostName, port);
- if (session == null) {
- return null;
- }
-
- String protocol = session.getProtocol();
- boolean protocolFound = false;
- for (String enabledProtocol : enabledProtocols) {
- if (protocol.equals(enabledProtocol)) {
- protocolFound = true;
- break;
- }
- }
- if (!protocolFound) {
- return null;
- }
-
- String cipherSuite = session.getCipherSuite();
- boolean cipherSuiteFound = false;
- for (String enabledCipherSuite : enabledCipherSuites) {
- if (cipherSuite.equals(enabledCipherSuite)) {
- cipherSuiteFound = true;
- break;
- }
- }
- if (!cipherSuiteFound) {
- return null;
- }
-
- return session;
- }
-
/**
* For abstracting the X509KeyManager calls between
* {@link X509KeyManager#chooseClientAlias(String[], java.security.Principal[], java.net.Socket)}
@@ -1004,93 +583,6 @@
this.useCipherSuitesOrder = useCipherSuitesOrder;
}
- /** Key type: RSA certificate. */
- private static final String KEY_TYPE_RSA = "RSA";
-
- /** Key type: Diffie-Hellman certificate signed by issuer with RSA signature. */
- private static final String KEY_TYPE_DH_RSA = "DH_RSA";
-
- /** Key type: Elliptic Curve certificate. */
- private static final String KEY_TYPE_EC = "EC";
-
- /** Key type: Elliptic Curve certificate signed by issuer with ECDSA signature. */
- private static final String KEY_TYPE_EC_EC = "EC_EC";
-
- /** Key type: Elliptic Curve certificate signed by issuer with RSA signature. */
- private static final String KEY_TYPE_EC_RSA = "EC_RSA";
-
- /**
- * Returns key type constant suitable for calling X509KeyManager.chooseServerAlias or
- * X509ExtendedKeyManager.chooseEngineServerAlias. Returns {@code null} for key exchanges that
- * do not use X.509 for server authentication.
- */
- private static String getServerX509KeyType(long sslCipherNative) throws SSLException {
- String kx_name = NativeCrypto.SSL_CIPHER_get_kx_name(sslCipherNative);
- if (kx_name.equals("RSA") || kx_name.equals("DHE_RSA") || kx_name.equals("ECDHE_RSA")) {
- return KEY_TYPE_RSA;
- } else if (kx_name.equals("ECDHE_ECDSA")) {
- return KEY_TYPE_EC;
- } else if (kx_name.equals("ECDH_RSA")) {
- return KEY_TYPE_EC_RSA;
- } else if (kx_name.equals("ECDH_ECDSA")) {
- return KEY_TYPE_EC_EC;
- } else if (kx_name.equals("DH_RSA")) {
- return KEY_TYPE_DH_RSA;
- } else {
- return null;
- }
- }
-
- /**
- * Similar to getServerKeyType, but returns value given TLS
- * ClientCertificateType byte values from a CertificateRequest
- * message for use with X509KeyManager.chooseClientAlias or
- * X509ExtendedKeyManager.chooseEngineClientAlias.
- * <p>
- * Visible for testing.
- */
- static String getClientKeyType(byte clientCertificateType) {
- // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
- switch (clientCertificateType) {
- case NativeConstants.TLS_CT_RSA_SIGN:
- return KEY_TYPE_RSA; // RFC rsa_sign
- case NativeConstants.TLS_CT_RSA_FIXED_DH:
- return KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
- case NativeConstants.TLS_CT_ECDSA_SIGN:
- return KEY_TYPE_EC; // RFC ecdsa_sign
- case NativeConstants.TLS_CT_RSA_FIXED_ECDH:
- return KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
- case NativeConstants.TLS_CT_ECDSA_FIXED_ECDH:
- return KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh
- default:
- return null;
- }
- }
-
- /**
- * Gets the supported key types for client certificates based on the
- * {@code ClientCertificateType} values provided by the server.
- *
- * @param clientCertificateTypes {@code ClientCertificateType} values provided by the server.
- * See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml.
- * @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and
- * {@code X509ExtendedKeyManager.chooseEngineClientAlias}.
- *
- * Visible for testing.
- */
- static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes) {
- Set<String> result = new HashSet<String>(clientCertificateTypes.length);
- for (byte keyTypeCode : clientCertificateTypes) {
- String keyType = getClientKeyType(keyTypeCode);
- if (keyType == null) {
- // Unsupported client key type -- ignore
- continue;
- }
- result.add(keyType);
- }
- return result;
- }
-
private static String[] getDefaultCipherSuites(
boolean x509CipherSuitesNeeded,
boolean pskCipherSuitesNeeded) {
diff --git a/common/src/main/java/org/conscrypt/SSLUtils.java b/common/src/main/java/org/conscrypt/SSLUtils.java
index c770260..7e9822f 100644
--- a/common/src/main/java/org/conscrypt/SSLUtils.java
+++ b/common/src/main/java/org/conscrypt/SSLUtils.java
@@ -41,8 +41,14 @@
import static org.conscrypt.NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
import java.nio.ByteBuffer;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.Set;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.security.cert.CertificateException;
/**
* Utility methods for SSL packet processing. Copied from the Netty project.
@@ -50,10 +56,95 @@
* This is a public class to allow testing to occur on Android via CTS.
*/
final class SSLUtils {
- static final boolean USE_ENGINE_SOCKET_BY_DEFAULT =
- Boolean.parseBoolean(System.getProperty("org.conscrypt.useEngineSocketByDefault"));
+ static final boolean USE_ENGINE_SOCKET_BY_DEFAULT = Boolean.parseBoolean(
+ System.getProperty("org.conscrypt.useEngineSocketByDefault", "false"));
private static final int MAX_PROTOCOL_LENGTH = 255;
+ // TODO(nathanmittler): Should these be in NativeConstants?
+ enum SessionType {
+ /**
+ * Identifies OpenSSL sessions.
+ */
+ OPEN_SSL(1),
+
+ /**
+ * Identifies OpenSSL sessions with OCSP stapled data.
+ */
+ OPEN_SSL_WITH_OCSP(2),
+
+ /**
+ * Identifies OpenSSL sessions with TLS SCT data.
+ */
+ OPEN_SSL_WITH_TLS_SCT(3);
+
+ SessionType(int value) {
+ this.value = value;
+ }
+
+ static final boolean isSupportedType(int type) {
+ return type == OPEN_SSL.value || type == OPEN_SSL_WITH_OCSP.value
+ || type == OPEN_SSL_WITH_TLS_SCT.value;
+ }
+
+ final int value;
+ }
+
+ /**
+ * States for SSL engines.
+ */
+ static final class EngineStates {
+ private EngineStates() {}
+
+ /**
+ * The engine is constructed, but the initial handshake hasn't been started
+ */
+ static final int STATE_NEW = 0;
+
+ /**
+ * The client/server mode of the engine has been set.
+ */
+ static final int STATE_MODE_SET = 1;
+
+ /**
+ * The handshake has been started
+ */
+ static final int STATE_HANDSHAKE_STARTED = 2;
+
+ /**
+ * Listeners of the handshake have been notified of completion but the handshake call
+ * hasn't returned.
+ */
+ static final int STATE_HANDSHAKE_COMPLETED = 3;
+
+ /**
+ * The handshake call returned but the listeners have not yet been notified. This is expected
+ * behaviour in cut-through mode, where SSL_do_handshake returns before the handshake is
+ * complete. We can now start writing data to the socket.
+ */
+ static final int STATE_READY_HANDSHAKE_CUT_THROUGH = 4;
+
+ /**
+ * The handshake call has returned and the listeners have been notified. Ready to begin
+ * writing data.
+ */
+ static final int STATE_READY = 5;
+
+ /**
+ * The inbound direction of the engine has been closed.
+ */
+ static final int STATE_CLOSED_INBOUND = 6;
+
+ /**
+ * The outbound direction of the engine has been closed.
+ */
+ static final int STATE_CLOSED_OUTBOUND = 7;
+
+ /**
+ * The engine has been closed.
+ */
+ static final int STATE_CLOSED = 8;
+ }
+
/**
* This is the maximum overhead when encrypting plaintext as defined by
* <a href="https://www.ietf.org/rfc/rfc5246.txt">rfc5264</a>,
@@ -74,6 +165,106 @@
private static final int MAX_ENCRYPTION_OVERHEAD_DIFF =
Integer.MAX_VALUE - MAX_ENCRYPTION_OVERHEAD_LENGTH;
+ /** Key type: RSA certificate. */
+ private static final String KEY_TYPE_RSA = "RSA";
+
+ /** Key type: Elliptic Curve certificate. */
+ private static final String KEY_TYPE_EC = "EC";
+
+ /**
+ * Returns key type constant suitable for calling X509KeyManager.chooseServerAlias or
+ * X509ExtendedKeyManager.chooseEngineServerAlias. Returns {@code null} for key exchanges that
+ * do not use X.509 for server authentication.
+ */
+ static String getServerX509KeyType(long sslCipherNative) throws SSLException {
+ String kx_name = NativeCrypto.SSL_CIPHER_get_kx_name(sslCipherNative);
+ if (kx_name.equals("RSA") || kx_name.equals("DHE_RSA") || kx_name.equals("ECDHE_RSA")) {
+ return KEY_TYPE_RSA;
+ } else if (kx_name.equals("ECDHE_ECDSA")) {
+ return KEY_TYPE_EC;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Similar to getServerKeyType, but returns value given TLS
+ * ClientCertificateType byte values from a CertificateRequest
+ * message for use with X509KeyManager.chooseClientAlias or
+ * X509ExtendedKeyManager.chooseEngineClientAlias.
+ * <p>
+ * Visible for testing.
+ */
+ static String getClientKeyType(byte clientCertificateType) {
+ // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
+ switch (clientCertificateType) {
+ case NativeConstants.TLS_CT_RSA_SIGN:
+ return KEY_TYPE_RSA; // RFC rsa_sign
+ case NativeConstants.TLS_CT_ECDSA_SIGN:
+ return KEY_TYPE_EC; // RFC ecdsa_sign
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets the supported key types for client certificates based on the
+ * {@code ClientCertificateType} values provided by the server.
+ *
+ * @param clientCertificateTypes {@code ClientCertificateType} values provided by the server.
+ * See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml.
+ * @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and
+ * {@code X509ExtendedKeyManager.chooseEngineClientAlias}.
+ *
+ * Visible for testing.
+ */
+ static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes) {
+ Set<String> result = new HashSet<String>(clientCertificateTypes.length);
+ for (byte keyTypeCode : clientCertificateTypes) {
+ String keyType = SSLUtils.getClientKeyType(keyTypeCode);
+ if (keyType == null) {
+ // Unsupported client key type -- ignore
+ continue;
+ }
+ result.add(keyType);
+ }
+ return result;
+ }
+
+ static byte[][] encodeIssuerX509Principals(X509Certificate[] certificates)
+ throws CertificateEncodingException {
+ byte[][] principalBytes = new byte[certificates.length][];
+ for (int i = 0; i < certificates.length; i++) {
+ principalBytes[i] = certificates[i].getIssuerX500Principal().getEncoded();
+ }
+ return principalBytes;
+ }
+
+ /**
+ * Converts the peer certificates into a cert chain.
+ */
+ static javax.security.cert.X509Certificate[] toCertificateChain(X509Certificate[] certificates)
+ throws SSLPeerUnverifiedException {
+ try {
+ javax.security.cert.X509Certificate[] chain =
+ new javax.security.cert.X509Certificate[certificates.length];
+
+ for (int i = 0; i < certificates.length; i++) {
+ byte[] encoded = certificates[i].getEncoded();
+ chain[i] = javax.security.cert.X509Certificate.getInstance(encoded);
+ }
+ return chain;
+ } catch (CertificateEncodingException e) {
+ SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
+ exception.initCause(exception);
+ throw exception;
+ } catch (CertificateException e) {
+ SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
+ exception.initCause(exception);
+ throw exception;
+ }
+ }
+
/**
* Calculates the minimum bytes required in the encrypted output buffer for the given number of
* plaintext source bytes.
diff --git a/common/src/main/java/org/conscrypt/ServerSessionContext.java b/common/src/main/java/org/conscrypt/ServerSessionContext.java
index 20fc3ae..dc4cafb 100644
--- a/common/src/main/java/org/conscrypt/ServerSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ServerSessionContext.java
@@ -16,7 +16,7 @@
package org.conscrypt;
-import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLContext;
/**
* Caches server sessions. Indexes by session ID. Users typically look up
@@ -26,10 +26,9 @@
*/
@Internal
public final class ServerSessionContext extends AbstractSessionContext {
-
private SSLServerSessionCache persistentCache;
- public ServerSessionContext() {
+ ServerSessionContext() {
super(100);
// TODO make sure SSL_CTX does not automaticaly clear sessions we want it to cache
@@ -48,30 +47,23 @@
NativeCrypto.SSL_CTX_set_session_id_context(sslCtxNativePointer, new byte[] { ' ' });
}
+ /**
+ * Applications should not use this method. Instead use {@link
+ * Conscrypt.Contexts#setServerSessionCache(SSLContext, SSLServerSessionCache)}.
+ */
public void setPersistentCache(SSLServerSessionCache persistentCache) {
this.persistentCache = persistentCache;
}
@Override
- protected void sessionRemoved(SSLSession session) {}
-
- @Override
- public SSLSession getSession(byte[] sessionId) {
- // First see if AbstractSessionContext can satisfy the request.
- SSLSession cachedSession = super.getSession(sessionId);
- if (cachedSession != null) {
- // This will already have gone through Platform#wrapSSLSession
- return cachedSession;
- }
-
- // Then check the persistent cache.
+ SslSessionWrapper getSessionFromPersistentCache(byte[] sessionId) {
if (persistentCache != null) {
byte[] data = persistentCache.getSessionData(sessionId);
if (data != null) {
- OpenSSLSessionImpl session = toSession(data, null, -1);
+ SslSessionWrapper session = SslSessionWrapper.newInstance(this, data, null, -1);
if (session != null && session.isValid()) {
- super.putSession(session);
- return Platform.wrapSSLSession(session);
+ cacheSession(session);
+ return session;
}
}
}
@@ -80,15 +72,18 @@
}
@Override
- void putSession(SSLSession session) {
- super.putSession(session);
-
- // TODO: In background thread.
+ void onBeforeAddSession(SslSessionWrapper session) {
+ // TODO: Do this in background thread.
if (persistentCache != null) {
- byte[] data = toBytes(session);
+ byte[] data = session.toBytes();
if (data != null) {
- persistentCache.putSessionData(session, data);
+ persistentCache.putSessionData(session.toSSLSession(), data);
}
}
}
+
+ @Override
+ void onBeforeRemoveSession(SslSessionWrapper session) {
+ // Do nothing.
+ }
}
diff --git a/common/src/main/java/org/conscrypt/SslSessionWrapper.java b/common/src/main/java/org/conscrypt/SslSessionWrapper.java
new file mode 100644
index 0000000..8758fe8
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/SslSessionWrapper.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License", "www.google.com", 443);
+ * 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.SSLUtils.SessionType.OPEN_SSL_WITH_OCSP;
+import static org.conscrypt.SSLUtils.SessionType.OPEN_SSL_WITH_TLS_SCT;
+import static org.conscrypt.SSLUtils.SessionType.isSupportedType;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.util.List;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.X509Certificate;
+
+/**
+ * A utility wrapper that abstracts operations on the underlying native SSL_SESSION instance.
+ *
+ * This is abstract only to support mocking for tests.
+ */
+abstract class SslSessionWrapper {
+ /**
+ * Creates a new instance. Since BoringSSL does not provide an API to get access to all
+ * session information via the SSL_SESSION, we get some values (e.g. peer certs) from
+ * the active session instead (i.e. the SSL object).
+ */
+ static SslSessionWrapper newInstance(NativeRef.SSL_SESSION ref, ActiveSession activeSession)
+ throws SSLPeerUnverifiedException {
+ AbstractSessionContext context = (AbstractSessionContext) activeSession.getSessionContext();
+ if (context instanceof ClientSessionContext) {
+ return new Impl(context, ref, activeSession.getPeerHost(),
+ activeSession.getPeerPort(), activeSession.getPeerCertificates(),
+ getOcspResponse(activeSession),
+ activeSession.getPeerSignedCertificateTimestamp());
+ }
+
+ // Server's will be cached by ID and won't have any of the extra fields.
+ return new Impl(context, ref, null, -1, null, null, null);
+ }
+
+ private static byte[] getOcspResponse(ActiveSession activeSession) {
+ List<byte[]> ocspResponseList = activeSession.getStatusResponses();
+ if (ocspResponseList.size() >= 1) {
+ return ocspResponseList.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new {@link SslSessionWrapper} instance from the provided serialized bytes, which
+ * were generated by {@link #toBytes()}.
+ *
+ * @return The new instance if successful. If unable to parse the bytes for any reason, returns
+ * {@code null}.
+ */
+ static SslSessionWrapper newInstance(
+ AbstractSessionContext context, byte[] data, String host, int port) {
+ ByteBuffer buf = ByteBuffer.wrap(data);
+ try {
+ int type = buf.getInt();
+ if (!isSupportedType(type)) {
+ throw new IOException("Unexpected type ID: " + type);
+ }
+
+ int length = buf.getInt();
+ checkRemaining(buf, length);
+
+ byte[] sessionData = new byte[length];
+ buf.get(sessionData);
+
+ int count = buf.getInt();
+ checkRemaining(buf, count);
+
+ java.security.cert.X509Certificate[] peerCerts =
+ new java.security.cert.X509Certificate[count];
+ for (int i = 0; i < count; i++) {
+ length = buf.getInt();
+ checkRemaining(buf, length);
+
+ byte[] certData = new byte[length];
+ buf.get(certData);
+ try {
+ peerCerts[i] = OpenSSLX509Certificate.fromX509Der(certData);
+ } catch (Exception e) {
+ throw new IOException("Can not read certificate " + i + "/" + count);
+ }
+ }
+
+ byte[] ocspData = null;
+ if (type >= OPEN_SSL_WITH_OCSP.value) {
+ // We only support one OCSP response now, but in the future
+ // we may support RFC 6961 which has multiple.
+ int countOcspResponses = buf.getInt();
+ checkRemaining(buf, countOcspResponses);
+
+ if (countOcspResponses >= 1) {
+ int ocspLength = buf.getInt();
+ checkRemaining(buf, ocspLength);
+
+ ocspData = new byte[ocspLength];
+ buf.get(ocspData);
+
+ // Skip the rest of the responses.
+ for (int i = 1; i < countOcspResponses; i++) {
+ ocspLength = buf.getInt();
+ checkRemaining(buf, ocspLength);
+ buf.position(buf.position() + ocspLength);
+ }
+ }
+ }
+
+ byte[] tlsSctData = null;
+ if (type == OPEN_SSL_WITH_TLS_SCT.value) {
+ int tlsSctDataLength = buf.getInt();
+ checkRemaining(buf, tlsSctDataLength);
+
+ if (tlsSctDataLength > 0) {
+ tlsSctData = new byte[tlsSctDataLength];
+ buf.get(tlsSctData);
+ }
+ }
+
+ if (buf.remaining() != 0) {
+ log(new AssertionError("Read entire session, but data still remains; rejecting"));
+ return null;
+ }
+
+ NativeRef.SSL_SESSION ref =
+ new NativeRef.SSL_SESSION(NativeCrypto.d2i_SSL_SESSION(sessionData));
+ return new Impl(context, ref, host, port, peerCerts, ocspData, tlsSctData);
+ } catch (IOException e) {
+ log(e);
+ return null;
+ } catch (BufferUnderflowException e) {
+ log(e);
+ return null;
+ }
+ }
+
+ abstract byte[] getId();
+
+ abstract boolean isValid();
+
+ abstract void offerToResume(SslWrapper ssl) throws SSLException;
+
+ abstract String getCipherSuite();
+
+ abstract String getProtocol();
+
+ abstract String getPeerHost();
+
+ abstract int getPeerPort();
+
+ /**
+ * Returns the OCSP stapled response. The returned array is not copied; the caller must
+ * either not modify the returned array or make a copy.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
+ * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
+ */
+ abstract byte[] getPeerOcspStapledResponse();
+
+ /**
+ * Returns the signed certificate timestamp (SCT) received from the peer. The returned array
+ * is not copied; the caller must either not modify the returned array or make a copy.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
+ */
+ abstract byte[] getPeerSignedCertificateTimestamp();
+
+ /**
+ * Converts the given session to bytes.
+ *
+ * @return session data as bytes or null if the session can't be converted
+ */
+ abstract byte[] toBytes();
+
+ /**
+ * Converts this object to a {@link SSLSession}. The returned session will support only a
+ * subset of the {@link SSLSession} API.
+ */
+ abstract SSLSession toSSLSession();
+
+ /**
+ * The session wrapper implementation.
+ */
+ private static final class Impl extends SslSessionWrapper {
+ private final NativeRef.SSL_SESSION ref;
+
+ // BoringSSL offers no API to obtain these values directly from the SSL_SESSION.
+ private final AbstractSessionContext context;
+ private final String host;
+ private final int port;
+ private final String protocol;
+ private final String cipherSuite;
+ private final java.security.cert.X509Certificate[] peerCertificates;
+ private final byte[] peerOcspStapledResponse;
+ private final byte[] peerSignedCertificateTimestamp;
+
+ private Impl(AbstractSessionContext context, NativeRef.SSL_SESSION ref, String host,
+ int port, java.security.cert.X509Certificate[] peerCertificates,
+ byte[] peerOcspStapledResponse, byte[] peerSignedCertificateTimestamp) {
+ this.context = context;
+ this.host = host;
+ this.port = port;
+ this.peerCertificates = peerCertificates;
+ this.peerOcspStapledResponse = peerOcspStapledResponse;
+ this.peerSignedCertificateTimestamp = peerSignedCertificateTimestamp;
+ this.protocol = NativeCrypto.SSL_SESSION_get_version(ref.context);
+ this.cipherSuite =
+ NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_SESSION_cipher(ref.context));
+ this.ref = ref;
+ }
+
+ @Override
+ byte[] getId() {
+ return NativeCrypto.SSL_SESSION_session_id(ref.context);
+ }
+
+ private long getCreationTime() {
+ return NativeCrypto.SSL_SESSION_get_time(ref.context);
+ }
+
+ @Override
+ boolean isValid() {
+ long creationTimeMillis = getCreationTime();
+ // Use the minimum of the timeout from the context and the session.
+ long timeoutMillis =
+ Math.max(0,
+ Math.min(context.getSessionTimeout(),
+ NativeCrypto.SSL_SESSION_get_timeout(ref.context)))
+ * 1000;
+ return (System.currentTimeMillis() - timeoutMillis) < creationTimeMillis;
+ }
+
+ @Override
+ void offerToResume(SslWrapper ssl) throws SSLException {
+ ssl.offerToResumeSession(ref.context);
+ }
+
+ @Override
+ String getCipherSuite() {
+ return cipherSuite;
+ }
+
+ @Override
+ String getProtocol() {
+ return protocol;
+ }
+
+ @Override
+ String getPeerHost() {
+ return host;
+ }
+
+ @Override
+ int getPeerPort() {
+ return port;
+ }
+
+ @Override
+ byte[] getPeerOcspStapledResponse() {
+ return peerOcspStapledResponse;
+ }
+
+ @Override
+ byte[] getPeerSignedCertificateTimestamp() {
+ return peerSignedCertificateTimestamp;
+ }
+
+ @Override
+ byte[] toBytes() {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream daos = new DataOutputStream(baos);
+
+ daos.writeInt(OPEN_SSL_WITH_TLS_SCT.value); // session type ID
+
+ // Session data.
+ byte[] data = NativeCrypto.i2d_SSL_SESSION(ref.context);
+ daos.writeInt(data.length);
+ daos.write(data);
+
+ // Certificates.
+ daos.writeInt(peerCertificates.length);
+
+ for (Certificate cert : peerCertificates) {
+ data = cert.getEncoded();
+ daos.writeInt(data.length);
+ daos.write(data);
+ }
+
+ if (peerOcspStapledResponse != null) {
+ daos.writeInt(1);
+ daos.writeInt(peerOcspStapledResponse.length);
+ daos.write(peerOcspStapledResponse);
+ } else {
+ daos.writeInt(0);
+ }
+
+ if (peerSignedCertificateTimestamp != null) {
+ daos.writeInt(peerSignedCertificateTimestamp.length);
+ daos.write(peerSignedCertificateTimestamp);
+ } else {
+ daos.writeInt(0);
+ }
+
+ // TODO: local certificates?
+
+ return baos.toByteArray();
+ } catch (IOException e) {
+ // TODO(nathanmittler): Better error handling?
+ System.err.println("Failed to convert saved SSL Session: " + e.getMessage());
+ return null;
+ } catch (CertificateEncodingException e) {
+ log(e);
+ return null;
+ }
+ }
+
+ @Override
+ SSLSession toSSLSession() {
+ return new SSLSession() {
+ @Override
+ public byte[] getId() {
+ return Impl.this.getId();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ return Impl.this.getCipherSuite();
+ }
+
+ @Override
+ public String getProtocol() {
+ return Impl.this.getProtocol();
+ }
+
+ @Override
+ public String getPeerHost() {
+ return Impl.this.getPeerHost();
+ }
+
+ @Override
+ public int getPeerPort() {
+ return Impl.this.getPeerPort();
+ }
+
+ @Override
+ public long getCreationTime() {
+ return Impl.this.getCreationTime();
+ }
+
+ @Override
+ public boolean isValid() {
+ return Impl.this.isValid();
+ }
+
+ // UNSUPPORTED OPERATIONS
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putValue(String s, Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeValue(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ private static void log(Throwable t) {
+ // TODO(nathanmittler): Better error handling?
+ System.out.println("Error inflating SSL session: "
+ + (t.getMessage() != null ? t.getMessage() : t.getClass().getName()));
+ }
+
+ private static void checkRemaining(ByteBuffer buf, int length) throws IOException {
+ if (length < 0) {
+ throw new IOException("Length is negative: " + length);
+ }
+ if (length > buf.remaining()) {
+ throw new IOException(
+ "Length of blob is longer than available: " + length + " > " + buf.remaining());
+ }
+ }
+}
diff --git a/common/src/main/java/org/conscrypt/SslWrapper.java b/common/src/main/java/org/conscrypt/SslWrapper.java
new file mode 100644
index 0000000..c03dbed
--- /dev/null
+++ b/common/src/main/java/org/conscrypt/SslWrapper.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 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 static org.conscrypt.NativeConstants.SSL_RECEIVED_SHUTDOWN;
+import static org.conscrypt.NativeConstants.SSL_SENT_SHUTDOWN;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.SocketTimeoutException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.Set;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import org.conscrypt.NativeCrypto.SSLHandshakeCallbacks;
+import org.conscrypt.SSLParametersImpl.AliasChooser;
+import org.conscrypt.SSLParametersImpl.PSKCallbacks;
+
+/**
+ * A utility wrapper that abstracts operations on the underlying native SSL instance.
+ */
+final class SslWrapper {
+ private final SSLParametersImpl parameters;
+ private final SSLHandshakeCallbacks handshakeCallbacks;
+ private final AliasChooser aliasChooser;
+ private final PSKCallbacks pskCallbacks;
+ private long ssl;
+
+ static SslWrapper newInstance(SSLParametersImpl parameters,
+ SSLHandshakeCallbacks handshakeCallbacks, AliasChooser chooser,
+ PSKCallbacks pskCallbacks) throws SSLException {
+ long ctx = parameters.getSessionContext().sslCtxNativePointer;
+ long ssl = NativeCrypto.SSL_new(ctx);
+ return new SslWrapper(ssl, parameters, handshakeCallbacks, chooser, pskCallbacks);
+ }
+
+ private SslWrapper(long ssl, SSLParametersImpl parameters,
+ SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser,
+ PSKCallbacks pskCallbacks) {
+ this.ssl = ssl;
+ this.parameters = parameters;
+ this.handshakeCallbacks = handshakeCallbacks;
+ this.aliasChooser = aliasChooser;
+ this.pskCallbacks = pskCallbacks;
+ }
+
+ long ssl() {
+ return ssl;
+ }
+
+ BioWrapper newBio() {
+ try {
+ return new BioWrapper();
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void offerToResumeSession(long sslSessionNativePointer) throws SSLException {
+ NativeCrypto.SSL_set_session(ssl, sslSessionNativePointer);
+ }
+
+ byte[] getSessionId() {
+ return NativeCrypto.SSL_session_id(ssl);
+ }
+
+ long getTime() {
+ return NativeCrypto.SSL_get_time(ssl);
+ }
+
+ long getTimeout() {
+ return NativeCrypto.SSL_get_timeout(ssl);
+ }
+
+ void setTimeout(long millis) {
+ NativeCrypto.SSL_set_timeout(ssl, millis);
+ }
+
+ String getCipherSuite() {
+ return NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_get_current_cipher(ssl));
+ }
+
+ OpenSSLX509Certificate[] getLocalCertificates() {
+ return OpenSSLX509Certificate.createCertChain(NativeCrypto.SSL_get_certificate(ssl));
+ }
+
+ OpenSSLX509Certificate[] getPeerCertificates() {
+ return OpenSSLX509Certificate.createCertChain(NativeCrypto.SSL_get_peer_cert_chain(ssl));
+ }
+
+ byte[] getPeerCertificateOcspData() {
+ return NativeCrypto.SSL_get_ocsp_response(ssl);
+ }
+
+ byte[] getPeerTlsSctData() {
+ return NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl);
+ }
+
+ /**
+ * @see NativeCrypto.SSLHandshakeCallbacks#clientPSKKeyRequested(String, byte[], byte[])
+ */
+ @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+ int clientPSKKeyRequested(String identityHint, byte[] identityBytesOut, byte[] key) {
+ PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
+ if (pskKeyManager == null) {
+ return 0;
+ }
+
+ String identity = pskCallbacks.chooseClientPSKIdentity(pskKeyManager, identityHint);
+ // Store identity in NULL-terminated modified UTF-8 representation into ientityBytesOut
+ byte[] identityBytes;
+ if (identity == null) {
+ identity = "";
+ identityBytes = EmptyArray.BYTE;
+ } else if (identity.isEmpty()) {
+ identityBytes = EmptyArray.BYTE;
+ } else {
+ try {
+ identityBytes = identity.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF-8 encoding not supported", e);
+ }
+ }
+ if (identityBytes.length + 1 > identityBytesOut.length) {
+ // Insufficient space in the output buffer
+ return 0;
+ }
+ if (identityBytes.length > 0) {
+ System.arraycopy(identityBytes, 0, identityBytesOut, 0, identityBytes.length);
+ }
+ identityBytesOut[identityBytes.length] = 0;
+
+ SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
+ byte[] secretKeyBytes = secretKey.getEncoded();
+ if (secretKeyBytes == null) {
+ return 0;
+ } else if (secretKeyBytes.length > key.length) {
+ // Insufficient space in the output buffer
+ return 0;
+ }
+ System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
+ return secretKeyBytes.length;
+ }
+
+ /**
+ * @see NativeCrypto.SSLHandshakeCallbacks#serverPSKKeyRequested(String, String, byte[])
+ */
+ @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+ int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+ PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
+ if (pskKeyManager == null) {
+ return 0;
+ }
+ SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
+ byte[] secretKeyBytes = secretKey.getEncoded();
+ if (secretKeyBytes == null) {
+ return 0;
+ } else if (secretKeyBytes.length > key.length) {
+ return 0;
+ }
+ System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
+ return secretKeyBytes.length;
+ }
+
+ void chooseClientCertificate(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+ throws SSLException, CertificateEncodingException {
+ Set<String> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes);
+ String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]);
+
+ X500Principal[] issuers;
+ if (asn1DerEncodedPrincipals == null) {
+ issuers = null;
+ } else {
+ issuers = new X500Principal[asn1DerEncodedPrincipals.length];
+ for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
+ issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
+ }
+ }
+ X509KeyManager keyManager = parameters.getX509KeyManager();
+ String alias = (keyManager != null)
+ ? aliasChooser.chooseClientAlias(keyManager, issuers, keyTypes)
+ : null;
+ setCertificate(alias);
+ }
+
+ void setCertificate(String alias) throws CertificateEncodingException, SSLException {
+ if (alias == null) {
+ return;
+ }
+ X509KeyManager keyManager = parameters.getX509KeyManager();
+ if (keyManager == null) {
+ return;
+ }
+ PrivateKey privateKey = keyManager.getPrivateKey(alias);
+ if (privateKey == null) {
+ return;
+ }
+ X509Certificate[] certificates = keyManager.getCertificateChain(alias);
+ if (certificates == null) {
+ return;
+ }
+ PublicKey publicKey = (certificates.length > 0) ? certificates[0].getPublicKey() : null;
+
+ /*
+ * Make sure we keep a reference to the OpenSSLX509Certificate by using
+ * this array. Otherwise, if they're not OpenSSLX509Certificate
+ * instances originally, they may be garbage collected before we
+ * complete our JNI calls.
+ */
+ OpenSSLX509Certificate[] openSslCerts = new OpenSSLX509Certificate[certificates.length];
+ long[] x509refs = new long[certificates.length];
+ for (int i = 0; i < certificates.length; i++) {
+ OpenSSLX509Certificate openSslCert =
+ OpenSSLX509Certificate.fromCertificate(certificates[i]);
+ openSslCerts[i] = openSslCert;
+ x509refs[i] = openSslCert.getContext();
+ }
+
+ // Note that OpenSSL says to use SSL_use_certificate before
+ // SSL_use_PrivateKey.
+ NativeCrypto.SSL_use_certificate(ssl, x509refs);
+
+ final OpenSSLKey key;
+ try {
+ key = OpenSSLKey.fromPrivateKeyForTLSStackOnly(privateKey, publicKey);
+ NativeCrypto.SSL_use_PrivateKey(ssl, key.getNativeRef());
+ } catch (InvalidKeyException e) {
+ throw new SSLException(e);
+ }
+
+ // We may not have access to all the information to check the private key
+ // if it's a wrapped platform key, so skip this check.
+ if (!key.isWrapped()) {
+ // Makes sure the set PrivateKey and X509Certificate refer to the same
+ // key by comparing the public values.
+ NativeCrypto.SSL_check_private_key(ssl);
+ }
+ }
+
+ String getVersion() {
+ return NativeCrypto.SSL_get_version(ssl);
+ }
+
+ boolean isReused() {
+ return NativeCrypto.SSL_session_reused(ssl);
+ }
+
+ String getRequestedServerName() {
+ return NativeCrypto.SSL_get_servername(ssl);
+ }
+
+ byte[] getTlsChannelId() throws SSLException {
+ return NativeCrypto.SSL_get_tls_channel_id(ssl);
+ }
+
+ void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOException {
+ boolean enableSessionCreation = parameters.getEnableSessionCreation();
+ if (!enableSessionCreation) {
+ NativeCrypto.SSL_set_session_creation_enabled(ssl, false);
+ }
+
+ // Allow servers to trigger renegotiation. Some inadvisable server
+ // configurations cause them to attempt to renegotiate during
+ // certain protocols.
+ NativeCrypto.SSL_accept_renegotiations(ssl);
+
+ if (isClient()) {
+ NativeCrypto.SSL_set_connect_state(ssl);
+
+ // Configure OCSP and CT extensions for client
+ NativeCrypto.SSL_enable_ocsp_stapling(ssl);
+ if (parameters.isCTVerificationEnabled(hostname)) {
+ NativeCrypto.SSL_enable_signed_cert_timestamps(ssl);
+ }
+ } else {
+ NativeCrypto.SSL_set_accept_state(ssl);
+
+ // Configure OCSP for server
+ if (parameters.getOCSPResponse() != null) {
+ NativeCrypto.SSL_enable_ocsp_stapling(ssl);
+ }
+ }
+
+ if (parameters.getEnabledProtocols().length == 0 && parameters.isEnabledProtocolsFiltered) {
+ throw new SSLHandshakeException("No enabled protocols; "
+ + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3
+ + " is no longer supported and was filtered from the list");
+ }
+ NativeCrypto.SSL_configure_alpn(ssl, isClient(), parameters.alpnProtocols);
+ NativeCrypto.setEnabledProtocols(ssl, parameters.enabledProtocols);
+ NativeCrypto.setEnabledCipherSuites(ssl, parameters.enabledCipherSuites);
+
+ // setup server certificates and private keys.
+ // clients will receive a call back to request certificates.
+ if (!isClient()) {
+ Set<String> keyTypes = new HashSet<String>();
+ for (long sslCipherNativePointer : NativeCrypto.SSL_get_ciphers(ssl)) {
+ String keyType = SSLUtils.getServerX509KeyType(sslCipherNativePointer);
+ if (keyType != null) {
+ keyTypes.add(keyType);
+ }
+ }
+ X509KeyManager keyManager = parameters.getX509KeyManager();
+ if (keyManager != null) {
+ for (String keyType : keyTypes) {
+ try {
+ setCertificate(aliasChooser.chooseServerAlias(keyManager, keyType));
+ } catch (CertificateEncodingException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ NativeCrypto.SSL_set_options(ssl, NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ if (parameters.sctExtension != null) {
+ NativeCrypto.SSL_set_signed_cert_timestamp_list(ssl, parameters.sctExtension);
+ }
+
+ if (parameters.ocspResponse != null) {
+ NativeCrypto.SSL_set_ocsp_response(ssl, parameters.ocspResponse);
+ }
+ }
+
+ enablePSKKeyManagerIfRequested();
+
+ if (parameters.useSessionTickets) {
+ NativeCrypto.SSL_clear_options(ssl, NativeConstants.SSL_OP_NO_TICKET);
+ } else {
+ NativeCrypto.SSL_set_options(
+ ssl, NativeCrypto.SSL_get_options(ssl) | NativeConstants.SSL_OP_NO_TICKET);
+ }
+
+ if (parameters.getUseSni() && AddressUtils.isValidSniHostname(hostname)) {
+ NativeCrypto.SSL_set_tlsext_host_name(ssl, hostname);
+ }
+
+ // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites
+ // with TLSv1 and SSLv3).
+ NativeCrypto.SSL_set_mode(ssl, NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING);
+
+ setCertificateValidation(ssl);
+ setTlsChannelId(channelIdPrivateKey);
+ }
+
+ // TODO(nathanmittler): Remove once after we switch to the engine socket.
+ void doHandshake(FileDescriptor fd, int timeoutMillis)
+ throws CertificateException, SocketTimeoutException, SSLException {
+ NativeCrypto.SSL_do_handshake(ssl, fd, handshakeCallbacks, timeoutMillis);
+ }
+
+ int doHandshake() throws IOException {
+ return NativeCrypto.ENGINE_SSL_do_handshake(ssl, handshakeCallbacks);
+ }
+
+ // TODO(nathanmittler): Remove once after we switch to the engine socket.
+ int read(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
+ throws IOException {
+ return NativeCrypto.SSL_read(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
+ }
+
+ // TODO(nathanmittler): Remove once after we switch to the engine socket.
+ void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
+ throws IOException {
+ NativeCrypto.SSL_write(ssl, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
+ }
+
+ @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+ private void enablePSKKeyManagerIfRequested() throws SSLException {
+ // Enable Pre-Shared Key (PSK) key exchange if requested
+ PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
+ if (pskKeyManager != null) {
+ boolean pskEnabled = false;
+ for (String enabledCipherSuite : parameters.enabledCipherSuites) {
+ if ((enabledCipherSuite != null) && (enabledCipherSuite.contains("PSK"))) {
+ pskEnabled = true;
+ break;
+ }
+ }
+ if (pskEnabled) {
+ if (isClient()) {
+ NativeCrypto.set_SSL_psk_client_callback_enabled(ssl, true);
+ } else {
+ NativeCrypto.set_SSL_psk_server_callback_enabled(ssl, true);
+ String identityHint = pskCallbacks.chooseServerPSKIdentityHint(pskKeyManager);
+ NativeCrypto.SSL_use_psk_identity_hint(ssl, identityHint);
+ }
+ }
+ }
+ }
+
+ private void setTlsChannelId(OpenSSLKey channelIdPrivateKey) throws SSLException {
+ if (!parameters.channelIdEnabled) {
+ return;
+ }
+
+ if (parameters.getUseClientMode()) {
+ // Client-side TLS Channel ID
+ if (channelIdPrivateKey == null) {
+ throw new SSLHandshakeException("Invalid TLS channel ID key specified");
+ }
+ NativeCrypto.SSL_set1_tls_channel_id(ssl, channelIdPrivateKey.getNativeRef());
+ } else {
+ // Server-side TLS Channel ID
+ NativeCrypto.SSL_enable_tls_channel_id(ssl);
+ }
+ }
+
+ private void setCertificateValidation(long sslNativePointer) throws SSLException {
+ // setup peer certificate verification
+ if (!isClient()) {
+ // needing client auth takes priority...
+ boolean certRequested;
+ if (parameters.getNeedClientAuth()) {
+ NativeCrypto.SSL_set_verify(sslNativePointer, NativeCrypto.SSL_VERIFY_PEER
+ | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+ certRequested = true;
+ // ... over just wanting it...
+ } else if (parameters.getWantClientAuth()) {
+ NativeCrypto.SSL_set_verify(sslNativePointer, NativeCrypto.SSL_VERIFY_PEER);
+ certRequested = true;
+ // ... and we must disable verification if we don't want client auth.
+ } else {
+ NativeCrypto.SSL_set_verify(sslNativePointer, NativeCrypto.SSL_VERIFY_NONE);
+ certRequested = false;
+ }
+
+ if (certRequested) {
+ X509TrustManager trustManager = parameters.getX509TrustManager();
+ X509Certificate[] issuers = trustManager.getAcceptedIssuers();
+ if (issuers != null && issuers.length != 0) {
+ byte[][] issuersBytes;
+ try {
+ issuersBytes = SSLUtils.encodeIssuerX509Principals(issuers);
+ } catch (CertificateEncodingException e) {
+ throw new SSLException("Problem encoding principals", e);
+ }
+ NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
+ }
+ }
+ }
+ }
+
+ void interrupt() {
+ NativeCrypto.SSL_interrupt(ssl);
+ }
+
+ // TODO(nathanmittler): Remove once after we switch to the engine socket.
+ void shutdown(FileDescriptor fd) throws IOException {
+ NativeCrypto.SSL_shutdown(ssl, fd, handshakeCallbacks);
+ }
+
+ void shutdown() throws IOException {
+ NativeCrypto.ENGINE_SSL_shutdown(ssl, handshakeCallbacks);
+ }
+
+ boolean wasShutdownReceived() {
+ return (NativeCrypto.SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) != 0;
+ }
+
+ boolean wasShutdownSent() {
+ return (NativeCrypto.SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) != 0;
+ }
+
+ int readDirectByteBuffer(long destAddress, int destLength)
+ throws IOException, CertificateException {
+ return NativeCrypto.ENGINE_SSL_read_direct(
+ ssl, destAddress, destLength, handshakeCallbacks);
+ }
+
+ int readArray(byte[] destJava, int destOffset, int destLength)
+ throws IOException, CertificateException {
+ return NativeCrypto.ENGINE_SSL_read_heap(
+ ssl, destJava, destOffset, destLength, handshakeCallbacks);
+ }
+
+ int writeDirectByteBuffer(long sourceAddress, int sourceLength) throws IOException {
+ return NativeCrypto.ENGINE_SSL_write_direct(
+ ssl, sourceAddress, sourceLength, handshakeCallbacks);
+ }
+
+ int writeArray(byte[] sourceJava, int sourceOffset, int sourceLength) throws IOException {
+ return NativeCrypto.ENGINE_SSL_write_heap(
+ ssl, sourceJava, sourceOffset, sourceLength, handshakeCallbacks);
+ }
+
+ int getPendingReadableBytes() {
+ return NativeCrypto.SSL_pending_readable_bytes(ssl);
+ }
+
+ int getMaxSealOverhead() {
+ return NativeCrypto.SSL_max_seal_overhead(ssl);
+ }
+
+ void close() {
+ NativeCrypto.SSL_free(ssl);
+ ssl = 0L;
+ }
+
+ boolean isClosed() {
+ return ssl == 0L;
+ }
+
+ int getError(int result) {
+ return NativeCrypto.SSL_get_error(ssl, result);
+ }
+
+ byte[] getAlpnSelectedProtocol() {
+ return NativeCrypto.SSL_get0_alpn_selected(ssl);
+ }
+
+ private boolean isClient() {
+ return parameters.getUseClientMode();
+ }
+
+ /**
+ * A utility wrapper that abstracts operations on the underlying native BIO instance.
+ */
+ final class BioWrapper {
+ private long bio;
+
+ private BioWrapper() throws SSLException {
+ this.bio = NativeCrypto.SSL_BIO_new(ssl);
+ }
+
+ int getPendingWrittenBytes() {
+ return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio);
+ }
+
+ int writeDirectByteBuffer(long address, int length) throws IOException {
+ return NativeCrypto.ENGINE_SSL_write_BIO_direct(
+ ssl, bio, address, length, handshakeCallbacks);
+ }
+
+ int writeArray(byte[] sourceJava, int sourceOffset, int sourceLength) throws IOException {
+ return NativeCrypto.ENGINE_SSL_write_BIO_heap(
+ ssl, bio, sourceJava, sourceOffset, sourceLength, handshakeCallbacks);
+ }
+
+ int readDirectByteBuffer(long destAddress, int destLength) throws IOException {
+ return NativeCrypto.ENGINE_SSL_read_BIO_direct(
+ ssl, bio, destAddress, destLength, handshakeCallbacks);
+ }
+
+ int readArray(byte[] destJava, int destOffset, int destLength) throws IOException {
+ return NativeCrypto.ENGINE_SSL_read_BIO_heap(
+ ssl, bio, destJava, destOffset, destLength, handshakeCallbacks);
+ }
+
+ void close() {
+ NativeCrypto.BIO_free_all(bio);
+ bio = 0L;
+ }
+ }
+}
diff --git a/constants/src/gen/cpp/generate_constants.cpp b/constants/src/gen/cpp/generate_constants.cpp
index 92b5c7f..5725abd 100644
--- a/constants/src/gen/cpp/generate_constants.cpp
+++ b/constants/src/gen/cpp/generate_constants.cpp
@@ -46,21 +46,8 @@
printf("package org.conscrypt;\n\n");
printf("final class NativeConstants {\n");
- printf(" static final boolean HAS_EVP_AEAD = %s;\n",
-#if defined(EVP_AEAD_DEFAULT_TAG_LENGTH)
- "true"
-#else
- "false"
-#endif
- );
-
#define CONST(x) \
printf(" static final int %s = %ld;\n", #x, (long int)(x))
-#define CONST_MINUS_1(x) printf(" static final int %s = -1;\n", #x)
- CONST(OPENSSL_EC_NAMED_CURVE);
-
- CONST(POINT_CONVERSION_COMPRESSED);
- CONST(POINT_CONVERSION_UNCOMPRESSED);
CONST(EXFLAG_CA);
CONST(EXFLAG_CRITICAL);
@@ -79,7 +66,6 @@
CONST(SSL_OP_CIPHER_SERVER_PREFERENCE);
CONST(SSL_OP_NO_TICKET);
- CONST(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
CONST(SSL_OP_NO_SSLv3);
CONST(SSL_OP_NO_TLSv1);
CONST(SSL_OP_NO_TLSv1_1);
@@ -96,43 +82,10 @@
CONST(TLS_CT_RSA_SIGN);
CONST(TLS_CT_ECDSA_SIGN);
-#if defined(TLS_CT_RSA_FIXED_DH)
- CONST(TLS_CT_RSA_FIXED_DH);
-#else
- CONST_MINUS_1(TLS_CT_RSA_FIXED_DH);
-#endif
-#if defined(TLS_CT_RSA_FIXED_ECDH)
- CONST(TLS_CT_RSA_FIXED_ECDH);
-#else
- CONST_MINUS_1(TLS_CT_RSA_FIXED_ECDH);
-#endif
-#if defined(TLS_CT_ECDSA_FIXED_ECDH)
- CONST(TLS_CT_ECDSA_FIXED_ECDH);
-#else
- CONST_MINUS_1(TLS_CT_ECDSA_FIXED_ECDH);
-#endif
-
CONST(SSL_VERIFY_NONE);
CONST(SSL_VERIFY_PEER);
CONST(SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
- CONST(SSL_ST_CONNECT);
- CONST(SSL_ST_ACCEPT);
- CONST(SSL_ST_MASK);
- CONST(SSL_ST_INIT);
- CONST(SSL_ST_OK);
- CONST(SSL_ST_RENEGOTIATE);
- CONST(SSL_CB_LOOP);
- CONST(SSL_CB_EXIT);
- CONST(SSL_CB_READ);
- CONST(SSL_CB_WRITE);
- CONST(SSL_CB_ALERT);
- CONST(SSL_CB_READ_ALERT);
- CONST(SSL_CB_WRITE_ALERT);
- CONST(SSL_CB_ACCEPT_LOOP);
- CONST(SSL_CB_ACCEPT_EXIT);
- CONST(SSL_CB_CONNECT_LOOP);
- CONST(SSL_CB_CONNECT_EXIT);
CONST(SSL_CB_HANDSHAKE_START);
CONST(SSL_CB_HANDSHAKE_DONE);
diff --git a/libcore-stub/build.gradle b/libcore-stub/build.gradle
index 92e5344..d1927de 100644
--- a/libcore-stub/build.gradle
+++ b/libcore-stub/build.gradle
@@ -1,12 +1,23 @@
description = 'Conscrypt: libcore Stub'
-dependencies {
- // Only compile against this. Other modules will embed the generated code directly.
- compileOnly project(':conscrypt-constants')
+configurations {
+ publicApiDocs
+}
- compile libraries.bouncycastle_provider,
- libraries.bouncycastle_apis,
- libraries.junit
+dependencies {
+ // This is used for the @hide annotation processing in JavaDoc
+ publicApiDocs project(':conscrypt-api-doclet')
+
+ // Only compile against this. Other modules will embed the generated code directly.
+ compileOnly project(':conscrypt-constants'),
+ configurations.publicApiDocs
+
+ compile libraries.junit
+}
+
+javadoc {
+ options.doclet = "org.conscrypt.doclet.FilterDoclet"
+ options.docletpath = configurations.publicApiDocs.files as List
}
// Don't include this artifact in the distribution.
diff --git a/openjdk-benchmarks/build.gradle b/openjdk-benchmarks/build.gradle
deleted file mode 100644
index 73a0809..0000000
--- a/openjdk-benchmarks/build.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-plugins {
- id 'me.champeau.gradle.jmh' version '0.3.1'
-}
-
-apply plugin: 'idea'
-
-description = 'Conscrypt: OpenJDK Benchmarks'
-
-evaluationDependsOn(':conscrypt-openjdk')
-
-def preferredNativeConfiguration = project(':conscrypt-openjdk').preferredNativeConfiguration
-def preferredNativeFileDir = project(':conscrypt-openjdk').preferredNativeFileDir
-
-sourceSets {
- main {
- resources {
- // This shouldn't be needed but seems to help IntelliJ locate the native artifact.
- srcDirs += preferredNativeFileDir
- }
- }
-}
-
-jmh {
- jmhVersion = "$jmhVersion"
- warmupIterations = 10
- iterations = 10
- fork = 1
- jvmArgs = '-server -Xms2g -Xmx2g'
- duplicateClassesStrategy = 'warn'
-}
-
-configurations {
- jmhGenerators
-}
-
-dependencies {
- compile project(':conscrypt-openjdk'),
- project(':conscrypt-testing'),
- libraries.junit,
- libraries.netty_handler,
- libraries.netty_tcnative
-
- // Add the preferred native openjdk configuration for this platform.
- compile project(path: ':conscrypt-openjdk', configuration: "$preferredNativeConfiguration")
-
- jmh libraries.jmh_core
-
- jmhGenerators libraries.jmh_generator_asm,
- libraries.jmh_generator_bytecode,
- libraries.jmh_generator_reflection,
- libraries.jmh_generator_annprocess
-}
-
-// Running benchmarks in IntelliJ seems broken without this.
-// See https://github.com/melix/jmh-gradle-plugin/issues/39
-idea.module {
- scopes.PROVIDED.plus += [ configurations.jmh, configurations.jmhGenerators ]
-}
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java
deleted file mode 100644
index d9db3f7..0000000
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.benchmarks;
-
-import static org.conscrypt.TestUtils.LOCALHOST;
-import static org.conscrypt.TestUtils.getConscryptServerSocketFactory;
-import static org.conscrypt.TestUtils.getConscryptSocketFactory;
-import static org.conscrypt.TestUtils.getJdkServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkSocketFactory;
-import static org.conscrypt.TestUtils.getProtocols;
-import static org.conscrypt.TestUtils.newTextMessage;
-import static org.conscrypt.TestUtils.pickUnusedPort;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import org.conscrypt.TestClient;
-import org.conscrypt.TestServer;
-import org.openjdk.jmh.annotations.AuxCounters;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.Fork;
-import org.openjdk.jmh.annotations.Level;
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.TearDown;
-
-/**
- * Benchmark for comparing performance of client socket implementations. All benchmarks use Netty
- * with tcnative as the server.
- */
-@State(Scope.Benchmark)
-@Fork(1)
-public class ClientSocketThroughputBenchmark {
- /**
- * Use an AuxCounter so we can measure that bytes per second as they accumulate without
- * consuming CPU in the benchmark method.
- */
- @AuxCounters
- @State(Scope.Thread)
- public static class BytesPerSecondCounter {
- @Setup(Level.Iteration)
- public void clean() {
- bytesCounter.set(0);
- }
-
- public long bytesPerSecond() {
- return bytesCounter.get();
- }
- }
-
- /**
- * Various factories for SSL sockets.
- */
- public enum SslProvider {
- JDK(getJdkSocketFactory(), getJdkServerSocketFactory()),
- CONSCRYPT(getConscryptSocketFactory(false), getConscryptServerSocketFactory(false)),
- CONSCRYPT_ENGINE(getConscryptSocketFactory(true), getConscryptServerSocketFactory(true)) {
- @Override
- SSLSocket newClientSocket(String host, int port, SSLSocketFactory socketFactory) throws IOException {
- return (SSLSocket) socketFactory.createSocket(
- SocketFactory.getDefault().createSocket(host, port), host, port, true);
- }
- };
-
- private final SSLSocketFactory clientSocketFactory;
- private final SSLServerSocketFactory serverSocketFactory;
-
- SslProvider(SSLSocketFactory clientSocketFactory, SSLServerSocketFactory serverSocketFactory) {
- this.clientSocketFactory = clientSocketFactory;
- this.serverSocketFactory = serverSocketFactory;
- }
-
- final SSLSocket newClientSocket(String host, int port, String cipher) {
- try {
- SSLSocket sslSocket = newClientSocket(host, port, clientSocketFactory);
- sslSocket.setEnabledProtocols(getProtocols());
- sslSocket.setEnabledCipherSuites(new String[] {cipher});
- return sslSocket;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- SSLSocket newClientSocket(String host, int port, SSLSocketFactory socketFactory) throws IOException {
- return (SSLSocket) socketFactory.createSocket(host, port);
- }
-
- final SSLServerSocket newServerSocket(String cipher) {
- try {
- int port = pickUnusedPort();
- SSLServerSocket sslSocket =
- (SSLServerSocket) serverSocketFactory.createServerSocket(port);
- sslSocket.setEnabledProtocols(getProtocols());
- sslSocket.setEnabledCipherSuites(new String[] {cipher});
- return sslSocket;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- @Param public SslProvider sslProvider;
-
- @Param({"64", "1024"}) public int messageSize;
-
- @Param({"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}) public String cipher;
-
- private TestClient client;
- private TestServer server;
- private byte[] message;
- private ExecutorService executor;
- private volatile boolean stopping;
-
- private static final AtomicLong bytesCounter = new AtomicLong();
- private AtomicBoolean recording = new AtomicBoolean();
-
- @Setup(Level.Trial)
- public void setup() throws Exception {
- recording.set(false);
-
- message = newTextMessage(messageSize);
-
- server = new TestServer(sslProvider.newServerSocket(cipher), messageSize);
- server.setMessageProcessor(new TestServer.MessageProcessor() {
- @Override
- public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
- if (recording.get()) {
- // Server received a message, increment the count.
- bytesCounter.addAndGet(numBytes);
- }
- }
- });
- Future<?> connectedFuture = server.start();
-
- client = new TestClient(sslProvider.newClientSocket(LOCALHOST, server.port(), cipher));
- client.start();
-
- // Wait for the initial connection to complete.
- connectedFuture.get(5, TimeUnit.SECONDS);
-
- executor = Executors.newSingleThreadExecutor();
- executor.submit(new Runnable() {
- @Override
- public void run() {
- Thread thread = Thread.currentThread();
- while (!stopping && !thread.isInterrupted()) {
- client.sendMessage(message);
- }
- }
- });
- }
-
- @TearDown(Level.Trial)
- public void teardown() throws Exception {
- stopping = true;
- client.stop();
- server.stop();
- executor.shutdown();
- executor.awaitTermination(5, TimeUnit.SECONDS);
- }
-
- @Benchmark
- public final void throughput(BytesPerSecondCounter counter) throws Exception {
- recording.set(true);
- // No need to do anything, just sleep here.
- Thread.sleep(1001);
- recording.set(false);
- }
-}
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java
deleted file mode 100644
index 8778da9..0000000
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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.benchmarks;
-
-import static org.conscrypt.TestUtils.LOCALHOST;
-import static org.conscrypt.TestUtils.getConscryptServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkSocketFactory;
-import static org.conscrypt.TestUtils.getProtocols;
-import static org.conscrypt.TestUtils.newTextMessage;
-import static org.conscrypt.TestUtils.pickUnusedPort;
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import org.conscrypt.TestClient;
-import org.conscrypt.TestServer;
-import org.conscrypt.TestServer.MessageProcessor;
-import org.openjdk.jmh.annotations.AuxCounters;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.Level;
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.TearDown;
-
-/**
- * Benchmark for comparing performance of server socket implementations. All benchmarks use the
- * standard JDK TLS implementation.
- */
-@State(Scope.Benchmark)
-public class ServerSocketThroughputBenchmark {
- /**
- * Use an AuxCounter so we can measure that bytes per second as they accumulate without
- * consuming CPU in the benchmark method.
- */
- @AuxCounters
- @State(Scope.Thread)
- public static class BytesPerSecondCounter {
- @Setup(Level.Iteration)
- public void clean() {
- bytesCounter.set(0);
- }
-
- public long bytesPerSecond() {
- return bytesCounter.get();
- }
- }
-
- /**
- * Various factories for SSL server sockets.
- */
- public enum SslProvider {
- JDK(getJdkServerSocketFactory()),
- CONSCRYPT(getConscryptServerSocketFactory(false)),
- CONSCRYPT_ENGINE(getConscryptServerSocketFactory(true));
-
- private final SSLServerSocketFactory serverSocketFactory;
-
- SslProvider(SSLServerSocketFactory serverSocketFactory) {
- this.serverSocketFactory = serverSocketFactory;
- }
-
- final SSLServerSocket newServerSocket(String cipher) {
- try {
- int port = pickUnusedPort();
- SSLServerSocket sslSocket =
- (SSLServerSocket) serverSocketFactory.createServerSocket(port);
- sslSocket.setEnabledProtocols(getProtocols());
- sslSocket.setEnabledCipherSuites(new String[] {cipher});
- return sslSocket;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- @Param public SslProvider sslProvider;
-
- @Param({"64", "1024"}) public int messageSize;
-
- @Param({"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}) public String cipher;
-
- private TestClient client;
- private TestServer server;
- private byte[] message;
- private ExecutorService executor;
- private volatile boolean stopping;
- private static final AtomicLong bytesCounter = new AtomicLong();
- private AtomicBoolean recording = new AtomicBoolean();
-
- @Setup
- public void setup() throws Exception {
- recording.set(false);
-
- message = newTextMessage(messageSize);
-
- server = new TestServer(sslProvider.newServerSocket(cipher), messageSize);
- server.setMessageProcessor(new MessageProcessor() {
- @Override
- public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
- try {
- while (!stopping) {
- os.write(inMessage, 0, numBytes);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- });
-
- Future<?> connectedFuture = server.start();
-
- SSLSocket socket;
- try {
- SSLSocketFactory socketFactory = getJdkSocketFactory();
- socket = (SSLSocket) socketFactory.createSocket(LOCALHOST, server.port());
- socket.setEnabledProtocols(getProtocols());
- socket.setEnabledCipherSuites(new String[] {cipher});
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- client = new TestClient(socket);
- client.start();
-
- // Wait for the initial connection to complete.
- connectedFuture.get(5, TimeUnit.SECONDS);
-
- // Start the server-side streaming by sending a message to the server.
- client.sendMessage(message);
- client.flush();
-
- executor = Executors.newSingleThreadExecutor();
- executor.submit(new Runnable() {
- @Override
- public void run() {
- Thread thread = Thread.currentThread();
- byte[] buffer = new byte[messageSize];
- while (!stopping && !thread.isInterrupted()) {
- int numBytes = client.readMessage(buffer);
- assertEquals(messageSize, numBytes);
-
- // Increment the message counter if we're recording.
- if (recording.get()) {
- bytesCounter.addAndGet(numBytes);
- }
- }
- }
- });
- }
-
- @TearDown
- public void teardown() throws Exception {
- stopping = true;
- client.stop();
- server.stop();
- executor.shutdown();
- executor.awaitTermination(5, TimeUnit.SECONDS);
- }
-
- @Benchmark
- public final void throughput(BytesPerSecondCounter counter) throws Exception {
- recording.set(true);
- // No need to do anything, just sleep here.
- Thread.sleep(1001);
- recording.set(false);
- }
-}
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
deleted file mode 100644
index 679dec1..0000000
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * Copyright 2017 The Netty Project
- *
- * The Netty Project licenses this file to you under the Apache License,
- * version 2.0 (the "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package org.conscrypt.benchmarks;
-
-import static java.lang.Math.max;
-import static org.conscrypt.TestUtils.PROTOCOL_TLS_V1_2;
-import static org.conscrypt.TestUtils.doEngineHandshake;
-import static org.conscrypt.TestUtils.initClientSslContext;
-import static org.conscrypt.TestUtils.initEngine;
-import static org.conscrypt.TestUtils.initServerSslContext;
-import static org.conscrypt.TestUtils.newTextMessage;
-import static org.junit.Assert.assertEquals;
-
-import io.netty.buffer.UnpooledByteBufAllocator;
-import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
-import java.nio.ByteBuffer;
-import java.security.KeyStore.PrivateKeyEntry;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.X509Certificate;
-import java.util.Collections;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import libcore.java.security.TestKeyStore;
-import org.conscrypt.OpenSSLProvider;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-
-/**
- * Benchmark comparing performance of various engine implementations to conscrypt.
- */
-@State(Scope.Benchmark)
-public class SslEngineBenchmark {
- public enum SslProvider {
- JDK {
- private final SSLContext clientContext = initClientSslContext(newContext());
- private final SSLContext serverContext = initServerSslContext(newContext());
-
- @Override
- SSLEngine newClientEngine(String cipher) {
- return initEngine(clientContext.createSSLEngine(), cipher, true);
- }
-
- @Override
- SSLEngine newServerEngine(String cipher) {
- return initEngine(serverContext.createSSLEngine(), cipher, false);
- }
-
- private SSLContext newContext() {
- try {
- return SSLContext.getInstance(PROTOCOL_TLS_V1_2);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
- },
- CONSCRYPT {
- private final SSLContext clientContext = initClientSslContext(newContext());
- private final SSLContext serverContext = initServerSslContext(newContext());
-
- @Override
- SSLEngine newClientEngine(String cipher) {
- return initEngine(clientContext.createSSLEngine(), cipher, true);
- }
-
- @Override
- SSLEngine newServerEngine(String cipher) {
- return initEngine(serverContext.createSSLEngine(), cipher, false);
- }
-
- private SSLContext newContext() {
- try {
- return SSLContext.getInstance(PROTOCOL_TLS_V1_2, new OpenSSLProvider());
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
- },
- NETTY {
- private final SslContext clientContext = newClientContext(null);
- private final SslContext serverContext = newServerContext(null);
-
- @Override
- SSLEngine newClientEngine(String cipher) {
- return initEngine(
- clientContext.newEngine(UnpooledByteBufAllocator.DEFAULT), cipher, true);
- }
-
- @Override
- SSLEngine newServerEngine(String cipher) {
- return initEngine(
- serverContext.newEngine(UnpooledByteBufAllocator.DEFAULT), cipher, false);
- }
- };
-
- abstract SSLEngine newClientEngine(String cipher);
- abstract SSLEngine newServerEngine(String cipher);
- }
-
- public enum BufferType {
- HEAP {
- @Override
- ByteBuffer newBuffer(int size) {
- return ByteBuffer.allocate(size);
- }
- },
- DIRECT {
- @Override
- ByteBuffer newBuffer(int size) {
- return ByteBuffer.allocateDirect(size);
- }
- };
-
- abstract ByteBuffer newBuffer(int size);
- }
-
- @Param public SslProvider sslProvider;
-
- @Param public BufferType bufferType;
-
- @Param({"64", "128", "512", "1024", "4096"}) public int messageSize;
-
- @Param({"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}) public String cipher;
-
- private SSLEngine clientEngine;
- private SSLEngine serverEngine;
-
- private ByteBuffer clientCleartextBuffer;
- private ByteBuffer encryptedBuffer;
- private ByteBuffer serverCleartextBuffer;
-
- @Setup
- public void setup() throws Exception {
- clientEngine = sslProvider.newClientEngine(cipher);
- serverEngine = sslProvider.newServerEngine(cipher);
-
- encryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
-
- // Generate the message to be sent from the client.
- serverCleartextBuffer = bufferType.newBuffer(
- max(messageSize, serverEngine.getSession().getApplicationBufferSize()));
- clientCleartextBuffer = bufferType.newBuffer(messageSize);
- clientCleartextBuffer.put(newTextMessage(messageSize));
- clientCleartextBuffer.flip();
-
- // Complete the initial TLS handshake.
- doEngineHandshake(clientEngine, serverEngine);
- }
-
- private static SslContext newClientContext(String cipher) {
- try {
- TestKeyStore server = TestKeyStore.getServer();
- SslContextBuilder ctx =
- SslContextBuilder.forClient()
- .sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL)
- .trustManager((X509Certificate[]) server.getPrivateKey("RSA", "RSA")
- .getCertificateChain());
- if (cipher != null) {
- ctx.ciphers(Collections.singletonList(cipher));
- }
- return ctx.build();
- } catch (SSLException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static SslContext newServerContext(String cipher) {
- try {
- PrivateKeyEntry server = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
- SslContextBuilder ctx =
- SslContextBuilder
- .forServer(server.getPrivateKey(),
- (X509Certificate[]) server.getCertificateChain())
- .sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL);
- if (cipher != null) {
- ctx.ciphers(Collections.singletonList(cipher));
- }
- return ctx.build();
- } catch (SSLException e) {
- throw new RuntimeException(e);
- }
- }
-
-
- /**
- * Simple benchmark that sends a single message from client to server.
- */
- @Benchmark
- public void sendMessage() throws SSLException {
- // Reset the buffers.
- clientCleartextBuffer.position(0);
- encryptedBuffer.clear();
- serverCleartextBuffer.clear();
-
- // Wrap the original message and create the encrypted data.
- SSLEngineResult wrapResult = clientEngine.wrap(clientCleartextBuffer, encryptedBuffer);
- if (wrapResult.getStatus() != SSLEngineResult.Status.OK) {
- throw new RuntimeException("Wrap returned unexpected result " + wrapResult);
- }
-
- // Unwrap the encrypted data and get back the original result.
- encryptedBuffer.flip();
- SSLEngineResult unwrapResult = serverEngine.unwrap(encryptedBuffer, serverCleartextBuffer);
- if (unwrapResult.getStatus() != SSLEngineResult.Status.OK) {
- throw new RuntimeException("Unwrap returned unexpected result " + wrapResult);
- }
- serverCleartextBuffer.flip();
-
- // Lightweight comparison - just make sure the unencrypted data length is correct.
- assertEquals(clientCleartextBuffer.limit(), serverCleartextBuffer.limit());
- }
-}
diff --git a/openjdk-integ-tests/build.gradle b/openjdk-integ-tests/build.gradle
index a4ca546..fb55ab6 100644
--- a/openjdk-integ-tests/build.gradle
+++ b/openjdk-integ-tests/build.gradle
@@ -24,6 +24,18 @@
project(':conscrypt-testing')
}
+// Add a second round of tests using the engine-based socket.
+task testEngineSocket(type: Test) {
+ dependsOn testClasses
+ // Use the engine socket by default.
+ jvmArgs "-Dorg.conscrypt.useEngineSocketByDefault=true"
+ testClassesDir = test.testClassesDir
+ doFirst {
+ classpath = test.classpath
+ }
+}
+test.dependsOn testEngineSocket
+
// Don't include this artifact in the distribution.
tasks.install.enabled = false
tasks.uploadArchives.enabled = false;
diff --git a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java
index 72de22f..91f00ba 100644
--- a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java
+++ b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java
@@ -39,14 +39,13 @@
@RunWith(JUnit4.class)
public class SSLSessionContextTest extends AbstractSSLTest {
-
@Test
public void test_SSLSessionContext_getIds() {
TestSSLContext c = TestSSLContext.create();
assertSSLSessionContextSize(0, c);
c.close();
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
assertSSLSessionContextSize(1, s.c);
Enumeration<byte[]> clientIds = s.c.clientContext.getClientSessionContext().getIds();
Enumeration<byte[]> serverIds = s.c.serverContext.getServerSessionContext().getIds();
@@ -83,7 +82,7 @@
assertNull(c.serverContext.getServerSessionContext().getSession(new byte[1]));
c.close();
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
SSLSessionContext client = s.c.clientContext.getClientSessionContext();
SSLSessionContext server = s.c.serverContext.getServerSessionContext();
byte[] clientId = client.getIds().nextElement();
@@ -110,7 +109,7 @@
c.serverContext.getServerSessionContext().getSessionCacheSize());
c.close();
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
assertEquals(expectedClientSessionCacheSize,
s.c.clientContext.getClientSessionContext().getSessionCacheSize());
assertEquals(expectedServerSessionCacheSize,
@@ -124,11 +123,9 @@
int expectedClientSessionCacheSize = expectedClientSslSessionCacheSize(c);
int expectedServerSessionCacheSize = expectedServerSslSessionCacheSize(c);
assertNoConnectSetSessionCacheSizeBehavior(
- expectedClientSessionCacheSize,
- c.clientContext.getClientSessionContext());
+ expectedClientSessionCacheSize, c.clientContext.getClientSessionContext());
assertNoConnectSetSessionCacheSizeBehavior(
- expectedServerSessionCacheSize,
- c.serverContext.getServerSessionContext());
+ expectedServerSessionCacheSize, c.serverContext.getServerSessionContext());
c.close();
}
@@ -147,15 +144,13 @@
@Test
public void test_SSLSessionContext_setSessionCacheSize_oneConnect() {
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
int expectedClientSessionCacheSize = expectedClientSslSessionCacheSize(s.c);
int expectedServerSessionCacheSize = expectedServerSslSessionCacheSize(s.c);
SSLSessionContext client = s.c.clientContext.getClientSessionContext();
SSLSessionContext server = s.c.serverContext.getServerSessionContext();
- assertEquals(expectedClientSessionCacheSize,
- client.getSessionCacheSize());
- assertEquals(expectedServerSessionCacheSize,
- server.getSessionCacheSize());
+ assertEquals(expectedClientSessionCacheSize, client.getSessionCacheSize());
+ assertEquals(expectedServerSessionCacheSize, server.getSessionCacheSize());
assertSSLSessionContextSize(1, s.c);
s.close();
}
@@ -212,11 +207,14 @@
String cipherSuite3 = uniqueCipherSuites.get(2);
List<SSLSocket[]> toClose = new ArrayList<>();
- toClose.add(TestSSLSocketPair.connect(c, new String[] {cipherSuite1}, null));
+ toClose.add(
+ TestSSLSocketPair.create(c).connect(new String[] {cipherSuite1}, null).sockets());
assertSSLSessionContextSize(1, c);
- toClose.add(TestSSLSocketPair.connect(c, new String[] {cipherSuite2}, null));
+ toClose.add(
+ TestSSLSocketPair.create(c).connect(new String[] {cipherSuite2}, null).sockets());
assertSSLSessionContextSize(2, c);
- toClose.add(TestSSLSocketPair.connect(c, new String[] {cipherSuite3}, null));
+ toClose.add(
+ TestSSLSocketPair.create(c).connect(new String[] {cipherSuite3}, null).sockets());
assertSSLSessionContextSize(3, c);
client.setSessionCacheSize(1);
@@ -224,14 +222,17 @@
assertEquals(1, client.getSessionCacheSize());
assertEquals(1, server.getSessionCacheSize());
assertSSLSessionContextSize(1, c);
- toClose.add(TestSSLSocketPair.connect(c, new String[] {cipherSuite1}, null));
+ toClose.add(
+ TestSSLSocketPair.create(c).connect(new String[] {cipherSuite1}, null).sockets());
assertSSLSessionContextSize(1, c);
client.setSessionCacheSize(2);
server.setSessionCacheSize(2);
- toClose.add(TestSSLSocketPair.connect(c, new String[] {cipherSuite2}, null));
+ toClose.add(
+ TestSSLSocketPair.create(c).connect(new String[] {cipherSuite2}, null).sockets());
assertSSLSessionContextSize(2, c);
- toClose.add(TestSSLSocketPair.connect(c, new String[] {cipherSuite3}, null));
+ toClose.add(
+ TestSSLSocketPair.create(c).connect(new String[] {cipherSuite3}, null).sockets());
assertSSLSessionContextSize(2, c);
for (SSLSocket[] pair : toClose) {
@@ -252,7 +253,7 @@
c.serverContext.getServerSessionContext().getSessionTimeout());
c.close();
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
assertEquals(expectedCacheTimeout,
s.c.clientContext.getClientSessionContext().getSessionTimeout());
assertEquals(expectedCacheTimeout,
@@ -287,7 +288,7 @@
}
c.close();
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
assertSSLSessionContextSize(1, s.c);
Thread.sleep(1000);
s.c.clientContext.getClientSessionContext().setSessionTimeout(1);
@@ -311,11 +312,10 @@
private static void assertSSLSessionContextSize(
int expected, SSLSessionContext s, boolean server) {
- int size = Collections.list(s.getIds()).size();
if (server && TestSSLContext.sslServerSocketSupportsSessionTickets()) {
- assertEquals(0, size);
+ assertEquals(0, numSessions(s));
} else {
- assertEquals(expected, size);
+ assertEquals(expected, numSessions(s));
}
}
@@ -331,6 +331,10 @@
return (isConscrypt(c.serverContext.getProvider())) ? 8 * 3600 : 24 * 3600;
}
+ private static int numSessions(SSLSessionContext s) {
+ return Collections.list(s.getIds()).size();
+ }
+
private boolean isConscrypt(Provider provider) {
return "AndroidOpenSSL".equals(provider.getName());
}
diff --git a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index e0c3656..928a50a 100644
--- a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -25,7 +25,8 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeTrue;
import java.io.ByteArrayInputStream;
@@ -94,6 +95,7 @@
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
@@ -121,7 +123,6 @@
import libcore.tlswire.util.TlsProtocolVersion;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -151,12 +152,14 @@
SSLConfigurationAsserts.assertSSLSocketDefaultConfiguration(
(SSLSocket) SSLSocketFactory.getDefault().createSocket());
}
+
@Test
public void test_SSLSocket_getSupportedCipherSuites_returnsCopies() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
assertNotSame(ssl.getSupportedCipherSuites(), ssl.getSupportedCipherSuites());
}
+
@Test
public void test_SSLSocket_getSupportedCipherSuites_connect() throws Exception {
// note the rare usage of non-RSA keys
@@ -183,6 +186,16 @@
@Override
protected SecretKey getKey(
String identityHint, String identity, Socket socket) {
+ return newKey();
+ }
+
+ @Override
+ protected SecretKey getKey(
+ String identityHint, String identity, SSLEngine engine) {
+ return newKey();
+ }
+
+ private SecretKey newKey() {
return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW");
}
});
@@ -221,10 +234,10 @@
}
String[] clientCipherSuiteArray =
new String[] {cipherSuite, StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION};
- SSLSocket[] pair = TestSSLSocketPair.connect(
- c, clientCipherSuiteArray, clientCipherSuiteArray);
- SSLSocket server = pair[0];
- SSLSocket client = pair[1];
+ TestSSLSocketPair socketPair = TestSSLSocketPair.create(c).connect(
+ clientCipherSuiteArray, clientCipherSuiteArray);
+ SSLSocket server = socketPair.server;
+ SSLSocket client = socketPair.client;
// Check that the client can read the message sent by the server
server.getOutputStream().write(serverToClient);
byte[] clientFromServer = new byte[serverToClient.length];
@@ -265,12 +278,14 @@
}
c.close();
}
+
@Test
public void test_SSLSocket_getEnabledCipherSuites_returnsCopies() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
assertNotSame(ssl.getEnabledCipherSuites(), ssl.getEnabledCipherSuites());
}
+
@Test
public void test_SSLSocket_setEnabledCipherSuites_storesCopy() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -281,6 +296,7 @@
array[0] = "Modified after having been set";
assertEquals(originalFirstElement, ssl.getEnabledCipherSuites()[0]);
}
+
@Test
public void test_SSLSocket_setEnabledCipherSuites() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -311,18 +327,21 @@
ssl.setEnabledCipherSuites(cipherSuites);
assertEquals(Arrays.asList(cipherSuites), Arrays.asList(ssl.getEnabledCipherSuites()));
}
+
@Test
public void test_SSLSocket_getSupportedProtocols_returnsCopies() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
assertNotSame(ssl.getSupportedProtocols(), ssl.getSupportedProtocols());
}
+
@Test
public void test_SSLSocket_getEnabledProtocols_returnsCopies() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
assertNotSame(ssl.getEnabledProtocols(), ssl.getEnabledProtocols());
}
+
@Test
public void test_SSLSocket_setEnabledProtocols_storesCopy() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -333,6 +352,7 @@
array[0] = "Modified after having been set";
assertEquals(originalFirstElement, ssl.getEnabledProtocols()[0]);
}
+
@Test
public void test_SSLSocket_setEnabledProtocols() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -375,6 +395,7 @@
}
}
}
+
@Test
public void test_SSLSocket_getSession() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -383,6 +404,7 @@
assertNotNull(session);
assertFalse(session.isValid());
}
+
@Test
public void test_SSLSocket_getHandshakeSession() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -390,6 +412,7 @@
SSLSession session = ssl.getHandshakeSession();
assertNull(session);
}
+
@Test
public void test_SSLSocket_startHandshake() throws Exception {
final TestSSLContext c = TestSSLContext.create();
@@ -410,8 +433,7 @@
assertNotNull(localCertificates);
TestKeyStore.assertChainLength(localCertificates);
assertNotNull(localCertificates[0]);
- TestSSLContext.assertServerCertificateChain(
- c.serverTrustManager, localCertificates);
+ TestSSLContext.assertServerCertificateChain(c.serverTrustManager, localCertificates);
TestSSLContext.assertCertificateInKeyStore(localCertificates[0], c.serverKeyStore);
return null;
});
@@ -442,6 +464,7 @@
return server.getSession().getId();
}
}
+
@Test
public void test_SSLSocket_confirmSessionReuse() throws Exception {
final TestSSLContext c = TestSSLContext.create();
@@ -472,6 +495,7 @@
assertTrue(Arrays.equals(clientSessionId1, clientSessionId2));
c.close();
}
+
@Test
public void test_SSLSocket_NoEnabledCipherSuites_Failure() throws Exception {
TestSSLContext c = TestSSLContext.newBuilder()
@@ -503,13 +527,14 @@
client.close();
c.close();
}
+
@Test
public void test_SSLSocket_startHandshake_noKeyStore() throws Exception {
TestSSLContext c = TestSSLContext.newBuilder()
- .useDefaults(false)
- .clientContext(SSLContext.getDefault())
- .serverContext(SSLContext.getDefault())
- .build();
+ .useDefaults(false)
+ .clientContext(SSLContext.getDefault())
+ .serverContext(SSLContext.getDefault())
+ .build();
SSLSocket client =
(SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
@@ -533,6 +558,7 @@
client.close();
c.close();
}
+
@Test
public void test_SSLSocket_startHandshake_noClientCertificate() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -550,6 +576,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_HandshakeCompletedListener() throws Exception {
final TestSSLContext c = TestSSLContext.create();
@@ -576,20 +603,16 @@
byte[] id = session.getId();
assertNotNull(id);
assertEquals(32, id.length);
- assertNotNull(c.clientContext.getClientSessionContext().getSession(id));
assertNotNull(cipherSuite);
- assertTrue(
- Arrays.asList(client.getEnabledCipherSuites()).contains(cipherSuite));
+ assertTrue(Arrays.asList(client.getEnabledCipherSuites()).contains(cipherSuite));
assertTrue(Arrays.asList(c.serverSocket.getEnabledCipherSuites())
.contains(cipherSuite));
assertNull(localCertificates);
assertNotNull(peerCertificates);
TestKeyStore.assertChainLength(peerCertificates);
assertNotNull(peerCertificates[0]);
- TestSSLContext.assertServerCertificateChain(
- c.clientTrustManager, peerCertificates);
- TestSSLContext.assertCertificateInKeyStore(
- peerCertificates[0], c.serverKeyStore);
+ TestSSLContext.assertServerCertificateChain(c.clientTrustManager, peerCertificates);
+ TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
assertNotNull(peerCertificateChain);
TestKeyStore.assertChainLength(peerCertificateChain);
assertNotNull(peerCertificateChain[0]);
@@ -614,8 +637,8 @@
});
client.startHandshake();
future.get();
- assertNotNull(c.serverContext.getServerSessionContext().getSession(
- client.getSession().getId()));
+ assertNotNull(
+ c.serverContext.getServerSessionContext().getSession(client.getSession().getId()));
synchronized (handshakeCompletedListenerCalled) {
while (!handshakeCompletedListenerCalled[0]) {
handshakeCompletedListenerCalled.wait();
@@ -633,6 +656,7 @@
actualException = ex;
}
}
+
@Test
public void test_SSLSocket_HandshakeCompletedListener_RuntimeException() throws Exception {
final Thread self = Thread.currentThread();
@@ -648,9 +672,7 @@
server.startHandshake();
return null;
});
- client.addHandshakeCompletedListener(event -> {
- throw expectedException;
- });
+ client.addHandshakeCompletedListener(event -> { throw expectedException; });
client.startHandshake();
future.get();
client.close();
@@ -659,6 +681,7 @@
assertSame(expectedException, test.actualException);
self.setUncaughtExceptionHandler(original);
}
+
@Test
public void test_SSLSocket_getUseClientMode() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -671,27 +694,38 @@
server.close();
c.close();
}
+
@Test
- public void test_SSLSocket_setUseClientMode() throws Exception {
- // client is client, server is server
+ public void testClientMode_normal() throws Exception {
+ // Client is client and server is server.
test_SSLSocket_setUseClientMode(true, false);
- // client is server, server is client
- test_SSLSocket_setUseClientMode(true, false);
- // both are client
- try {
- test_SSLSocket_setUseClientMode(true, true);
- fail();
- } catch (SSLProtocolException | SSLHandshakeException expected) {
- // Ignored.
- }
- // both are server
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testClientMode_reverse() throws Exception {
+ // Client is server and server is client.
+ test_SSLSocket_setUseClientMode(false, true);
+ }
+
+ @Test(expected = SSLHandshakeException.class)
+ public void testClientMode_bothClient() throws Exception {
+ test_SSLSocket_setUseClientMode(true, true);
+ }
+
+ @Test
+ public void testClientMode_bothServer() throws Exception {
try {
test_SSLSocket_setUseClientMode(false, false);
fail();
} catch (SocketTimeoutException expected) {
- // Ignored.
+ // Ignore
+ } catch (SSLHandshakeException expected) {
+ // Depending on the timing of the socket closures, this can happen as well.
+ assertTrue("Unexpected handshake error: " + expected.getMessage(),
+ expected.getMessage().toLowerCase().contains("connection closed"));
}
}
+
private void test_SSLSocket_setUseClientMode(
final boolean clientClientMode, final boolean serverClientMode) throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -723,10 +757,11 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_setUseClientMode_afterHandshake() throws Exception {
// can't set after handshake
- TestSSLSocketPair pair = TestSSLSocketPair.create();
+ TestSSLSocketPair pair = TestSSLSocketPair.create().connect();
try {
pair.server.setUseClientMode(false);
fail();
@@ -740,6 +775,7 @@
// Ignored.
}
}
+
@Test
public void test_SSLSocket_untrustedServer() throws Exception {
TestSSLContext c =
@@ -767,6 +803,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_clientAuth() throws Exception {
TestSSLContext c = TestSSLContext.create(
@@ -802,6 +839,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_clientAuth_bogusAlias() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -861,14 +899,17 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_clientAuth_OpaqueKey_RSA() throws Exception {
run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientCertificate());
}
+
@Test
public void test_SSLSocket_clientAuth_OpaqueKey_EC_RSA() throws Exception {
run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcRsaCertificate());
}
+
@Test
public void test_SSLSocket_clientAuth_OpaqueKey_EC_EC() throws Exception {
run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcEcCertificate());
@@ -1137,6 +1178,7 @@
return delegate;
}
}
+
@Test
public void test_SSLSocket_TrustManagerRuntimeException() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -1181,6 +1223,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_getEnableSessionCreation() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -1193,6 +1236,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_setEnableSessionCreation_server() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -1220,6 +1264,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_setEnableSessionCreation_client() throws Exception {
TestSSLContext c = TestSSLContext.create();
@@ -1247,6 +1292,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_getSSLParameters() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -1269,6 +1315,7 @@
p.setEndpointIdentificationAlgorithm("FOO");
assertEquals("FOO", p.getEndpointIdentificationAlgorithm());
}
+
@Test
public void test_SSLSocket_setSSLParameters() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
@@ -1314,9 +1361,10 @@
assertFalse(ssl.getWantClientAuth());
}
}
+
@Test
public void test_SSLSocket_close() throws Exception {
- TestSSLSocketPair pair = TestSSLSocketPair.create();
+ TestSSLSocketPair pair = TestSSLSocketPair.create().connect();
SSLSocket server = pair.server;
SSLSocket client = pair.client;
assertFalse(server.isClosed());
@@ -1433,12 +1481,14 @@
// because the peer has closed, but it shouldn't throw.
server.close();
}
+
@Test
public void test_SSLSocket_endpointIdentification_Success() throws Exception {
final TestSSLContext c = TestSSLContext.create();
SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
SSLParameters p = client.getSSLParameters();
p.setEndpointIdentificationAlgorithm("HTTPS");
+ client.setSSLParameters(p);
client.connect(new InetSocketAddress(c.host, c.port));
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
Future<Void> future = runAsync(() -> {
@@ -1470,6 +1520,7 @@
server.close();
c.close();
}
+
@Test
public void test_SSLSocket_endpointIdentification_Failure() throws Exception {
final TestSSLContext c = TestSSLContext.create();
@@ -1503,6 +1554,7 @@
}
}
}
+
@Test
public void test_SSLSocket_setSoTimeout_basic() throws Exception {
ServerSocket listening = new ServerSocket(0);
@@ -1522,6 +1574,7 @@
assertEquals(0, wrapping.getSoTimeout());
assertEquals(0, underlying.getSoTimeout());
}
+
@Test
public void test_SSLSocket_setSoTimeout_wrapper() throws Exception {
ServerSocket listening = new ServerSocket(0);
@@ -1543,6 +1596,7 @@
underlying.close();
listening.close();
}
+
@Test(expected = SocketTimeoutException.class)
public void test_SSLSocket_setSoWriteTimeout() throws Exception {
// Only run this test on Linux since it relies on non-posix methods.
@@ -1557,9 +1611,6 @@
SSLSocket client =
(SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
- Method writeTimeoutMethod = getWriteTimeoutSetter(client);
- assumeNotNull("Client socket does not support setting write timeout", writeTimeoutMethod);
-
// Try to make the client SO_SNDBUF size as small as possible
// (it can default to 512k or even megabytes). Note that
// socket(7) says that the kernel will double the request to
@@ -1578,8 +1629,9 @@
});
server.startHandshake();
- writeTimeoutMethod.invoke(client, 1);
try {
+ setWriteTimeout(client, 1);
+
// Add extra space to the write to exceed the send buffer
// size and cause the write to block.
final int extra = 1;
@@ -1592,37 +1644,45 @@
}
}
- private static Method getWriteTimeoutSetter(Object socket) {
- try {
- return socket.getClass().getDeclaredMethod("setSoWriteTimeout", int.class);
- } catch (Exception e) {
- return null;
- }
- }
-
- private static String osName() {
- return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
- }
-
- private static boolean isLinux() {
- return osName().startsWith("linux");
- }
-
- @Ignore("TODO(nmittler): Fix this.")
+ // TODO(nmittler): Conscrypt socket read may return -1 instead of SocketException.
@Test
- public void test_SSLSocket_interrupt() throws Exception {
+ public void test_SSLSocket_interrupt_readUnderlyingAndCloseUnderlying() throws Exception {
test_SSLSocket_interrupt_case(true, true);
+ }
+
+ // TODO(nmittler): Conscrypt socket read may return -1 instead of SocketException.
+ @Test
+ public void test_SSLSocket_interrupt_readUnderlyingAndCloseWrapper() throws Exception {
test_SSLSocket_interrupt_case(true, false);
+ }
+
+ // TODO(nmittler): FD socket gets stuck in read on Windows and OSX.
+ @Test
+ public void test_SSLSocket_interrupt_readWrapperAndCloseUnderlying() throws Exception {
test_SSLSocket_interrupt_case(false, true);
+ }
+
+ // TODO(nmittler): Conscrypt socket read may return -1 instead of SocketException.
+ @Test
+ public void test_SSLSocket_interrupt_readWrapperAndCloseWrapper() throws Exception {
test_SSLSocket_interrupt_case(false, false);
}
+
private void test_SSLSocket_interrupt_case(boolean readUnderlying, boolean closeUnderlying)
throws Exception {
final int readingTimeoutMillis = 5000;
TestSSLContext c = TestSSLContext.create();
final Socket underlying = new Socket(c.host, c.port);
- final SSLSocket clientWrapping = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
- underlying, c.host.getHostName(), c.port, false);
+ final SSLSocket clientWrapping =
+ (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+ underlying, c.host.getHostName(), c.port, true);
+
+ if (isConscryptFdSocket(clientWrapping) && !readUnderlying && closeUnderlying) {
+ // TODO(nmittler): FD socket gets stuck in the read on Windows and OSX.
+ assumeFalse("Skipping interrupt test on Windows", isWindows());
+ assumeFalse("Skipping interrupt test on OSX", isOsx());
+ }
+
SSLSocket server = (SSLSocket) c.serverSocket.accept();
// Start the handshake.
@@ -1646,12 +1706,10 @@
// Read from the socket.
try {
toRead.setSoTimeout(readingTimeoutMillis);
- final InputStream inputStream = toRead.getInputStream();
- @SuppressWarnings("unused")
- int value = inputStream.read();
+ toRead.getInputStream().read();
fail();
} catch (SocketException expected) {
- // Ignored.
+ // Expected
}
future.get();
@@ -1659,22 +1717,33 @@
underlying.close();
server.close();
}
+
/**
* b/7014266 Test to confirm that an SSLSocket.close() on one
* thread will interrupt another thread blocked reading on the same
* socket.
*/
+ // TODO(nmittler): Interrupts do not work with the engine-based socket.
@Test
- public void test_SSLSocket_interrupt_read() throws Exception {
+ public void test_SSLSocket_interrupt_read_withoutAutoClose() throws Exception {
final int readingTimeoutMillis = 5000;
TestSSLContext c = TestSSLContext.create();
final Socket underlying = new Socket(c.host, c.port);
final SSLSocket wrapping = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
underlying, c.host.getHostName(), c.port, false);
+
+ // TODO(nmittler): Interrupts do not work with the engine-based socket.
+ assumeFalse(isConscryptEngineSocket(wrapping));
+
Future<Void> clientFuture = runAsync(() -> {
wrapping.startHandshake();
- wrapping.setSoTimeout(readingTimeoutMillis);
- assertEquals(-1, wrapping.getInputStream().read());
+ try {
+ wrapping.setSoTimeout(readingTimeoutMillis);
+ wrapping.getInputStream().read();
+ fail();
+ } catch (SocketException expected) {
+ // Expected
+ }
return null;
});
SSLSocket server = (SSLSocket) c.serverSocket.accept();
@@ -1701,12 +1770,14 @@
}
wrapping.close();
+
clientFuture.get();
server.close();
}
+
@Test
public void test_TestSSLSocketPair_create() {
- TestSSLSocketPair test = TestSSLSocketPair.create();
+ TestSSLSocketPair test = TestSSLSocketPair.create().connect();
assertNotNull(test.c);
assertNotNull(test.server);
assertNotNull(test.client);
@@ -1720,6 +1791,7 @@
assertTrue(test.client.getSession().isValid());
test.close();
}
+
@Test
public void test_SSLSocket_ClientHello_record_size() throws Exception {
// This test checks the size of ClientHello of the default SSLSocket. TLS/SSL handshakes
@@ -1733,25 +1805,11 @@
protected SSLSocket configureSocket(SSLSocket socket) {
// Enable SNI extension on the socket (this is typically enabled by default)
// to increase the size of ClientHello.
- try {
- Method setHostname = socket.getClass().getMethod("setHostname", String.class);
- setHostname.invoke(socket, "sslsockettest.androidcts.google.com");
- } catch (NoSuchMethodException ignored) {
- // Ignored.
- } catch (Exception e) {
- throw new RuntimeException("Failed to enable SNI", e);
- }
+ setHostname(socket);
+
// Enable Session Tickets extension on the socket (this is typically enabled
// by default) to increase the size of ClientHello.
- try {
- Method setUseSessionTickets =
- socket.getClass().getMethod("setUseSessionTickets", boolean.class);
- setUseSessionTickets.invoke(socket, true);
- } catch (NoSuchMethodException ignored) {
- // Ignored.
- } catch (Exception e) {
- throw new RuntimeException("Failed to enable Session Tickets", e);
- }
+ enableSessionTickets(socket);
return socket;
}
};
@@ -1767,6 +1825,7 @@
+ " bytes");
}
}
+
@Test
public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
ForEachRunner.runNamed(sslSocketFactory -> {
@@ -1777,8 +1836,7 @@
// indicate that a certain TLS extension should be used.
HelloExtension renegotiationInfoExtension =
clientHello.findExtensionByType(HelloExtension.TYPE_RENEGOTIATION_INFO);
- if (renegotiationInfoExtension != null
- && renegotiationInfoExtension.data.length == 1
+ if (renegotiationInfoExtension != null && renegotiationInfoExtension.data.length == 1
&& renegotiationInfoExtension.data[0] == 0) {
cipherSuites = new String[clientHello.cipherSuites.size() + 1];
cipherSuites[clientHello.cipherSuites.size()] =
@@ -1793,6 +1851,7 @@
StandardNames.assertDefaultCipherSuites(cipherSuites);
}, getSSLSocketFactoriesToTest());
}
+
@Test
public void test_SSLSocket_ClientHello_supportedCurves() throws Exception {
ForEachRunner.runNamed(sslSocketFactory -> {
@@ -1814,6 +1873,7 @@
StandardNames.assertDefaultEllipticCurves(supportedCurves);
}, getSSLSocketFactoriesToTest());
}
+
@Test
public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
ForEachRunner.runNamed(sslSocketFactory -> {
@@ -1821,6 +1881,7 @@
assertEquals(TlsProtocolVersion.TLSv1_2, clientHello.clientVersion);
}, getSSLSocketFactoriesToTest());
}
+
@Test
public void test_SSLSocket_ClientHello_compressionMethods() throws Exception {
ForEachRunner.runNamed(sslSocketFactory -> {
@@ -1829,6 +1890,7 @@
clientHello.compressionMethods);
}, getSSLSocketFactoriesToTest());
}
+
@Test
public void test_SSLSocket_ClientHello_SNI() throws Exception {
ForEachRunner.runNamed(sslSocketFactory -> {
@@ -1843,8 +1905,7 @@
}
private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
throws NoSuchAlgorithmException, KeyManagementException {
- List<Pair<String, SSLSocketFactory>> result =
- new ArrayList<>();
+ List<Pair<String, SSLSocketFactory>> result = new ArrayList<>();
result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
@@ -1898,21 +1959,20 @@
listeningSocket = ServerSocketFactory.getDefault().createServerSocket(0);
final ServerSocket finalListeningSocket = listeningSocket;
// 2. (in background) Wait for an incoming connection and read its first chunk.
- final Future<byte[]> readFirstReceivedChunkFuture =
- runAsync(() -> {
- Socket socket = finalListeningSocket.accept();
- sockets[1] = socket;
- try {
- byte[] buffer = new byte[64 * 1024];
- int bytesRead = socket.getInputStream().read(buffer);
- if (bytesRead == -1) {
- throw new EOFException("Failed to read anything");
- }
- return Arrays.copyOf(buffer, bytesRead);
- } finally {
- closeQuietly(socket);
- }
- });
+ final Future<byte[]> readFirstReceivedChunkFuture = runAsync(() -> {
+ Socket socket = finalListeningSocket.accept();
+ sockets[1] = socket;
+ try {
+ byte[] buffer = new byte[64 * 1024];
+ int bytesRead = socket.getInputStream().read(buffer);
+ if (bytesRead == -1) {
+ throw new EOFException("Failed to read anything");
+ }
+ return Arrays.copyOf(buffer, bytesRead);
+ } finally {
+ closeQuietly(socket);
+ }
+ });
// 3. Create a client socket, connect it to the server socket, and start the TLS/SSL
// handshake.
runAsync((Callable<Void>) () -> {
@@ -1924,8 +1984,7 @@
// server socket receives a ClientHello.
try {
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(client,
- "localhost.localdomain", finalListeningSocket.getLocalPort(),
- true);
+ "localhost.localdomain", finalListeningSocket.getLocalPort(), true);
sslSocket.startHandshake();
fail();
return null;
@@ -1949,28 +2008,22 @@
@Test
public void test_SSLSocket_getPortWithSNI() throws Exception {
TestSSLContext context = TestSSLContext.create();
- try (SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory()
- .createSocket()) {
+ try (SSLSocket client =
+ (SSLSocket) context.clientContext.getSocketFactory().createSocket()) {
client.connect(new InetSocketAddress(context.host, context.port));
- try {
- // This is crucial to reproducing issue 18428603.
- Method setHostname = client.getClass().getMethod("setHostname", String.class);
- setHostname.invoke(client, "sslsockettest.androidcts.google.com");
- } catch (NoSuchMethodException ignored) {
- // Ignored.
- }
+ setHostname(client);
assertTrue(client.getPort() > 0);
} finally {
context.close();
}
}
+
@Test
public void test_SSLSocket_SNIHostName() throws Exception {
TestSSLContext c = TestSSLContext.create();
final SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
SSLParameters clientParams = client.getSSLParameters();
- clientParams.setServerNames(
- Collections.singletonList(new SNIHostName("www.example.com")));
+ clientParams.setServerNames(Collections.singletonList(new SNIHostName("www.example.com")));
client.setSSLParameters(clientParams);
SSLParameters serverParams = c.serverSocket.getSSLParameters();
serverParams.setSNIMatchers(
@@ -1996,6 +2049,7 @@
SNIHostName serverHostName = (SNIHostName) serverName;
assertEquals("www.example.com", serverHostName.getAsciiName());
}
+
@Test
public void test_SSLSocket_sendsTlsFallbackScsv_Fallback_Success() throws Exception {
TestSSLContext context = TestSSLContext.create();
@@ -2050,10 +2104,13 @@
server.close();
context.close();
}
+
private static void assertInappropriateFallbackIsCause(Throwable cause) {
- assertTrue(cause.getMessage(), cause.getMessage().contains("inappropriate fallback")
+ assertTrue(cause.getMessage(),
+ cause.getMessage().contains("inappropriate fallback")
|| cause.getMessage().contains("INAPPROPRIATE_FALLBACK"));
}
+
@Test
public void test_SSLSocket_sendsTlsFallbackScsv_InappropriateFallback_Failure()
throws Exception {
@@ -2098,6 +2155,7 @@
server.close();
context.close();
}
+
@Test
public void test_SSLSocket_ClientGetsAlertDuringHandshake_HasGoodExceptionMessage()
throws Exception {
@@ -2123,11 +2181,9 @@
int bytesRead = server.getInputStream().read(scratch);
// Write a bogus TLS alert:
// TLSv1.2 Record Layer: Alert (Level: Warning, Description: Protocol Version)
- server.getOutputStream().write(
- new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x46});
+ server.getOutputStream().write(new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x46});
// TLSv1.2 Record Layer: Alert (Level: Warning, Description: Close Notify)
- server.getOutputStream().write(
- new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00});
+ server.getOutputStream().write(new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00});
return null;
});
c.get(5, TimeUnit.SECONDS);
@@ -2137,6 +2193,7 @@
listener.close();
context.close();
}
+
@Test
public void test_SSLSocket_ServerGetsAlertDuringHandshake_HasGoodExceptionMessage()
throws Exception {
@@ -2203,12 +2260,10 @@
// Write a bogus TLS alert:
// TLSv1.2 Record Layer: Alert (Level: Warning, Description:
// Protocol Version)
- client.getOutputStream().write(
- new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x46});
+ client.getOutputStream().write(new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x46});
// TLSv1.2 Record Layer: Alert (Level: Warning, Description:
// Close Notify)
- client.getOutputStream().write(
- new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00});
+ client.getOutputStream().write(new byte[] {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00});
return null;
});
c.get(5, TimeUnit.SECONDS);
@@ -2217,6 +2272,7 @@
server.close();
context.close();
}
+
@Test
public void test_SSLSocket_SSLv3Unsupported() throws Exception {
TestSSLContext context = TestSSLContext.create();
@@ -2233,6 +2289,73 @@
}
}
+ private static void setWriteTimeout(Object socket, int timeout) {
+ Exception ex = null;
+ try {
+ Method method = socket.getClass().getMethod("setSoWriteTimeout", int.class);
+ method.setAccessible(true);
+ method.invoke(socket, timeout);
+ } catch (Exception e) {
+ ex = e;
+ }
+ // Engine-based socket currently has the method but throws UnsupportedOperationException.
+ assumeNoException("Client socket does not support setting write timeout", ex);
+ }
+
+ private static void setHostname(SSLSocket socket) {
+ try {
+ Method method = socket.getClass().getMethod("setHostname", String.class);
+ method.setAccessible(true);
+ method.invoke(socket, "sslsockettest.androidcts.google.com");
+ } catch (NoSuchMethodException ignored) {
+ // Ignored.
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable SNI", e);
+ }
+ }
+
+ private static void enableSessionTickets(SSLSocket socket) {
+ try {
+ Method method =
+ socket.getClass().getMethod("setUseSessionTickets", boolean.class);
+ method.setAccessible(true);
+ method.invoke(socket, true);
+ } catch (NoSuchMethodException ignored) {
+ // Ignored.
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable Session Tickets", e);
+ }
+ }
+
+ private static boolean isConscryptSocket(Socket socket) {
+ return isConscryptFdSocket(socket) || isConscryptEngineSocket(socket);
+ }
+
+ private static boolean isConscryptFdSocket(Socket socket) {
+ return "ConscryptFileDescriptorSocket".equals(socket.getClass().getSimpleName());
+ }
+
+ private static boolean isConscryptEngineSocket(Socket socket) {
+ return "ConscryptEngineSocket".equals(socket.getClass().getSimpleName());
+ }
+
+ private static String osName() {
+ return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
+ }
+
+ private static boolean isLinux() {
+ return osName().startsWith("linux");
+ }
+
+ private static boolean isWindows() {
+ return osName().startsWith("windows");
+ }
+
+ private static boolean isOsx() {
+ String name = osName();
+ return name.startsWith("macosx") || name.startsWith("osx");
+ }
+
private <T> Future<T> runAsync(Callable<T> callable) {
return executor.submit(callable);
}
diff --git a/openjdk/build.gradle b/openjdk/build.gradle
index 02777a7..cf7b8b6 100644
--- a/openjdk/build.gradle
+++ b/openjdk/build.gradle
@@ -117,7 +117,8 @@
// This is used for the @hide annotation processing in JavaDoc
publicApiDocs project(':conscrypt-api-doclet')
- compileOnly project(':conscrypt-constants')
+ compileOnly project(':conscrypt-constants'),
+ configurations.publicApiDocs
testCompile project(':conscrypt-constants'),
project(':conscrypt-testing'),
@@ -186,7 +187,7 @@
def testTaskName = "${testSourceSet.name}"
def javaExecutable
def javaArchFlag
- if (testSourceSet.name.endsWith(arch32Name)) {
+ if (testSourceSet.name.endsWith("${arch32Name}Test")) {
// 32-bit test
javaExecutable = javaExecutable32 != null ? javaExecutable32 : test.executable
javaArchFlag = '-d32'
@@ -268,11 +269,13 @@
cppCompiler.define "CONSCRYPT_OPENJDK"
// Set up 32-bit vs 64-bit build
+ def building64Bit = false
def libPath
if (targetPlatform.getArchitecture().getName() == "x86") {
libPath = "$boringssl32BuildDir"
} else if (targetPlatform.getArchitecture().getName() == "x86-64") {
libPath = "$boringssl64BuildDir"
+ building64Bit = true
} else {
throw new GradleException("Unknown architecture: " +
targetPlatform.getArchitecture().name)
@@ -281,7 +284,7 @@
if (toolChain in Clang || toolChain in Gcc) {
cppCompiler.args "-Wall",
"-fPIC",
- "-O2",
+ "-O3",
"-std=c++11",
"-I$jniSourceDir/main/include",
"-I$jniSourceDir/unbundled/include",
@@ -292,7 +295,7 @@
"-I$jdkIncludeDir/win32"
// Static link to BoringSSL
- linker.args "-O2",
+ linker.args "-O3",
"-fvisibility=hidden",
"-lstdc++",
libPath + "/ssl/libssl.a",
@@ -300,7 +303,9 @@
} else if (toolChain in VisualCpp) {
cppCompiler.define "DLL_EXPORT"
cppCompiler.define "WIN32_LEAN_AND_MEAN"
- cppCompiler.define "WIN64"
+ if (building64Bit) {
+ cppCompiler.define "WIN64"
+ }
cppCompiler.define "_WINDOWS"
cppCompiler.define "UNICODE"
cppCompiler.define "_UNICODE"
@@ -337,8 +342,8 @@
linker.args "-WX",
"ws2_32.lib",
"advapi32.lib",
- libPath + "\\ssl\\ssl.lib",
- libPath + "\\crypto\\crypto.lib"
+ "${libPath}\\ssl\\ssl.lib",
+ "${libPath}\\crypto\\crypto.lib"
}
}
diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java
index 090980a..4bba5c8 100644
--- a/openjdk/src/main/java/org/conscrypt/Platform.java
+++ b/openjdk/src/main/java/org/conscrypt/Platform.java
@@ -87,8 +87,8 @@
}
}
- static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
- return getFileDescriptor(openSSLSocketImpl);
+ static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
+ return getFileDescriptor(socket);
}
static String getCurveName(ECParameterSpec spec) {
@@ -117,7 +117,7 @@
@SuppressWarnings("unchecked")
public static void setSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLSocketImpl socket) {
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
try {
Method getUseCipherSuitesOrder =
@@ -147,7 +147,7 @@
@SuppressWarnings({"LiteralClassName", "rawtypes"})
public static void getSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLSocketImpl socket) {
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
try {
Method setUseCipherSuitesOrder =
@@ -171,7 +171,7 @@
@SuppressWarnings("unchecked")
public static void setSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
try {
Method getUseCipherSuitesOrder =
@@ -185,7 +185,7 @@
for (Object serverName : serverNames) {
if ((int) serverName.getClass().getMethod("getType").invoke(serverName)
== hostNameType) {
- engine.setSniHostname((String) serverName.getClass()
+ engine.setHostname((String) serverName.getClass()
.getMethod("getAsciiName")
.invoke(serverName));
break;
@@ -200,19 +200,19 @@
@SuppressWarnings({"LiteralClassName", "rawtypes"})
public static void getSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
try {
Method setUseCipherSuitesOrder =
SSLParameters.class.getMethod("setUseCipherSuitesOrder", boolean.class);
setUseCipherSuitesOrder.invoke(params, impl.getUseCipherSuitesOrder());
Method setServerNames = SSLParameters.class.getMethod("setServerNames", List.class);
- if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) {
+ if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
Constructor sniHostNameConstructor =
Class.forName("javax.net.ssl.SNIHostName").getConstructor(String.class);
setServerNames.invoke(params,
(Collections.singletonList(
- sniHostNameConstructor.newInstance(engine.getSniHostname()))));
+ sniHostNameConstructor.newInstance(engine.getHostname()))));
}
} catch (NoSuchMethodException ignored) {
} catch (IllegalAccessException ignored) {
@@ -234,7 +234,7 @@
}
static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
- OpenSSLSocketImpl socket) throws CertificateException {
+ AbstractConscryptSocket socket) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkClientTrusted(chain, authType, socket);
@@ -244,7 +244,7 @@
}
static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
- OpenSSLSocketImpl socket) throws CertificateException {
+ AbstractConscryptSocket socket) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, socket);
@@ -254,7 +254,7 @@
}
static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
- OpenSSLEngineImpl engine) throws CertificateException {
+ ConscryptEngine engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkClientTrusted(chain, authType, engine);
@@ -264,7 +264,7 @@
}
static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
- OpenSSLEngineImpl engine) throws CertificateException {
+ ConscryptEngine engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, engine);
@@ -362,15 +362,16 @@
* Pre-Java-8 backward compatibility.
*/
- static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
- return new OpenSSLExtendedSessionImpl(sslSession);
+ static SSLSession wrapSSLSession(ActiveSession sslSession) {
+ return new DelegatingExtendedSSLSession(sslSession);
}
@SuppressWarnings("unused")
static SSLSession unwrapSSLSession(SSLSession sslSession) {
- if (sslSession instanceof OpenSSLExtendedSessionImpl) {
- return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
+ if (sslSession instanceof DelegatingExtendedSSLSession) {
+ return ((DelegatingExtendedSSLSession) sslSession).getDelegate();
}
+
return sslSession;
}
diff --git a/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java
index 756e294..1cda6f9 100644
--- a/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java
+++ b/openjdk/src/test/java/org/conscrypt/AbstractSessionContextTest.java
@@ -1,7 +1,7 @@
/*
- * Copyright 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License", "www.google.com", 443);
+ * 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
*
@@ -17,649 +17,118 @@
package org.conscrypt;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
+import java.security.cert.Certificate;
import javax.net.ssl.SSLSession;
-import org.junit.After;
-import org.junit.BeforeClass;
+import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-@RunWith(JUnit4.class)
-public class AbstractSessionContextTest {
- /*
- * Taken from external/boringssl/src/ssl/ssl_test.cc: kOpenSSLSession is a
- * serialized SSL_SESSION.
- */
- private static final byte[] kOpenSSLSession = new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x05,
- (byte) 0xAA, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x02,
- (byte) 0x03, (byte) 0x03, (byte) 0x04, (byte) 0x02, (byte) 0xC0, (byte) 0x2F,
- (byte) 0x04, (byte) 0x20, (byte) 0x06, (byte) 0xE5, (byte) 0x0D, (byte) 0x67,
- (byte) 0x76, (byte) 0xAE, (byte) 0x18, (byte) 0x7E, (byte) 0x66, (byte) 0xDE,
- (byte) 0xA3, (byte) 0x5C, (byte) 0xF0, (byte) 0x2E, (byte) 0x43, (byte) 0x51,
- (byte) 0x2A, (byte) 0x60, (byte) 0x97, (byte) 0x19, (byte) 0xD3, (byte) 0x60,
- (byte) 0x5A, (byte) 0xF1, (byte) 0x93, (byte) 0xDD, (byte) 0xCB, (byte) 0x24,
- (byte) 0x57, (byte) 0x4C, (byte) 0x90, (byte) 0x90, (byte) 0x04, (byte) 0x30,
- (byte) 0x26, (byte) 0x5A, (byte) 0xE5, (byte) 0xCE, (byte) 0x40, (byte) 0x16,
- (byte) 0x04, (byte) 0xE5, (byte) 0xA2, (byte) 0x2E, (byte) 0x3F, (byte) 0xE3,
- (byte) 0x27, (byte) 0xBE, (byte) 0x83, (byte) 0xEE, (byte) 0x5F, (byte) 0x94,
- (byte) 0x5E, (byte) 0x88, (byte) 0xB3, (byte) 0x3F, (byte) 0x62, (byte) 0x88,
- (byte) 0xD8, (byte) 0x2E, (byte) 0xC8, (byte) 0xD8, (byte) 0x57, (byte) 0x1C,
- (byte) 0xA8, (byte) 0xC9, (byte) 0x88, (byte) 0x7C, (byte) 0x59, (byte) 0xA6,
- (byte) 0x91, (byte) 0x4C, (byte) 0xB7, (byte) 0xDA, (byte) 0x72, (byte) 0x09,
- (byte) 0xD2, (byte) 0x66, (byte) 0x47, (byte) 0x21, (byte) 0x6A, (byte) 0x09,
- (byte) 0xA1, (byte) 0x06, (byte) 0x02, (byte) 0x04, (byte) 0x54, (byte) 0x43,
- (byte) 0x3B, (byte) 0x8E, (byte) 0xA2, (byte) 0x04, (byte) 0x02, (byte) 0x02,
- (byte) 0x01, (byte) 0x2C, (byte) 0xA3, (byte) 0x82, (byte) 0x04, (byte) 0x7A,
- (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x76, (byte) 0x30, (byte) 0x82,
- (byte) 0x03, (byte) 0x5E, (byte) 0xA0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
- (byte) 0x02, (byte) 0x02, (byte) 0x08, (byte) 0x2B, (byte) 0xD7, (byte) 0x54,
- (byte) 0xBE, (byte) 0xC3, (byte) 0xD6, (byte) 0x4A, (byte) 0x55, (byte) 0x30,
- (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48,
- (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x05,
- (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x49, (byte) 0x31, (byte) 0x0B,
- (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
- (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x0A, (byte) 0x13, (byte) 0x0A, (byte) 0x47, (byte) 0x6F,
- (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x20, (byte) 0x49,
- (byte) 0x6E, (byte) 0x63, (byte) 0x31, (byte) 0x25, (byte) 0x30, (byte) 0x23,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x13,
- (byte) 0x1C, (byte) 0x47, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
- (byte) 0x65, (byte) 0x20, (byte) 0x49, (byte) 0x6E, (byte) 0x74, (byte) 0x65,
- (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x41,
- (byte) 0x75, (byte) 0x74, (byte) 0x68, (byte) 0x6F, (byte) 0x72, (byte) 0x69,
- (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x47, (byte) 0x32, (byte) 0x30,
- (byte) 0x1E, (byte) 0x17, (byte) 0x0D, (byte) 0x31, (byte) 0x34, (byte) 0x31,
- (byte) 0x30, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x30,
- (byte) 0x37, (byte) 0x35, (byte) 0x37, (byte) 0x5A, (byte) 0x17, (byte) 0x0D,
- (byte) 0x31, (byte) 0x35, (byte) 0x30, (byte) 0x31, (byte) 0x30, (byte) 0x36,
- (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
- (byte) 0x5A, (byte) 0x30, (byte) 0x68, (byte) 0x31, (byte) 0x0B, (byte) 0x30,
- (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
- (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13,
- (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x08, (byte) 0x0C, (byte) 0x0A, (byte) 0x43, (byte) 0x61, (byte) 0x6C,
- (byte) 0x69, (byte) 0x66, (byte) 0x6F, (byte) 0x72, (byte) 0x6E, (byte) 0x69,
- (byte) 0x61, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0C, (byte) 0x0D,
- (byte) 0x4D, (byte) 0x6F, (byte) 0x75, (byte) 0x6E, (byte) 0x74, (byte) 0x61,
- (byte) 0x69, (byte) 0x6E, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
- (byte) 0x77, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0A, (byte) 0x0C, (byte) 0x0A,
- (byte) 0x47, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65,
- (byte) 0x20, (byte) 0x49, (byte) 0x6E, (byte) 0x63, (byte) 0x31, (byte) 0x17,
- (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x03, (byte) 0x0C, (byte) 0x0E, (byte) 0x77, (byte) 0x77, (byte) 0x77,
- (byte) 0x2E, (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
- (byte) 0x65, (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x30,
- (byte) 0x82, (byte) 0x01, (byte) 0x22, (byte) 0x30, (byte) 0x0D, (byte) 0x06,
- (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7,
- (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00,
- (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x0F, (byte) 0x00, (byte) 0x30,
- (byte) 0x82, (byte) 0x01, (byte) 0x0A, (byte) 0x02, (byte) 0x82, (byte) 0x01,
- (byte) 0x01, (byte) 0x00, (byte) 0x9C, (byte) 0x29, (byte) 0xE2, (byte) 0xEB,
- (byte) 0xA6, (byte) 0x50, (byte) 0x02, (byte) 0xF8, (byte) 0xBA, (byte) 0x1F,
- (byte) 0xCB, (byte) 0xCB, (byte) 0x7F, (byte) 0xC0, (byte) 0x3C, (byte) 0x2D,
- (byte) 0x07, (byte) 0xA7, (byte) 0xAE, (byte) 0xEF, (byte) 0x60, (byte) 0x95,
- (byte) 0xA7, (byte) 0x47, (byte) 0x09, (byte) 0xE1, (byte) 0x5D, (byte) 0xE5,
- (byte) 0x92, (byte) 0x73, (byte) 0x7A, (byte) 0x86, (byte) 0xE1, (byte) 0xFD,
- (byte) 0x72, (byte) 0xDE, (byte) 0x85, (byte) 0x16, (byte) 0x4E, (byte) 0xF4,
- (byte) 0xA1, (byte) 0x12, (byte) 0x21, (byte) 0xFD, (byte) 0x50, (byte) 0x4D,
- (byte) 0x04, (byte) 0x1C, (byte) 0xFD, (byte) 0xD3, (byte) 0x48, (byte) 0xD8,
- (byte) 0xCB, (byte) 0xEE, (byte) 0xF5, (byte) 0xD7, (byte) 0x52, (byte) 0x66,
- (byte) 0xD5, (byte) 0xBF, (byte) 0x22, (byte) 0xA8, (byte) 0xE4, (byte) 0xD0,
- (byte) 0xF5, (byte) 0xA4, (byte) 0xF9, (byte) 0x0B, (byte) 0xB4, (byte) 0x84,
- (byte) 0x84, (byte) 0xD7, (byte) 0x10, (byte) 0x14, (byte) 0x9B, (byte) 0xEA,
- (byte) 0xCC, (byte) 0x7D, (byte) 0xDE, (byte) 0x30, (byte) 0xF9, (byte) 0x1B,
- (byte) 0xE9, (byte) 0x94, (byte) 0x96, (byte) 0x1A, (byte) 0x6D, (byte) 0x72,
- (byte) 0x18, (byte) 0x5E, (byte) 0xCC, (byte) 0x09, (byte) 0x04, (byte) 0xC6,
- (byte) 0x41, (byte) 0x71, (byte) 0x76, (byte) 0xD1, (byte) 0x29, (byte) 0x3F,
- (byte) 0x3B, (byte) 0x5E, (byte) 0x85, (byte) 0x4A, (byte) 0x30, (byte) 0x32,
- (byte) 0x9D, (byte) 0x4F, (byte) 0xDB, (byte) 0xDE, (byte) 0x82, (byte) 0x66,
- (byte) 0x39, (byte) 0xCB, (byte) 0x5C, (byte) 0xC9, (byte) 0xC5, (byte) 0x98,
- (byte) 0x91, (byte) 0x8D, (byte) 0x32, (byte) 0xB5, (byte) 0x2F, (byte) 0xE4,
- (byte) 0xDC, (byte) 0xB0, (byte) 0x6E, (byte) 0x21, (byte) 0xDE, (byte) 0x39,
- (byte) 0x3C, (byte) 0x96, (byte) 0xA8, (byte) 0x32, (byte) 0xA8, (byte) 0xC1,
- (byte) 0xD1, (byte) 0x6C, (byte) 0xA9, (byte) 0xAA, (byte) 0xF3, (byte) 0x5E,
- (byte) 0x24, (byte) 0x70, (byte) 0xB7, (byte) 0xAB, (byte) 0x92, (byte) 0x63,
- (byte) 0x08, (byte) 0x1E, (byte) 0x11, (byte) 0x3F, (byte) 0xB3, (byte) 0x5F,
- (byte) 0xC7, (byte) 0x98, (byte) 0xE3, (byte) 0x1D, (byte) 0x2A, (byte) 0xC2,
- (byte) 0x32, (byte) 0x1C, (byte) 0x3C, (byte) 0x95, (byte) 0x43, (byte) 0x16,
- (byte) 0xE0, (byte) 0x46, (byte) 0x83, (byte) 0xC6, (byte) 0x36, (byte) 0x91,
- (byte) 0xF4, (byte) 0xA0, (byte) 0xE1, (byte) 0x3C, (byte) 0xB8, (byte) 0x23,
- (byte) 0xB2, (byte) 0x4F, (byte) 0x8B, (byte) 0x0C, (byte) 0x8C, (byte) 0x92,
- (byte) 0x45, (byte) 0x24, (byte) 0x43, (byte) 0x68, (byte) 0x24, (byte) 0x06,
- (byte) 0x84, (byte) 0x43, (byte) 0x96, (byte) 0x2C, (byte) 0x96, (byte) 0x55,
- (byte) 0x2F, (byte) 0x32, (byte) 0xE8, (byte) 0xE0, (byte) 0xDE, (byte) 0xBF,
- (byte) 0x52, (byte) 0x57, (byte) 0x2D, (byte) 0x08, (byte) 0x71, (byte) 0x25,
- (byte) 0x96, (byte) 0x90, (byte) 0x54, (byte) 0x4A, (byte) 0xF1, (byte) 0x0E,
- (byte) 0xC8, (byte) 0x58, (byte) 0x1A, (byte) 0xE7, (byte) 0x6A, (byte) 0xAB,
- (byte) 0xA0, (byte) 0x68, (byte) 0xE0, (byte) 0xAD, (byte) 0xFD, (byte) 0xD6,
- (byte) 0x39, (byte) 0x0F, (byte) 0x76, (byte) 0xE4, (byte) 0xC1, (byte) 0x70,
- (byte) 0xCD, (byte) 0xDE, (byte) 0x80, (byte) 0x2B, (byte) 0xE2, (byte) 0x1C,
- (byte) 0x87, (byte) 0x48, (byte) 0x03, (byte) 0x46, (byte) 0x0F, (byte) 0x2C,
- (byte) 0x41, (byte) 0xF7, (byte) 0x4B, (byte) 0x1F, (byte) 0x93, (byte) 0xAE,
- (byte) 0x3F, (byte) 0x57, (byte) 0x1F, (byte) 0x2D, (byte) 0xF5, (byte) 0x35,
- (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xA3,
- (byte) 0x82, (byte) 0x01, (byte) 0x41, (byte) 0x30, (byte) 0x82, (byte) 0x01,
- (byte) 0x3D, (byte) 0x30, (byte) 0x1D, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x1D, (byte) 0x25, (byte) 0x04, (byte) 0x16, (byte) 0x30, (byte) 0x14,
- (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05,
- (byte) 0x05, (byte) 0x07, (byte) 0x03, (byte) 0x01, (byte) 0x06, (byte) 0x08,
- (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07,
- (byte) 0x03, (byte) 0x02, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x1D, (byte) 0x11, (byte) 0x04, (byte) 0x12, (byte) 0x30,
- (byte) 0x10, (byte) 0x82, (byte) 0x0E, (byte) 0x77, (byte) 0x77, (byte) 0x77,
- (byte) 0x2E, (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
- (byte) 0x65, (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x30,
- (byte) 0x68, (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01,
- (byte) 0x05, (byte) 0x05, (byte) 0x07, (byte) 0x01, (byte) 0x01, (byte) 0x04,
- (byte) 0x5C, (byte) 0x30, (byte) 0x5A, (byte) 0x30, (byte) 0x2B, (byte) 0x06,
- (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05, (byte) 0x05,
- (byte) 0x07, (byte) 0x30, (byte) 0x02, (byte) 0x86, (byte) 0x1F, (byte) 0x68,
- (byte) 0x74, (byte) 0x74, (byte) 0x70, (byte) 0x3A, (byte) 0x2F, (byte) 0x2F,
- (byte) 0x70, (byte) 0x6B, (byte) 0x69, (byte) 0x2E, (byte) 0x67, (byte) 0x6F,
- (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x2E, (byte) 0x63,
- (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x47, (byte) 0x49, (byte) 0x41,
- (byte) 0x47, (byte) 0x32, (byte) 0x2E, (byte) 0x63, (byte) 0x72, (byte) 0x74,
- (byte) 0x30, (byte) 0x2B, (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06,
- (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07, (byte) 0x30, (byte) 0x01,
- (byte) 0x86, (byte) 0x1F, (byte) 0x68, (byte) 0x74, (byte) 0x74, (byte) 0x70,
- (byte) 0x3A, (byte) 0x2F, (byte) 0x2F, (byte) 0x63, (byte) 0x6C, (byte) 0x69,
- (byte) 0x65, (byte) 0x6E, (byte) 0x74, (byte) 0x73, (byte) 0x31, (byte) 0x2E,
- (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65,
- (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x6F,
- (byte) 0x63, (byte) 0x73, (byte) 0x70, (byte) 0x30, (byte) 0x1D, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x0E, (byte) 0x04, (byte) 0x16,
- (byte) 0x04, (byte) 0x14, (byte) 0x3B, (byte) 0x6B, (byte) 0xE0, (byte) 0x9C,
- (byte) 0xC6, (byte) 0xC6, (byte) 0x41, (byte) 0xC8, (byte) 0xEA, (byte) 0x5C,
- (byte) 0xFB, (byte) 0x1A, (byte) 0x58, (byte) 0x15, (byte) 0xC2, (byte) 0x1B,
- (byte) 0x9D, (byte) 0x43, (byte) 0x19, (byte) 0x85, (byte) 0x30, (byte) 0x0C,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x13, (byte) 0x01,
- (byte) 0x01, (byte) 0xFF, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
- (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D,
- (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80,
- (byte) 0x14, (byte) 0x4A, (byte) 0xDD, (byte) 0x06, (byte) 0x16, (byte) 0x1B,
- (byte) 0xBC, (byte) 0xF6, (byte) 0x68, (byte) 0xB5, (byte) 0x76, (byte) 0xF5,
- (byte) 0x81, (byte) 0xB6, (byte) 0xBB, (byte) 0x62, (byte) 0x1A, (byte) 0xBA,
- (byte) 0x5A, (byte) 0x81, (byte) 0x2F, (byte) 0x30, (byte) 0x17, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x20, (byte) 0x04, (byte) 0x10,
- (byte) 0x30, (byte) 0x0E, (byte) 0x30, (byte) 0x0C, (byte) 0x06, (byte) 0x0A,
- (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x04, (byte) 0x01, (byte) 0xD6,
- (byte) 0x79, (byte) 0x02, (byte) 0x05, (byte) 0x01, (byte) 0x30, (byte) 0x30,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x1F, (byte) 0x04,
- (byte) 0x29, (byte) 0x30, (byte) 0x27, (byte) 0x30, (byte) 0x25, (byte) 0xA0,
- (byte) 0x23, (byte) 0xA0, (byte) 0x21, (byte) 0x86, (byte) 0x1F, (byte) 0x68,
- (byte) 0x74, (byte) 0x74, (byte) 0x70, (byte) 0x3A, (byte) 0x2F, (byte) 0x2F,
- (byte) 0x70, (byte) 0x6B, (byte) 0x69, (byte) 0x2E, (byte) 0x67, (byte) 0x6F,
- (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x2E, (byte) 0x63,
- (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x47, (byte) 0x49, (byte) 0x41,
- (byte) 0x47, (byte) 0x32, (byte) 0x2E, (byte) 0x63, (byte) 0x72, (byte) 0x6C,
- (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86,
- (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82, (byte) 0x01,
- (byte) 0x01, (byte) 0x00, (byte) 0x9A, (byte) 0x39, (byte) 0x70, (byte) 0x81,
- (byte) 0x76, (byte) 0x8A, (byte) 0x94, (byte) 0xCB, (byte) 0x96, (byte) 0xF1,
- (byte) 0xCA, (byte) 0xAF, (byte) 0x96, (byte) 0xAE, (byte) 0x1D, (byte) 0x73,
- (byte) 0xB3, (byte) 0x2C, (byte) 0x82, (byte) 0x16, (byte) 0x29, (byte) 0xB5,
- (byte) 0x3C, (byte) 0x7E, (byte) 0x55, (byte) 0x53, (byte) 0x6F, (byte) 0xB2,
- (byte) 0xBC, (byte) 0x34, (byte) 0x96, (byte) 0xAE, (byte) 0x00, (byte) 0xD8,
- (byte) 0xF2, (byte) 0x26, (byte) 0xD1, (byte) 0x18, (byte) 0x99, (byte) 0x9F,
- (byte) 0x7D, (byte) 0xFD, (byte) 0xEB, (byte) 0xE0, (byte) 0xBB, (byte) 0x9D,
- (byte) 0xE6, (byte) 0x46, (byte) 0xA5, (byte) 0x74, (byte) 0xAB, (byte) 0x3D,
- (byte) 0x93, (byte) 0xC6, (byte) 0x25, (byte) 0x28, (byte) 0x3D, (byte) 0xC8,
- (byte) 0x4C, (byte) 0x6E, (byte) 0xCF, (byte) 0xD1, (byte) 0x84, (byte) 0xFF,
- (byte) 0x46, (byte) 0x4F, (byte) 0x21, (byte) 0x2E, (byte) 0x07, (byte) 0xC4,
- (byte) 0xB8, (byte) 0xB7, (byte) 0x2A, (byte) 0xE5, (byte) 0xC7, (byte) 0x34,
- (byte) 0xC6, (byte) 0xA9, (byte) 0x84, (byte) 0xE3, (byte) 0x6C, (byte) 0x49,
- (byte) 0xF8, (byte) 0x4A, (byte) 0x36, (byte) 0xBB, (byte) 0x3A, (byte) 0xBD,
- (byte) 0xAD, (byte) 0x8A, (byte) 0x2B, (byte) 0x73, (byte) 0x97, (byte) 0xA6,
- (byte) 0x30, (byte) 0x2C, (byte) 0x5F, (byte) 0xE4, (byte) 0xBD, (byte) 0x13,
- (byte) 0x24, (byte) 0xE5, (byte) 0xD9, (byte) 0xA8, (byte) 0x74, (byte) 0x29,
- (byte) 0x38, (byte) 0x47, (byte) 0x2E, (byte) 0xA6, (byte) 0xD6, (byte) 0x50,
- (byte) 0xE0, (byte) 0xE8, (byte) 0xDD, (byte) 0x60, (byte) 0xC7, (byte) 0xD2,
- (byte) 0xC6, (byte) 0x4E, (byte) 0x54, (byte) 0xCE, (byte) 0xE7, (byte) 0x94,
- (byte) 0x84, (byte) 0x0D, (byte) 0xE8, (byte) 0x81, (byte) 0x92, (byte) 0x91,
- (byte) 0x71, (byte) 0x19, (byte) 0x1D, (byte) 0x07, (byte) 0x75, (byte) 0x9E,
- (byte) 0x59, (byte) 0x1A, (byte) 0x7E, (byte) 0x9D, (byte) 0x84, (byte) 0x61,
- (byte) 0xC7, (byte) 0x84, (byte) 0xAD, (byte) 0xA3, (byte) 0x6A, (byte) 0xED,
- (byte) 0xD8, (byte) 0x0D, (byte) 0x0C, (byte) 0x2A, (byte) 0x66, (byte) 0x3D,
- (byte) 0xD7, (byte) 0xAE, (byte) 0x46, (byte) 0x1D, (byte) 0x4A, (byte) 0x8C,
- (byte) 0x2B, (byte) 0xD6, (byte) 0x1A, (byte) 0x69, (byte) 0x71, (byte) 0xC3,
- (byte) 0x5E, (byte) 0xA0, (byte) 0x6E, (byte) 0xED, (byte) 0x27, (byte) 0x9F,
- (byte) 0xAF, (byte) 0x5B, (byte) 0x92, (byte) 0xA0, (byte) 0x03, (byte) 0xFD,
- (byte) 0x83, (byte) 0x22, (byte) 0x09, (byte) 0x29, (byte) 0xE8, (byte) 0xA1,
- (byte) 0x32, (byte) 0x2B, (byte) 0xEC, (byte) 0x1A, (byte) 0xA2, (byte) 0x75,
- (byte) 0x4C, (byte) 0x3E, (byte) 0x99, (byte) 0x71, (byte) 0xCE, (byte) 0x8B,
- (byte) 0x31, (byte) 0xEF, (byte) 0x9D, (byte) 0x37, (byte) 0x63, (byte) 0xFC,
- (byte) 0x71, (byte) 0x91, (byte) 0x10, (byte) 0x1E, (byte) 0xD0, (byte) 0xF5,
- (byte) 0xCB, (byte) 0x6F, (byte) 0x7A, (byte) 0xBA, (byte) 0x5E, (byte) 0x0C,
- (byte) 0x8A, (byte) 0xFA, (byte) 0xA4, (byte) 0xDE, (byte) 0x36, (byte) 0xAD,
- (byte) 0x51, (byte) 0x52, (byte) 0xFC, (byte) 0xFE, (byte) 0x10, (byte) 0xB0,
- (byte) 0x81, (byte) 0xC8, (byte) 0x7D, (byte) 0x03, (byte) 0xC3, (byte) 0xB8,
- (byte) 0x3C, (byte) 0x66, (byte) 0x6A, (byte) 0xF5, (byte) 0x6A, (byte) 0x81,
- (byte) 0x7C, (byte) 0x45, (byte) 0xA6, (byte) 0x23, (byte) 0x21, (byte) 0xE1,
- (byte) 0xD5, (byte) 0xD3, (byte) 0xED, (byte) 0x6E, (byte) 0x0D, (byte) 0x65,
- (byte) 0x39, (byte) 0x77, (byte) 0x58, (byte) 0x09, (byte) 0x6B, (byte) 0x63,
- (byte) 0xA4, (byte) 0x02, (byte) 0x04, (byte) 0x00, (byte) 0xA5, (byte) 0x03,
- (byte) 0x02, (byte) 0x01, (byte) 0x14, (byte) 0xA9, (byte) 0x05, (byte) 0x02,
- (byte) 0x03, (byte) 0x01, (byte) 0x89, (byte) 0xC0, (byte) 0xAA, (byte) 0x81,
- (byte) 0xA7, (byte) 0x04, (byte) 0x81, (byte) 0xA4, (byte) 0x1C, (byte) 0x14,
- (byte) 0x42, (byte) 0xFA, (byte) 0x1E, (byte) 0x3A, (byte) 0x4D, (byte) 0x0A,
- (byte) 0x83, (byte) 0x7E, (byte) 0x92, (byte) 0x61, (byte) 0x37, (byte) 0x0B,
- (byte) 0x12, (byte) 0x45, (byte) 0xEA, (byte) 0x2B, (byte) 0x03, (byte) 0x81,
- (byte) 0x7C, (byte) 0x5F, (byte) 0x6F, (byte) 0x13, (byte) 0x82, (byte) 0x97,
- (byte) 0xD0, (byte) 0xDC, (byte) 0x5E, (byte) 0x2F, (byte) 0x08, (byte) 0xDC,
- (byte) 0x0D, (byte) 0x3A, (byte) 0x6C, (byte) 0xBA, (byte) 0x1D, (byte) 0xEA,
- (byte) 0x5C, (byte) 0x46, (byte) 0x99, (byte) 0xF7, (byte) 0xDD, (byte) 0xAB,
- (byte) 0xD4, (byte) 0xDD, (byte) 0xFC, (byte) 0x54, (byte) 0x37, (byte) 0x32,
- (byte) 0x4B, (byte) 0xA3, (byte) 0xFB, (byte) 0x23, (byte) 0xA1, (byte) 0xC1,
- (byte) 0x60, (byte) 0xDF, (byte) 0x41, (byte) 0xB0, (byte) 0xD1, (byte) 0xCC,
- (byte) 0xDF, (byte) 0xAD, (byte) 0xB3, (byte) 0x66, (byte) 0x76, (byte) 0x36,
- (byte) 0xEC, (byte) 0x6A, (byte) 0x53, (byte) 0xC3, (byte) 0xE2, (byte) 0xB0,
- (byte) 0x77, (byte) 0xBE, (byte) 0x75, (byte) 0x08, (byte) 0xBA, (byte) 0x17,
- (byte) 0x14, (byte) 0xFA, (byte) 0x1A, (byte) 0x30, (byte) 0xE7, (byte) 0xB9,
- (byte) 0xED, (byte) 0xD6, (byte) 0xC1, (byte) 0xA5, (byte) 0x7A, (byte) 0x2B,
- (byte) 0xA3, (byte) 0xA3, (byte) 0xDD, (byte) 0xDC, (byte) 0x14, (byte) 0xDB,
- (byte) 0x7F, (byte) 0xF4, (byte) 0xF3, (byte) 0xAF, (byte) 0xCF, (byte) 0x0A,
- (byte) 0xD3, (byte) 0xAC, (byte) 0x84, (byte) 0x39, (byte) 0x30, (byte) 0xCA,
- (byte) 0x3C, (byte) 0xD8, (byte) 0xF7, (byte) 0xFA, (byte) 0x29, (byte) 0xDB,
- (byte) 0x31, (byte) 0xA5, (byte) 0x62, (byte) 0x82, (byte) 0xD2, (byte) 0xB8,
- (byte) 0x3C, (byte) 0xBC, (byte) 0x8F, (byte) 0xAB, (byte) 0xE4, (byte) 0xE8,
- (byte) 0xA7, (byte) 0x2C, (byte) 0xEF, (byte) 0xC7, (byte) 0xD5, (byte) 0x12,
- (byte) 0x16, (byte) 0x04, (byte) 0x6F, (byte) 0xCA, (byte) 0xEA, (byte) 0x31,
- (byte) 0x9F, (byte) 0x41, (byte) 0xE0, (byte) 0x6F, (byte) 0xE4, (byte) 0x74,
- (byte) 0x03, (byte) 0x78, (byte) 0xFA, (byte) 0x48, (byte) 0xB4, (byte) 0x6E,
- (byte) 0xC8, (byte) 0xE7, (byte) 0x40, (byte) 0x8B, (byte) 0x88, (byte) 0x2F,
- (byte) 0xED, (byte) 0x8E, (byte) 0x68, (byte) 0x96, (byte) 0x2C, (byte) 0xA7,
- (byte) 0xB6, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x00};
+public abstract class AbstractSessionContextTest<T extends AbstractSessionContext> {
+ private T context;
- private static final byte[] DUMMY_CERT =
- new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x58, (byte) 0x30,
- (byte) 0x82, (byte) 0x01, (byte) 0xC1, (byte) 0xA0, (byte) 0x03, (byte) 0x02,
- (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xFB,
- (byte) 0xB0, (byte) 0x4C, (byte) 0x2E, (byte) 0xAB, (byte) 0x10, (byte) 0x9B,
- (byte) 0x0C, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
- (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
- (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x45,
- (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
- (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0C, (byte) 0x0A,
- (byte) 0x53, (byte) 0x6F, (byte) 0x6D, (byte) 0x65, (byte) 0x2D, (byte) 0x53,
- (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21,
- (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x0A, (byte) 0x0C, (byte) 0x18, (byte) 0x49, (byte) 0x6E, (byte) 0x74,
- (byte) 0x65, (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x20,
- (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74,
- (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20,
- (byte) 0x4C, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x1E, (byte) 0x17,
- (byte) 0x0D, (byte) 0x31, (byte) 0x34, (byte) 0x30, (byte) 0x34, (byte) 0x32,
- (byte) 0x33, (byte) 0x32, (byte) 0x30, (byte) 0x35, (byte) 0x30, (byte) 0x34,
- (byte) 0x30, (byte) 0x5A, (byte) 0x17, (byte) 0x0D, (byte) 0x31, (byte) 0x37,
- (byte) 0x30, (byte) 0x34, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x30,
- (byte) 0x35, (byte) 0x30, (byte) 0x34, (byte) 0x30, (byte) 0x5A, (byte) 0x30,
- (byte) 0x45, (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02,
- (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0C,
- (byte) 0x0A, (byte) 0x53, (byte) 0x6F, (byte) 0x6D, (byte) 0x65, (byte) 0x2D,
- (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31,
- (byte) 0x21, (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x0A, (byte) 0x0C, (byte) 0x18, (byte) 0x49, (byte) 0x6E,
- (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74,
- (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69,
- (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79,
- (byte) 0x20, (byte) 0x4C, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x81,
- (byte) 0x9F, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
- (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
- (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81,
- (byte) 0x8D, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02,
- (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xD8, (byte) 0x2B, (byte) 0xC8,
- (byte) 0xA6, (byte) 0x32, (byte) 0xE4, (byte) 0x62, (byte) 0xFF, (byte) 0x4D,
- (byte) 0xF3, (byte) 0xD0, (byte) 0xAD, (byte) 0x59, (byte) 0x8B, (byte) 0x45,
- (byte) 0xA7, (byte) 0xBD, (byte) 0xF1, (byte) 0x47, (byte) 0xBF, (byte) 0x09,
- (byte) 0x58, (byte) 0x7B, (byte) 0x22, (byte) 0xBD, (byte) 0x35, (byte) 0xAE,
- (byte) 0x97, (byte) 0x25, (byte) 0x86, (byte) 0x94, (byte) 0xA0, (byte) 0x80,
- (byte) 0xC0, (byte) 0xB4, (byte) 0x1F, (byte) 0x76, (byte) 0x91, (byte) 0x67,
- (byte) 0x46, (byte) 0x31, (byte) 0xD0, (byte) 0x10, (byte) 0x84, (byte) 0xB7,
- (byte) 0x22, (byte) 0x1E, (byte) 0x70, (byte) 0x23, (byte) 0x91, (byte) 0x72,
- (byte) 0xC8, (byte) 0xE9, (byte) 0x6D, (byte) 0x79, (byte) 0x3A, (byte) 0x85,
- (byte) 0x77, (byte) 0x80, (byte) 0x0F, (byte) 0xC4, (byte) 0x95, (byte) 0x16,
- (byte) 0x75, (byte) 0xC5, (byte) 0x4A, (byte) 0x71, (byte) 0x4C, (byte) 0xC8,
- (byte) 0x63, (byte) 0x3F, (byte) 0xA3, (byte) 0xF2, (byte) 0x63, (byte) 0x9C,
- (byte) 0x2A, (byte) 0x4F, (byte) 0x9A, (byte) 0xFA, (byte) 0xCB, (byte) 0xC1,
- (byte) 0x71, (byte) 0x6E, (byte) 0x28, (byte) 0x85, (byte) 0x28, (byte) 0xA0,
- (byte) 0x27, (byte) 0x1E, (byte) 0x65, (byte) 0x1C, (byte) 0xAE, (byte) 0x07,
- (byte) 0xD5, (byte) 0x5B, (byte) 0x6F, (byte) 0x2D, (byte) 0x43, (byte) 0xED,
- (byte) 0x2B, (byte) 0x90, (byte) 0xB1, (byte) 0x8C, (byte) 0xAF, (byte) 0x24,
- (byte) 0x6D, (byte) 0xAE, (byte) 0xE9, (byte) 0x17, (byte) 0x3A, (byte) 0x05,
- (byte) 0xC1, (byte) 0xBF, (byte) 0xB8, (byte) 0x1C, (byte) 0xAE, (byte) 0x65,
- (byte) 0x3B, (byte) 0x1B, (byte) 0x58, (byte) 0xC2, (byte) 0xD9, (byte) 0xAE,
- (byte) 0xD6, (byte) 0xAA, (byte) 0x67, (byte) 0x88, (byte) 0xF1, (byte) 0x02,
- (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xA3, (byte) 0x50,
- (byte) 0x30, (byte) 0x4E, (byte) 0x30, (byte) 0x1D, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x1D, (byte) 0x0E, (byte) 0x04, (byte) 0x16, (byte) 0x04,
- (byte) 0x14, (byte) 0x8B, (byte) 0x75, (byte) 0xD5, (byte) 0xAC, (byte) 0xCB,
- (byte) 0x08, (byte) 0xBE, (byte) 0x0E, (byte) 0x1F, (byte) 0x65, (byte) 0xB7,
- (byte) 0xFA, (byte) 0x56, (byte) 0xBE, (byte) 0x6C, (byte) 0xA7, (byte) 0x75,
- (byte) 0xDA, (byte) 0x85, (byte) 0xAF, (byte) 0x30, (byte) 0x1F, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x23, (byte) 0x04, (byte) 0x18,
- (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x8B, (byte) 0x75,
- (byte) 0xD5, (byte) 0xAC, (byte) 0xCB, (byte) 0x08, (byte) 0xBE, (byte) 0x0E,
- (byte) 0x1F, (byte) 0x65, (byte) 0xB7, (byte) 0xFA, (byte) 0x56, (byte) 0xBE,
- (byte) 0x6C, (byte) 0xA7, (byte) 0x75, (byte) 0xDA, (byte) 0x85, (byte) 0xAF,
- (byte) 0x30, (byte) 0x0C, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D,
- (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01,
- (byte) 0x01, (byte) 0xFF, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09,
- (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D,
- (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
- (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x3B, (byte) 0xE8, (byte) 0x78,
- (byte) 0x6D, (byte) 0x95, (byte) 0xD6, (byte) 0x3D, (byte) 0x6A, (byte) 0xF7,
- (byte) 0x13, (byte) 0x19, (byte) 0x2C, (byte) 0x1B, (byte) 0xC2, (byte) 0x88,
- (byte) 0xAE, (byte) 0x22, (byte) 0xAB, (byte) 0xF4, (byte) 0x8D, (byte) 0x32,
- (byte) 0xF5, (byte) 0x7C, (byte) 0x71, (byte) 0x67, (byte) 0xCF, (byte) 0x2D,
- (byte) 0xD1, (byte) 0x1C, (byte) 0xC2, (byte) 0xC3, (byte) 0x87, (byte) 0xE2,
- (byte) 0xE9, (byte) 0xBE, (byte) 0x89, (byte) 0x5C, (byte) 0xE4, (byte) 0x34,
- (byte) 0xAB, (byte) 0x48, (byte) 0x91, (byte) 0xC2, (byte) 0x3F, (byte) 0x95,
- (byte) 0xAE, (byte) 0x2B, (byte) 0x47, (byte) 0x9E, (byte) 0x25, (byte) 0x78,
- (byte) 0x6B, (byte) 0x4F, (byte) 0x9A, (byte) 0x10, (byte) 0xA4, (byte) 0x72,
- (byte) 0xFD, (byte) 0xCF, (byte) 0xF7, (byte) 0x02, (byte) 0x0C, (byte) 0xB0,
- (byte) 0x0A, (byte) 0x08, (byte) 0xA4, (byte) 0x5A, (byte) 0xE2, (byte) 0xE5,
- (byte) 0x74, (byte) 0x7E, (byte) 0x11, (byte) 0x1D, (byte) 0x39, (byte) 0x60,
- (byte) 0x6A, (byte) 0xC9, (byte) 0x1F, (byte) 0x69, (byte) 0xF3, (byte) 0x2E,
- (byte) 0x63, (byte) 0x26, (byte) 0xDC, (byte) 0x9E, (byte) 0xEF, (byte) 0x6B,
- (byte) 0x7A, (byte) 0x0A, (byte) 0xE1, (byte) 0x54, (byte) 0x57, (byte) 0x98,
- (byte) 0xAA, (byte) 0x72, (byte) 0x91, (byte) 0x78, (byte) 0x04, (byte) 0x7E,
- (byte) 0x1F, (byte) 0x8F, (byte) 0x65, (byte) 0x4D, (byte) 0x1F, (byte) 0x0B,
- (byte) 0x12, (byte) 0xAC, (byte) 0x9C, (byte) 0x24, (byte) 0x0F, (byte) 0x84,
- (byte) 0x14, (byte) 0x1A, (byte) 0x55, (byte) 0x2D, (byte) 0x1F, (byte) 0xBB,
- (byte) 0xF0, (byte) 0x9D, (byte) 0x09, (byte) 0xB2, (byte) 0x08, (byte) 0x5C,
- (byte) 0x59, (byte) 0x32, (byte) 0x65, (byte) 0x80, (byte) 0x26};
-
- private static final byte[] DUMMY_OCSP_DATA = new byte[1];
-
- private static final byte[] DUMMY_TLS_SCT_DATA = new byte[1];
-
- private static ClientSessionContext clientCtx;
-
- @BeforeClass
- public static void setup() {
- clientCtx = new ClientSessionContext();
+ @Before
+ public void setup() {
+ context = newContext();
}
- @After
- public void tearDown() throws Exception {
- assertEquals(0, NativeCrypto.ERR_peek_last_error());
- }
+ abstract T newContext();
+ abstract int size(T context);
+ abstract SslSessionWrapper getCachedSession(T context, SslSessionWrapper s);
- private static TestSessionBuilder getType1() {
- return new TestSessionBuilder()
- .setType(0x01)
- .setSessionData(kOpenSSLSession)
- .addCertificate(DUMMY_CERT);
- }
+ @Test
+ public void testSimpleAddition() {
+ SslSessionWrapper a = newSession("a");
+ SslSessionWrapper b = newSession("b");
- private static TestSessionBuilder getType2() {
- return new TestSessionBuilder()
- .setType(0x02)
- .setSessionData(kOpenSSLSession)
- .addCertificate(DUMMY_CERT)
- .addOcspData(DUMMY_OCSP_DATA);
- }
+ context.cacheSession(a);
+ assertSessionContextContents(toArray(a), toArray(b));
- private static TestSessionBuilder getType3() {
- return new TestSessionBuilder()
- .setType(0x03)
- .setSessionData(kOpenSSLSession)
- .addCertificate(DUMMY_CERT)
- .addOcspData(DUMMY_OCSP_DATA)
- .setTlsSctData(DUMMY_TLS_SCT_DATA);
+ context.cacheSession(b);
+ assertSessionContextContents(toArray(a, b), toArray());
}
@Test
- public void toSession_EmptyArray_Invalid_Failure() throws Exception {
- assertInvalidSession(new byte[0]);
+ public void testTrimToSize() {
+ SslSessionWrapper a = newSession("a");
+ SslSessionWrapper b = newSession("b");
+ SslSessionWrapper c = newSession("c");
+ SslSessionWrapper d = newSession("d");
+
+ context.cacheSession(a);
+ context.cacheSession(b);
+ context.cacheSession(c);
+ context.cacheSession(d);
+ assertSessionContextContents(toArray(a, b, c, d), toArray());
+
+ context.setSessionCacheSize(2);
+ assertSessionContextContents(toArray(c, d), toArray(a, b));
}
@Test
- public void toSession_Type1_Valid_Success() throws Exception {
- assertValidSession(getType1().build());
+ public void testImplicitRemovalOfOldest() {
+ context.setSessionCacheSize(2);
+ SslSessionWrapper a = newSession("a");
+ SslSessionWrapper b = newSession("b");
+ SslSessionWrapper c = newSession("c");
+ SslSessionWrapper d = newSession("d");
+
+ context.cacheSession(a);
+ assertSessionContextContents(toArray(a), toArray(b, c, d));
+
+ context.cacheSession(b);
+ assertSessionContextContents(toArray(a, b), toArray(c, d));
+
+ context.cacheSession(c);
+ assertSessionContextContents(toArray(b, c), toArray(a, d));
+
+ context.cacheSession(d);
+ assertSessionContextContents(toArray(c, d), toArray(a, b));
}
@Test
- public void toSession_Type2_Valid_Success() throws Exception {
- assertValidSession(getType2().build());
+ public void testSerializeSession() throws Exception {
+ Certificate mockCert = mock(Certificate.class);
+ when(mockCert.getEncoded()).thenReturn(new byte[] {0x05, 0x06, 0x07, 0x10});
+
+ byte[] encodedBytes = new byte[] {0x01, 0x02, 0x03};
+ SslSessionWrapper session = new MockSessionBuilder()
+ .id(new byte[] {0x11, 0x09, 0x03, 0x20})
+ .host("ssl.example.com")
+ .encodedBytes(encodedBytes)
+ .build();
+
+ SSLClientSessionCache mockCache = mock(SSLClientSessionCache.class);
+ ClientSessionContext context = new ClientSessionContext();
+ context.setPersistentCache(mockCache);
+
+ context.cacheSession(session);
+ verify(mockCache).putSessionData(any(SSLSession.class), same(encodedBytes));
}
- @Test
- public void toSession_Type3_Valid_Success() throws Exception {
- assertValidSession(getType3().build());
- }
+ private void assertSessionContextContents(
+ SslSessionWrapper[] contains, SslSessionWrapper[] exludes) {
+ assertEquals(contains.length, size(context));
- private void assertTruncatedSessionFails(byte[] validSession) {
- for (int i = 0; i < validSession.length - 1; i++) {
- byte[] truncatedSession = new byte[i];
- System.arraycopy(validSession, 0, truncatedSession, 0, i);
- assertNull("Truncating to " + i + " bytes of " + validSession.length
- + " should not succeed",
- clientCtx.toSession(truncatedSession, "www.google.com", 443));
+ for (SslSessionWrapper s : contains) {
+ assertSame(s.getPeerHost(), s, getCachedSession(context, s));
+ }
+ for (SslSessionWrapper s : exludes) {
+ assertNull(s.getPeerHost(), getCachedSession(context, s));
}
}
- @Test
- public void toSession_Type3_Truncated_Failure() throws Exception {
- assertTruncatedSessionFails(getType3().build());
+ private static SslSessionWrapper[] toArray(SslSessionWrapper... sessions) {
+ return sessions;
}
- private static void assertValidSession(byte[] data) {
- assertNotNull(clientCtx.toSession(data, "www.google.com", 443));
- }
-
- private static void assertInvalidSession(byte[] data) {
- assertNull(clientCtx.toSession(data, "www.google.com", 443));
- }
-
- @Test
- public void toSession_UnknownType_Failure() throws Exception {
- assertInvalidSession(getType3().setType((byte) 0xEE).build());
- }
-
- @Test
- public void toSession_CertificatesCountTooLarge_Failure() throws Exception {
- assertInvalidSession(getType3().setCertificatesLength(16834).build());
- }
-
- @Test
- public void toSession_CertificatesCountNegative_Failure() throws Exception {
- assertInvalidSession(getType3().setCertificatesLength(-1).build());
- }
-
- @Test
- public void toSession_CertificateSizeNegative_Failure() throws Exception {
- assertInvalidSession(getType3().setCertificateLength(0, -1).build());
- }
-
- @Test
- public void toSession_CertificateSizeTooLarge_Failure() throws Exception {
- assertInvalidSession(getType3().setCertificateLength(0, 16834).build());
- }
-
- @Test
- public void toSession_SessionDataSizeTooLarge_Failure() throws Exception {
- assertInvalidSession(getType3().setSessionDataLength(16834).build());
- }
-
- @Test
- public void toSession_SessionDataSizeNegative_Failure() throws Exception {
- assertInvalidSession(getType3().setSessionDataLength(-1).build());
- }
-
- @Test
- public void toSession_OcspDatasNumberTooMany_Failure() throws Exception {
- assertInvalidSession(getType3().setOcspDatasLength(32791).build());
- }
-
- @Test
- public void toSession_OcspDatasNumberNegative_Failure() throws Exception {
- assertInvalidSession(getType3().setOcspDatasLength(-1).build());
- }
-
- @Test
- public void toSession_OcspDataSizeNegative_Failure() throws Exception {
- assertInvalidSession(getType3().setOcspDataLength(0, -1).build());
- }
-
- @Test
- public void toSession_OcspDataSizeTooLarge_Failure() throws Exception {
- assertInvalidSession(getType3().setOcspDataLength(0, 92948).build());
- }
-
- @Test
- public void toSession_TlsSctDataSizeNegative_Failure() throws Exception {
- assertInvalidSession(getType3().setTlsSctDataLength(-1).build());
- }
-
- @Test
- public void toSession_TlsSctDataSizeTooLarge_Failure() throws Exception {
- assertInvalidSession(getType3().setTlsSctDataLength(931148).build());
- }
-
- @Test
- public void toSession_Type2OcspDataEmpty_Success() throws Exception {
- assertValidSession(getType1().setType(0x02).setOcspDataEmpty().build());
- }
-
- @Test
- public void toSession_Type3TlsSctDataEmpty_Success() throws Exception {
- assertValidSession(getType2().setType(0x03).setTlsSctDataEmpty().build());
- }
-
- @Test
- public void toSession_Type3OcspAndTlsSctDataEmpty_Success() throws Exception {
- assertValidSession(
- getType1().setType(0x03).setOcspDataEmpty().setTlsSctDataEmpty().build());
- }
-
- private static void assertTrailingDataFails(byte[] validSession) {
- byte[] invalidSession = new byte[validSession.length + 1];
- System.arraycopy(validSession, 0, invalidSession, 0, validSession.length);
- assertInvalidSession(invalidSession);
- }
-
- @Test
- public void toSession_Type1TrailingData_Failure() throws Exception {
- assertTrailingDataFails(getType1().build());
- }
-
- @Test
- public void toSession_Type2TrailingData_Failure() throws Exception {
- assertTrailingDataFails(getType2().build());
- }
-
- @Test
- public void toSession_Type3TrailingData_Failure() throws Exception {
- assertTrailingDataFails(getType3().build());
- }
-
- @Test
- public void test_reserializableFromByteArray_roundTrip_type1() throws Exception {
- // Converting OPEN_SSL (type 1) -> OPEN_SSL_WITH_TLS_SCT (type 3) adds
- // eight zero-bytes:
- // 1.) 4 bytes for int32 value 0 == countOcspResponses
- // 2.) 4 bytes for int32 value 0 == tlsSctDataLength
- // since OPEN_SSL (type 1) cannot contain OSCP or TLS SCT data.
- check_reserializableFromByteArray_roundTrip(getType1().build(), new byte[8]);
- }
-
- @Test
- public void test_reserializableFromByteArray_roundTrip_type2() throws Exception {
- // Converting OPEN_SSL_WITH_OCSP (type 2) -> OPEN_SSL_WITH_TLS_SCT (type 3) adds
- // four zero-bytes for int32 value 0 == tlsSctDataLength
- // since OPEN_SSL_WITH_OCSP (type 2) cannot contain TLS SCT data.
- check_reserializableFromByteArray_roundTrip(getType2().build(), new byte[4]);
- }
-
- @Test
- public void test_reserializableFromByteArray_roundTrip_type3() throws Exception {
- check_reserializableFromByteArray_roundTrip(getType3().build(), new byte[0]);
- }
-
- private static void check_reserializableFromByteArray_roundTrip(
- byte[] data, byte[] expectedTrailingBytesAfterReserialization) throws Exception {
- SSLSession session = clientCtx.toSession(data, "www.example.com", 12345);
- byte[] sessionBytes = clientCtx.toBytes(session);
-
- SSLSession session2 = clientCtx.toSession(sessionBytes, "www.example.com", 12345);
- byte[] sessionBytes2 = clientCtx.toBytes(session);
-
- assertSSLSessionEquals(session, session2);
- assertByteArrayEquals(sessionBytes, sessionBytes2);
-
- assertEquals("www.example.com", session.getPeerHost());
- assertEquals(12345, session.getPeerPort());
- assertTrue(sessionBytes.length >= data.length);
-
- byte[] expectedReserializedData = concat(data, expectedTrailingBytesAfterReserialization);
- // AbstractSessionContext.toBytes() always writes type 3 == OPEN_SSL_WITH_TLS_SCT
- expectedReserializedData[3] = 3;
- assertByteArrayEquals(expectedReserializedData, sessionBytes);
- }
-
- private static byte[] concat(byte[] a, byte[] b) {
- byte[] result = new byte[a.length + b.length];
- System.arraycopy(a, 0, result, 0, a.length);
- System.arraycopy(b, 0, result, a.length, b.length);
- return result;
- }
-
- private static void assertSSLSessionEquals(SSLSession a, SSLSession b) throws Exception {
- assertEquals(a.getApplicationBufferSize(), b.getApplicationBufferSize());
- assertEquals(a.getCipherSuite(), b.getCipherSuite());
- assertEquals(a.getCreationTime(), b.getCreationTime());
- assertByteArrayEquals(a.getId(), b.getId());
- assertEquals(a.getLastAccessedTime(), b.getLastAccessedTime());
- assertArrayEquals(a.getLocalCertificates(), b.getLocalCertificates());
- assertEquals(a.getLocalPrincipal(), b.getLocalPrincipal());
- assertArrayEquals(a.getPeerCertificateChain(), b.getPeerCertificateChain());
- assertArrayEquals(a.getPeerCertificates(), b.getPeerCertificates());
- assertEquals(a.getPeerHost(), b.getPeerHost());
- assertEquals(a.getPeerPort(), b.getPeerPort());
- assertEquals(a.getPeerPrincipal(), b.getPeerPrincipal());
- assertEquals(a.getProtocol(), b.getProtocol());
- assertEquals(getValueMap(a), getValueMap(b));
- assertEquals(a.isValid(), b.isValid());
-
- assertEquals(a.getClass(), b.getClass());
-
- // Could potentially cast to AbstractOpenSSLSession here and compare additional fields.
- }
-
- private static Map<String, Object> getValueMap(SSLSession sslSession) {
- Map<String, Object> result = new HashMap<>();
- for (String valueName : sslSession.getValueNames()) {
- result.put(valueName, sslSession.getValue(valueName));
- }
- return Collections.unmodifiableMap(result);
- }
-
- private static <T> void assertArrayEquals(T[] expected, T[] actual) {
- assertTrue("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual),
- Arrays.equals(expected, actual));
- }
-
- private static void assertByteArrayEquals(byte[] expected, byte[] actual) {
- // If running on OpenJDK 8+, could use java.util.Base64 for better failure messages:
- // assertEquals(Base64.encode(expected), Base64.encode(actual));
- assertTrue("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual),
- Arrays.equals(expected, actual));
+ private SslSessionWrapper newSession(String host) {
+ return new MockSessionBuilder().host(host).build();
}
}
diff --git a/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java
index f214d7b..709b893 100644
--- a/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ClientSessionContextTest.java
@@ -16,135 +16,36 @@
package org.conscrypt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.conscrypt.MockSessionBuilder.DEFAULT_PORT;
-import java.security.cert.Certificate;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Set;
-import javax.net.ssl.SSLSession;
-import junit.framework.TestCase;
-import libcore.javax.net.ssl.FakeSSLSession;
+import java.security.KeyManagementException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
-public final class ClientSessionContextTest extends TestCase {
+@RunWith(JUnit4.class)
+public class ClientSessionContextTest extends AbstractSessionContextTest<ClientSessionContext> {
- public void testSimpleAddition() {
- ClientSessionContext context = new ClientSessionContext();
- SSLSession a = new ValidSSLSession("a");
- SSLSession b = new ValidSSLSession("b");
-
- context.putSession(a);
- assertSessionContextContents(context, new SSLSession[] { a }, new SSLSession[] { b });
-
- context.putSession(b);
- assertSessionContextContents(context, new SSLSession[] { a, b }, new SSLSession[0]);
+ @Override
+ ClientSessionContext newContext() {
+ return new ClientSessionContext();
}
- public void testTrimToSize() {
- ClientSessionContext context = new ClientSessionContext();
- ValidSSLSession a = new ValidSSLSession("a");
- ValidSSLSession b = new ValidSSLSession("b");
- ValidSSLSession c = new ValidSSLSession("c");
- ValidSSLSession d = new ValidSSLSession("d");
-
- context.putSession(a);
- assertSessionContextContents(context, new SSLSession[] { a }, new SSLSession[] { b, c, d });
-
- context.putSession(b);
- assertSessionContextContents(context, new SSLSession[] { a, b }, new SSLSession[] { c, d });
-
- context.putSession(c);
- assertSessionContextContents(context, new SSLSession[] { a, b, c }, new SSLSession[] { d });
-
- context.putSession(d);
- assertSessionContextContents(context, new SSLSession[] { a, b, c, d }, new SSLSession[0]);
-
- context.setSessionCacheSize(2);
- assertSessionContextContents(context, new SSLSession[] { c, d }, new SSLSession[] { a, b });
+ @Override
+ SslSessionWrapper getCachedSession(ClientSessionContext context, SslSessionWrapper s) {
+ return context.getCachedSession(s.getPeerHost(), DEFAULT_PORT,
+ getDefaultSSLParameters());
}
- public void testImplicitRemovalOfOldest() {
- ClientSessionContext context = new ClientSessionContext();
- context.setSessionCacheSize(2);
- ValidSSLSession a = new ValidSSLSession("a");
- ValidSSLSession b = new ValidSSLSession("b");
- ValidSSLSession c = new ValidSSLSession("c");
- ValidSSLSession d = new ValidSSLSession("d");
-
- context.putSession(a);
- assertSessionContextContents(context, new SSLSession[] { a }, new SSLSession[] { b, c, d });
-
- context.putSession(b);
- assertSessionContextContents(context, new SSLSession[] { a, b }, new SSLSession[] { c, d });
-
- context.putSession(c);
- assertSessionContextContents(context, new SSLSession[] { b, c }, new SSLSession[] { a, d });
-
- context.putSession(d);
- assertSessionContextContents(context, new SSLSession[] { c, d }, new SSLSession[] { a, b });
+ @Override
+ int size(ClientSessionContext context) {
+ return context.size();
}
- public void testSerializeSession_NoStatusResponses() throws Exception {
- OpenSSLSessionImpl mockSession = mock(OpenSSLSessionImpl.class);
- when(mockSession.getId()).thenReturn(new byte[] { 0x11, 0x09, 0x03, 0x20 });
- when(mockSession.getPeerHost()).thenReturn("ssl.example.com");
- when(mockSession.getPeerPort()).thenReturn(443);
- when(mockSession.getEncoded()).thenReturn(new byte[] { 0x01, 0x02, 0x03 });
- when(mockSession.getStatusResponses()).thenReturn(Collections.<byte[]>emptyList());
-
- Certificate mockCert = mock(Certificate.class);
- when(mockCert.getEncoded()).thenReturn(new byte[] { 0x05, 0x06, 0x07, 0x10 });
-
- when(mockSession.getPeerCertificates()).thenReturn(new Certificate[] { mockCert });
-
- SSLClientSessionCache mockCache = mock(SSLClientSessionCache.class);
- ClientSessionContext context = new ClientSessionContext();
- context.setPersistentCache(mockCache);
-
- context.putSession(mockSession);
- verify(mockCache).putSessionData(eq(mockSession), any(byte[].class));
- }
-
-
- private static void assertSessionContextContents(ClientSessionContext context,
- SSLSession[] contains,
- SSLSession[] exludes) {
- assertEquals(contains.length, context.size());
-
- for (SSLSession s : contains) {
- assertSame(s.getPeerHost(), s, context.getSession(s.getId()));
- assertSame(s.getPeerHost(), s, context.getSession(s.getPeerHost(), 443));
- }
- for (SSLSession s : exludes) {
- assertNull(s.getPeerHost(), context.getSession(s.getId()));
- assertNull(s.getPeerHost(), context.getSession(s.getPeerHost(), 443));
- }
-
- Set<SSLSession> sessions = new HashSet<SSLSession>();
- Enumeration<byte[]> ids = context.getIds();
- while (ids.hasMoreElements()) {
- byte[] id = ids.nextElement();
- sessions.add(context.getSession(id));
- }
-
- Set<SSLSession> expected = new HashSet<SSLSession>();
- for (SSLSession s : sessions) {
- expected.add(s);
- }
- assertEquals(expected, sessions);
- }
-
- static class ValidSSLSession extends FakeSSLSession {
- ValidSSLSession(String host) {
- super(host);
- }
- @Override public boolean isValid() {
- return true;
+ private static SSLParametersImpl getDefaultSSLParameters() {
+ try {
+ return SSLParametersImpl.getDefault();
+ } catch (KeyManagementException e) {
+ throw new RuntimeException(e);
}
}
}
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
similarity index 73%
rename from openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java
rename to openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
index 27df90e..ecbf8b3 100644
--- a/openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
@@ -16,7 +16,9 @@
package org.conscrypt;
+import static org.conscrypt.Conscrypt.Engines.setBufferAllocator;
import static org.conscrypt.TestUtils.PROTOCOL_TLS_V1_2;
+import static org.conscrypt.TestUtils.TEST_CIPHER;
import static org.conscrypt.TestUtils.initEngine;
import static org.conscrypt.TestUtils.initSslContext;
import static org.conscrypt.TestUtils.newTextMessage;
@@ -44,18 +46,24 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class OpenSSLEngineImplTest {
- private static final String CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+public class ConscryptEngineTest {
private static final int MESSAGE_SIZE = 4096;
+ @SuppressWarnings("ImmutableEnumChecker")
public enum BufferType {
- HEAP {
+ HEAP_ALLOCATOR(BufferAllocator.unpooled()) {
@Override
ByteBuffer newBuffer(int size) {
return ByteBuffer.allocate(size);
}
},
- DIRECT {
+ HEAP_NO_ALLOCATOR(null) {
+ @Override
+ ByteBuffer newBuffer(int size) {
+ return ByteBuffer.allocate(size);
+ }
+ },
+ DIRECT(null) {
@Override
ByteBuffer newBuffer(int size) {
return ByteBuffer.allocateDirect(size);
@@ -63,47 +71,54 @@
};
abstract ByteBuffer newBuffer(int size);
+
+ BufferType(BufferAllocator allocator) {
+ this.allocator = allocator;
+ }
+
+ private final BufferAllocator allocator;
}
private enum ClientAuth {
NONE {
@Override
- SSLEngine apply(SSLEngine engine) {
+ void apply(SSLEngine engine) {
engine.setWantClientAuth(false);
engine.setNeedClientAuth(false);
- return engine;
}
},
OPTIONAL {
@Override
- SSLEngine apply(SSLEngine engine) {
+ void apply(SSLEngine engine) {
engine.setWantClientAuth(true);
engine.setNeedClientAuth(false);
- return engine;
}
},
REQUIRED {
@Override
- SSLEngine apply(SSLEngine engine) {
+ void apply(SSLEngine engine) {
engine.setWantClientAuth(false);
engine.setNeedClientAuth(true);
- return engine;
}
};
- abstract SSLEngine apply(SSLEngine engine);
+ abstract void apply(SSLEngine engine);
}
@Parameters(name = "{0}")
public static Iterable<BufferType> data() {
- return Arrays.asList(BufferType.HEAP, BufferType.DIRECT);
+ return Arrays.asList(
+ BufferType.HEAP_ALLOCATOR, BufferType.HEAP_NO_ALLOCATOR, BufferType.DIRECT);
}
- @Parameter
- public BufferType bufferType;
+ @Parameter public BufferType bufferType;
private SSLEngine clientEngine;
private SSLEngine serverEngine;
+ private ByteBuffer clientApplicationBuffer;
+ private ByteBuffer clientPacketBuffer;
+ private ByteBuffer serverApplicationBuffer;
+ private ByteBuffer serverPacketBuffer;
@Test
public void mutualAuthWithSameCertsShouldSucceed() throws Exception {
@@ -117,7 +132,8 @@
@Test(expected = SSLHandshakeException.class)
public void mutualAuthWithUntrustedServerShouldFail() throws Exception {
- doMutualAuthHandshake(TestKeyStore.getClientCA2(), TestKeyStore.getServer(), ClientAuth.NONE);
+ doMutualAuthHandshake(
+ TestKeyStore.getClientCA2(), TestKeyStore.getServer(), ClientAuth.NONE);
}
@Test(expected = SSLHandshakeException.class)
@@ -127,28 +143,32 @@
@Test
public void optionalClientAuthShouldSucceed() throws Exception {
- doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getServer(), ClientAuth.OPTIONAL);
+ doMutualAuthHandshake(
+ TestKeyStore.getClient(), TestKeyStore.getServer(), ClientAuth.OPTIONAL);
}
@Test(expected = SSLHandshakeException.class)
public void optionalClientAuthShouldFail() throws Exception {
- doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.OPTIONAL);
+ doMutualAuthHandshake(
+ TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.OPTIONAL);
}
@Test
public void requiredClientAuthShouldSucceed() throws Exception {
- doMutualAuthHandshake(TestKeyStore.getServer(), TestKeyStore.getServer(), ClientAuth.REQUIRED);
+ doMutualAuthHandshake(
+ TestKeyStore.getServer(), TestKeyStore.getServer(), ClientAuth.REQUIRED);
}
@Test(expected = SSLHandshakeException.class)
public void requiredClientAuthShouldFail() throws Exception {
- doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.REQUIRED);
+ doMutualAuthHandshake(
+ TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.REQUIRED);
}
@Test
public void exchangeMessages() throws Exception {
setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
- TestUtils.doEngineHandshake(clientEngine, serverEngine);
+ doHandshake();
ByteBuffer clientCleartextBuffer = bufferType.newBuffer(MESSAGE_SIZE);
clientCleartextBuffer.put(newTextMessage(MESSAGE_SIZE));
@@ -171,12 +191,10 @@
byte[] expectedMessage = toArray(clientCleartextBuffer);
// Unwrap the all of the encrypted messages.
- ByteBuffer[] cleartextBuffers = new ByteBuffer[numMessages];
for (int i = 0; i < numMessages; ++i) {
ByteBuffer out = bufferType.newBuffer(2 * MESSAGE_SIZE);
- cleartextBuffers[i] = out;
- SSLEngineResult unwrapResult = Conscrypt.Engines.unwrap(serverEngine, encryptedBuffers,
- new ByteBuffer[] {out});
+ SSLEngineResult unwrapResult = Conscrypt.Engines.unwrap(
+ serverEngine, encryptedBuffers, new ByteBuffer[] {out});
assertEquals(SSLEngineResult.Status.OK, unwrapResult.getStatus());
assertEquals(MESSAGE_SIZE, unwrapResult.bytesProduced());
@@ -189,7 +207,7 @@
@Test
public void exchangeLargeMessage() throws Exception {
setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
- TestUtils.doEngineHandshake(clientEngine, serverEngine);
+ doHandshake();
// Create the input message.
final int largeMessageSize = 16413;
@@ -200,8 +218,9 @@
// Encrypt the input message.
List<ByteBuffer> encryptedBufferList = new ArrayList<ByteBuffer>();
- while(inputBuffer.hasRemaining()) {
- ByteBuffer encryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+ while (inputBuffer.hasRemaining()) {
+ ByteBuffer encryptedBuffer =
+ bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
SSLEngineResult wrapResult = clientEngine.wrap(inputBuffer, encryptedBuffer);
assertEquals(SSLEngineResult.Status.OK, wrapResult.getStatus());
encryptedBuffer.flip();
@@ -210,7 +229,8 @@
// Unwrap the all of the encrypted messages.
ByteArrayOutputStream cleartextStream = new ByteArrayOutputStream();
- ByteBuffer[] encryptedBuffers = encryptedBufferList.toArray(new ByteBuffer[encryptedBufferList.size()]);
+ ByteBuffer[] encryptedBuffers =
+ encryptedBufferList.toArray(new ByteBuffer[encryptedBufferList.size()]);
int decryptedBufferSize = 8192;
final ByteBuffer decryptedBuffer = bufferType.newBuffer(decryptedBufferSize);
for (ByteBuffer encryptedBuffer : encryptedBuffers) {
@@ -220,8 +240,8 @@
decryptedBuffer.clear();
}
int prevPos = decryptedBuffer.position();
- SSLEngineResult unwrapResult = Conscrypt.Engines.unwrap(serverEngine,
- encryptedBuffers, new ByteBuffer[]{decryptedBuffer});
+ SSLEngineResult unwrapResult = Conscrypt.Engines.unwrap(
+ serverEngine, encryptedBuffers, new ByteBuffer[] {decryptedBuffer});
status = unwrapResult.getStatus();
int newPos = decryptedBuffer.position();
int bytesProduced = unwrapResult.bytesProduced();
@@ -249,20 +269,37 @@
assertArrayEquals(message, actualMessage);
}
- private void doMutualAuthHandshake(TestKeyStore clientKs, TestKeyStore serverKs, ClientAuth clientAuth) throws Exception {
+ private void doMutualAuthHandshake(
+ TestKeyStore clientKs, TestKeyStore serverKs, ClientAuth clientAuth) throws Exception {
setupEngines(clientKs, serverKs);
clientAuth.apply(serverEngine);
- TestUtils.doEngineHandshake(clientEngine, serverEngine);
+ doHandshake();
assertEquals(HandshakeStatus.NOT_HANDSHAKING, clientEngine.getHandshakeStatus());
assertEquals(HandshakeStatus.NOT_HANDSHAKING, serverEngine.getHandshakeStatus());
}
- private void setupEngines(TestKeyStore clientKeyStore, TestKeyStore serverKeyStore) throws SSLException {
+ private void doHandshake() throws SSLException {
+ TestUtils.doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer,
+ clientPacketBuffer, serverApplicationBuffer, serverPacketBuffer);
+ }
+
+ private void setupEngines(TestKeyStore clientKeyStore, TestKeyStore serverKeyStore)
+ throws SSLException {
SSLContext clientContext = initSslContext(newContext(), clientKeyStore);
SSLContext serverContext = initSslContext(newContext(), serverKeyStore);
- clientEngine = initEngine(clientContext.createSSLEngine(), CIPHER, true);
- serverEngine = initEngine(serverContext.createSSLEngine(), CIPHER, false);
+ clientEngine = initEngine(clientContext.createSSLEngine(), TEST_CIPHER, true);
+ serverEngine = initEngine(serverContext.createSSLEngine(), TEST_CIPHER, false);
+ setBufferAllocator(clientEngine, bufferType.allocator);
+ setBufferAllocator(serverEngine, bufferType.allocator);
+
+ // Create the application and packet buffers for both endpoints.
+ clientApplicationBuffer =
+ bufferType.newBuffer(clientEngine.getSession().getApplicationBufferSize());
+ serverApplicationBuffer =
+ bufferType.newBuffer(serverEngine.getSession().getApplicationBufferSize());
+ clientPacketBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+ serverPacketBuffer = bufferType.newBuffer(serverEngine.getSession().getPacketBufferSize());
}
private static byte[] toArray(ByteBuffer buffer) {
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
similarity index 89%
rename from openjdk/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java
rename to openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
index 8c59e8f..bb0b3ce 100644
--- a/openjdk/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
@@ -58,7 +58,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class OpenSSLSocketImplTest {
+public class ConscryptSocketTest {
private static final long TIMEOUT_SECONDS = 5;
private static final char[] EMPTY_PASSWORD = new char[0];
@@ -77,7 +77,7 @@
@Override
void assertSocketType(Socket socket) {
assertTrue("Unexpected socket type: " + socket.getClass().getName(),
- socket instanceof OpenSSLEngineSocketImpl);
+ socket instanceof ConscryptEngineSocket);
}
};
@@ -87,23 +87,23 @@
this.useEngineSocket = useEngineSocket;
}
- OpenSSLSocketImpl createClientSocket(OpenSSLContextImpl context, ServerSocket listener)
- throws IOException {
+ AbstractConscryptSocket createClientSocket(
+ OpenSSLContextImpl context, ServerSocket listener) throws IOException {
SSLSocketFactory factory = context.engineGetSocketFactory();
Conscrypt.SocketFactories.setUseEngineSocket(factory, useEngineSocket);
- OpenSSLSocketImpl socket = (OpenSSLSocketImpl) factory.createSocket(
+ AbstractConscryptSocket socket = (AbstractConscryptSocket) factory.createSocket(
listener.getInetAddress(), listener.getLocalPort());
assertSocketType(socket);
socket.setUseClientMode(true);
return socket;
}
- OpenSSLSocketImpl createServerSocket(OpenSSLContextImpl context, ServerSocket listener)
- throws IOException {
+ AbstractConscryptSocket createServerSocket(
+ OpenSSLContextImpl context, ServerSocket listener) throws IOException {
SSLSocketFactory factory = context.engineGetSocketFactory();
Conscrypt.SocketFactories.setUseEngineSocket(factory, useEngineSocket);
- OpenSSLSocketImpl socket = (OpenSSLSocketImpl) factory.createSocket(listener.accept(),
- null, -1, // hostname, port
+ AbstractConscryptSocket socket = (AbstractConscryptSocket) factory.createSocket(
+ listener.accept(), null, -1, // hostname, port
true); // autoclose
assertSocketType(socket);
socket.setUseClientMode(false);
@@ -152,7 +152,7 @@
KeyManager[] keyManagers;
TrustManager[] trustManagers;
- abstract OpenSSLSocketImpl createSocket(ServerSocket listener) throws IOException;
+ abstract AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException;
OpenSSLContextImpl createContext() throws IOException {
OpenSSLContextImpl context = OpenSSLContextImpl.getPreferred();
@@ -192,8 +192,9 @@
}
@Override
- OpenSSLSocketImpl createSocket(ServerSocket listener) throws IOException {
- OpenSSLSocketImpl socket = socketType.createClientSocket(createContext(), listener);
+ AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException {
+ AbstractConscryptSocket socket =
+ socketType.createClientSocket(createContext(), listener);
socket.setHostname(hostname);
return socket;
}
@@ -217,7 +218,7 @@
}
@Override
- OpenSSLSocketImpl createSocket(ServerSocket listener) throws IOException {
+ AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException {
return socketType.createServerSocket(createContext(), listener);
}
}
@@ -226,8 +227,8 @@
ServerHooks serverHooks;
ClientHooks clientHooks;
- OpenSSLSocketImpl client;
- OpenSSLSocketImpl server;
+ AbstractConscryptSocket client;
+ AbstractConscryptSocket server;
Exception clientException;
Exception serverException;
@@ -274,8 +275,8 @@
void doHandshake() throws Exception {
ServerSocket listener = newServerSocket();
- Future<OpenSSLSocketImpl> clientFuture = handshake(listener, clientHooks);
- Future<OpenSSLSocketImpl> serverFuture = handshake(listener, serverHooks);
+ Future<AbstractConscryptSocket> clientFuture = handshake(listener, clientHooks);
+ Future<AbstractConscryptSocket> serverFuture = handshake(listener, serverHooks);
try {
client = getOrThrowCause(clientFuture, TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -289,9 +290,9 @@
}
}
- Future<OpenSSLSocketImpl> handshake(final ServerSocket listener, final Hooks hooks) {
+ Future<AbstractConscryptSocket> handshake(final ServerSocket listener, final Hooks hooks) {
return executor.submit(() -> {
- OpenSSLSocketImpl socket = hooks.createSocket(listener);
+ AbstractConscryptSocket socket = hooks.createSocket(listener);
socket.addHandshakeCompletedListener(hooks);
socket.startHandshake();
@@ -390,8 +391,8 @@
connection.clientHooks = new ClientHooks() {
@Override
- public OpenSSLSocketImpl createSocket(ServerSocket listener) throws IOException {
- OpenSSLSocketImpl socket = super.createSocket(listener);
+ public AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException {
+ AbstractConscryptSocket socket = super.createSocket(listener);
socket.setEnabledProtocols(new String[] {"SSLv3"});
assertEquals(
"SSLv3 should be filtered out", 0, socket.getEnabledProtocols().length);
diff --git a/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java b/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java
new file mode 100644
index 0000000..5fb4caa
--- /dev/null
+++ b/openjdk/src/test/java/org/conscrypt/MockSessionBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 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 static org.conscrypt.TestUtils.UTF_8;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Utility class for constructing mock sessions.
+ */
+final class MockSessionBuilder {
+ static final String DEFAULT_CIPHER_SUITE = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
+ static final String DEFAULT_PROTOCOL = TestUtils.PROTOCOL_TLS_V1_2;
+ static final int DEFAULT_PORT = 443;
+
+ private byte[] id;
+ private boolean valid = true;
+ private String host;
+ private int port = DEFAULT_PORT;
+ private String cipherSuite = DEFAULT_CIPHER_SUITE;
+ private String protocol = DEFAULT_PROTOCOL;
+ private byte[] encodedBytes = EmptyArray.BYTE;
+
+ MockSessionBuilder id(byte[] id) {
+ this.id = id;
+ return this;
+ }
+
+ MockSessionBuilder protocol(String protocol) {
+ this.protocol = protocol;
+ return this;
+ }
+
+ MockSessionBuilder host(String host) {
+ this.host = host;
+ return this;
+ }
+
+ MockSessionBuilder port(int port) {
+ this.port = port;
+ return this;
+ }
+
+ MockSessionBuilder valid(boolean valid) {
+ this.valid = valid;
+ return this;
+ }
+
+ MockSessionBuilder cipherSuite(String cipherSuite) {
+ this.cipherSuite = cipherSuite;
+ return this;
+ }
+
+ MockSessionBuilder encodedBytes(byte[] encodedBytes) {
+ this.encodedBytes = encodedBytes;
+ return this;
+ }
+
+ SslSessionWrapper build() {
+ SslSessionWrapper session = mock(SslSessionWrapper.class);
+ byte[] id = this.id == null ? host.getBytes(UTF_8) : this.id;
+ when(session.getId()).thenReturn(id);
+ when(session.isValid()).thenReturn(valid);
+ when(session.getProtocol()).thenReturn(protocol);
+ when(session.getPeerHost()).thenReturn(host);
+ when(session.getPeerPort()).thenReturn(port);
+ when(session.getCipherSuite()).thenReturn(cipherSuite);
+ when(session.toBytes()).thenReturn(encodedBytes);
+ return session;
+ }
+}
diff --git a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
index e684e1a..8ec1029 100644
--- a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -35,6 +35,7 @@
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.math.BigInteger;
+import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
@@ -53,6 +54,7 @@
import java.security.spec.ECPrivateKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
@@ -72,7 +74,6 @@
import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
import org.junit.After;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -251,6 +252,14 @@
NativeCrypto.EVP_PKEY_cmp(null, null);
}
+ @Test(expected = NullPointerException.class)
+ public void EVP_PKEY_cmp_withNullShouldThrow() throws Exception {
+ RSAPrivateCrtKey privKey1 = generateRsaKey();
+ NativeRef.EVP_PKEY pkey1 = getRsaPkey(privKey1);
+ assertNotSame(NULL, pkey1);
+ NativeCrypto.EVP_PKEY_cmp(pkey1, null);
+ }
+
@Test
public void test_EVP_PKEY_cmp() throws Exception {
RSAPrivateCrtKey privKey1 = generateRsaKey();
@@ -264,12 +273,6 @@
NativeRef.EVP_PKEY pkey2 = getRsaPkey(generateRsaKey());
assertNotSame(NULL, pkey2);
- try {
- NativeCrypto.EVP_PKEY_cmp(pkey1, null);
- fail("Should throw NullPointerException when arguments are NULL");
- } catch (NullPointerException expected) {
- }
-
assertEquals("Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1));
assertEquals(
@@ -305,7 +308,7 @@
}
@Test(expected = NullPointerException.class)
- public void SSL_CTX_set_session_id_context_NullSessionIdArgument() throws Exception {
+ public void SSL_CTX_set_session_id_context_withNullShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
try {
NativeCrypto.SSL_CTX_set_session_id_context(c, null);
@@ -314,6 +317,16 @@
}
}
+ @Test(expected = IllegalArgumentException.class)
+ public void test_SSL_CTX_set_session_id_context_withInvalidIdShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ try {
+ NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[33]);
+ } finally {
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
@Test
public void test_SSL_CTX_set_session_id_context() throws Exception {
byte[] empty = new byte[0];
@@ -322,12 +335,6 @@
try {
NativeCrypto.SSL_CTX_set_session_id_context(c, empty);
NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[32]);
- try {
- NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[33]);
- fail("Expected IllegalArgumentException");
- } catch (IllegalArgumentException expected) {
- // Expected.
- }
} finally {
NativeCrypto.SSL_CTX_free(c);
}
@@ -357,42 +364,55 @@
NativeCrypto.SSL_use_certificate(NULL, null);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_use_certificate_withNullShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_use_certificate(s, null);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
@Test
public void test_SSL_use_certificate() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_use_certificate(s, null);
- fail();
- } catch (NullPointerException expected) {
- }
-
NativeCrypto.SSL_use_certificate(s, getServerCertificates());
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_set1_tls_channel_id_withNullChannelShouldThrow() throws Exception {
+ NativeCrypto.SSL_set1_tls_channel_id(NULL, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_set1_tls_channel_id_withNullKeyShouldThrow() throws Exception {
+ initChannelIdKey();
+
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_set1_tls_channel_id(s, null);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
@Test
public void test_SSL_use_PrivateKey_for_tls_channel_id() throws Exception {
initChannelIdKey();
- try {
- NativeCrypto.SSL_set1_tls_channel_id(NULL, null);
- fail();
- } catch (NullPointerException expected) {
- }
-
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_set1_tls_channel_id(s, null);
- fail();
- } catch (NullPointerException expected) {
- }
-
// Use the key natively. This works because the initChannelIdKey method ensures that the
// key is backed by OpenSSL.
NativeCrypto.SSL_set1_tls_channel_id(s, CHANNEL_ID_PRIVATE_KEY.getNativeRef());
@@ -401,21 +421,21 @@
NativeCrypto.SSL_CTX_free(c);
}
- @Test
- public void test_SSL_use_PrivateKey() throws Exception {
- try {
- NativeCrypto.SSL_use_PrivateKey(NULL, null);
- fail();
- } catch (NullPointerException expected) {
- }
+ @Test(expected = NullPointerException.class)
+ public void SSL_use_PrivateKey_withNullSslShouldThrow() throws Exception {
+ NativeCrypto.SSL_use_PrivateKey(NULL, null);
+ }
+ @Test(expected = NullPointerException.class)
+ public void SSL_use_PrivateKeyWithNullKeyShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
try {
NativeCrypto.SSL_use_PrivateKey(s, null);
- fail();
- } catch (NullPointerException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
@@ -424,62 +444,74 @@
NativeCrypto.SSL_CTX_free(c);
}
+ @Test
+ public void test_SSL_use_PrivateKey() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+
+ NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
+
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+
@Test(expected = NullPointerException.class)
- public void SSL_check_private_key_NullArgument() throws Exception {
+ public void SSL_check_private_key_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_check_private_key(NULL);
}
- @Test
- public void test_SSL_check_private_key_no_key_no_cert() throws Exception {
+ @Test(expected = SSLException.class)
+ public void SSL_check_private_key_withNoKeyOrCertShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
// neither private or certificate set
try {
NativeCrypto.SSL_check_private_key(s);
- fail();
- } catch (SSLException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
-
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
}
- @Test
- public void test_SSL_check_private_key_cert_then_key() throws Exception {
+ @Test(expected = SSLException.class)
+ public void SSL_check_private_key_withNoKeyShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
- // first certificate, then private
+ // Certificate but no private key
NativeCrypto.SSL_use_certificate(s, getServerCertificates());
try {
NativeCrypto.SSL_check_private_key(s);
- fail();
- } catch (SSLException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
-
- NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
- NativeCrypto.SSL_check_private_key(s);
-
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
}
- @Test
- public void test_SSL_check_private_key_key_then_cert() throws Exception {
+
+ @Test(expected = SSLException.class)
+ public void test_SSL_check_private_NoCertificateShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
// first private, then certificate
NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
-
try {
NativeCrypto.SSL_check_private_key(s);
- fail();
- } catch (SSLException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
+ }
+
+ @Test
+ public void test_SSL_check_private_key_certThenKey() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
NativeCrypto.SSL_use_certificate(s, getServerCertificates());
+ NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
NativeCrypto.SSL_check_private_key(s);
NativeCrypto.SSL_free(s);
@@ -487,13 +519,26 @@
}
@Test
- public void test_SSL_get_mode() throws Exception {
- try {
- NativeCrypto.SSL_get_mode(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
+ public void test_SSL_check_private_key_keyThenCert() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ // first private, then certificate
+ NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
+ NativeCrypto.SSL_use_certificate(s, getServerCertificates());
+ NativeCrypto.SSL_check_private_key(s);
+
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_get_mode_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_get_mode(NULL);
+ }
+
+ @Test
+ public void test_SSL_get_mode() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
assertTrue(NativeCrypto.SSL_get_mode(s) != 0);
@@ -501,14 +546,13 @@
NativeCrypto.SSL_CTX_free(c);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_mode_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_mode(NULL, 0);
+ }
+
@Test
public void test_SSL_set_mode_and_clear_mode() throws Exception {
- try {
- NativeCrypto.SSL_set_mode(NULL, 0);
- fail();
- } catch (NullPointerException expected) {
- }
-
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
// check SSL_MODE_ENABLE_FALSE_START on by default for BoringSSL
@@ -528,14 +572,13 @@
NativeCrypto.SSL_CTX_free(c);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_get_options_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_get_options(NULL);
+ }
+
@Test
public void test_SSL_get_options() throws Exception {
- try {
- NativeCrypto.SSL_get_options(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
assertTrue(NativeCrypto.SSL_get_options(s) != 0);
@@ -543,14 +586,13 @@
NativeCrypto.SSL_CTX_free(c);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_options_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_options(NULL, 0);
+ }
+
@Test
public void test_SSL_set_options() throws Exception {
- try {
- NativeCrypto.SSL_set_options(NULL, 0);
- fail();
- } catch (NullPointerException expected) {
- }
-
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) == 0);
@@ -560,14 +602,13 @@
NativeCrypto.SSL_CTX_free(c);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_clear_options_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_clear_options(NULL, 0);
+ }
+
@Test
public void test_SSL_clear_options() throws Exception {
- try {
- NativeCrypto.SSL_clear_options(NULL, 0);
- fail();
- } catch (NullPointerException expected) {
- }
-
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) == 0);
@@ -579,32 +620,52 @@
NativeCrypto.SSL_CTX_free(c);
}
- @Test
- public void test_SSL_set_cipher_lists() throws Exception {
- try {
- NativeCrypto.SSL_set_cipher_lists(NULL, null);
- fail("Exception not thrown for null ssl and null list");
- } catch (NullPointerException expected) {
- }
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_cipher_lists_withNullSslShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_cipher_lists(NULL, null);
+ }
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_cipher_lists_withNullCiphersShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
-
try {
NativeCrypto.SSL_set_cipher_lists(s, null);
- fail("Exception not thrown for null list");
- } catch (NullPointerException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void test_SSL_set_cipher_lists_withNullCipherShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_set_cipher_lists(s, new String[] {null});
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
+ @Test
+ public void SSL_set_cipher_lists_withEmptyCiphersShouldSucceed() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
// Explicitly checking that the empty list is allowed.
// b/21816861
NativeCrypto.SSL_set_cipher_lists(s, new String[] {});
- try {
- NativeCrypto.SSL_set_cipher_lists(s, new String[] {null});
- fail("Exception not thrown for list with null element");
- } catch (NullPointerException expected) {
- }
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+
+ @Test
+ public void SSL_set_cipher_lists_withIllegalCipherShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
// see OpenSSL ciphers man page
String[] illegals = new String[] {// empty
@@ -619,25 +680,33 @@
NativeCrypto.SSL_set_cipher_lists(s, new String[] {illegal});
fail("Exception now thrown for illegal cipher: " + illegal);
} catch (IllegalArgumentException expected) {
+ // Expected.
}
}
- List<String> ciphers =
- new ArrayList<String>(NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.keySet());
- NativeCrypto.SSL_set_cipher_lists(s, ciphers.toArray(new String[ciphers.size()]));
-
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
@Test
- public void test_SSL_set_verify() throws Exception {
- try {
- NativeCrypto.SSL_set_verify(NULL, 0);
- fail();
- } catch (NullPointerException expected) {
- }
+ public void SSL_set_cipher_lists_withValidCiphersShouldSucceed() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ List<String> ciphers = new ArrayList<>(NativeCrypto.SUPPORTED_CIPHER_SUITES_SET);
+ NativeCrypto.SSL_set_cipher_lists(s, ciphers.toArray(new String[ciphers.size()]));
+
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_verify_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_verify(NULL, 0);
+ }
+
+ @Test
+ public void test_SSL_set_verify() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_NONE);
@@ -652,18 +721,19 @@
private static final boolean DEBUG = false;
public static class Hooks {
- protected String negotiatedCipherSuite;
+ String negotiatedCipherSuite;
private OpenSSLKey channelIdPrivateKey;
- protected boolean pskEnabled;
- protected byte[] pskKey;
- protected List<String> enabledCipherSuites;
+ boolean pskEnabled;
+ byte[] pskKey;
+ List<String> enabledCipherSuites;
/**
- * @throws SSLException
+ * @throws SSLException if an error occurs creating the context.
*/
public long getContext() throws SSLException {
return NativeCrypto.SSL_CTX_new();
}
+
public long beforeHandshake(long context) throws SSLException {
long s = NativeCrypto.SSL_new(context);
// Limit cipher suites to a known set so authMethod is known.
@@ -678,7 +748,7 @@
} else {
cipherSuites.addAll(enabledCipherSuites);
}
- NativeCrypto.SSL_set_cipher_lists(
+ NativeCrypto.setEnabledCipherSuites(
s, cipherSuites.toArray(new String[cipherSuites.size()]));
if (channelIdPrivateKey != null) {
@@ -699,6 +769,7 @@
try {
NativeCrypto.SSL_shutdown(ssl, fd, callback);
} catch (IOException e) {
+ // Expected.
}
NativeCrypto.SSL_free(ssl);
}
@@ -711,20 +782,20 @@
}
}
- public static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks {
+ static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks {
private final Socket socket;
private final long sslNativePointer;
private final Hooks hooks;
- public TestSSLHandshakeCallbacks(Socket socket, long sslNativePointer, Hooks hooks) {
+ TestSSLHandshakeCallbacks(Socket socket, long sslNativePointer, Hooks hooks) {
this.socket = socket;
this.sslNativePointer = sslNativePointer;
this.hooks = hooks;
}
- public long[] certificateChainRefs;
- public String authMethod;
- public boolean verifyCertificateChainCalled;
+ private long[] certificateChainRefs;
+ private String authMethod;
+ private boolean verifyCertificateChainCalled;
@Override
public void verifyCertificateChain(long[] certChainRefs, String authMethod)
@@ -740,9 +811,9 @@
this.verifyCertificateChainCalled = true;
}
- public byte[] keyTypes;
- public byte[][] asn1DerEncodedX500Principals;
- public boolean clientCertificateRequestedCalled;
+ private byte[] keyTypes;
+ private byte[][] asn1DerEncodedX500Principals;
+ private boolean clientCertificateRequestedCalled;
@Override
public void clientCertificateRequested(
@@ -762,7 +833,7 @@
}
}
- public boolean handshakeCompletedCalled;
+ private boolean handshakeCompletedCalled;
@Override
public void onSSLStateChange(int type, int val) {
@@ -773,7 +844,7 @@
this.handshakeCompletedCalled = true;
}
- public Socket getSocket() {
+ Socket getSocket() {
return socket;
}
@@ -809,6 +880,7 @@
private byte[] serverPSKKeyRequestedResultKey;
private String serverPSKKeyRequestedIdentityHint;
private String serverPSKKeyRequestedIdentity;
+
@Override
public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
if (DEBUG) {
@@ -826,10 +898,35 @@
}
return serverPSKKeyRequestedResult;
}
+
+ private boolean onNewSessionEstablishedInvoked;
+ private boolean onNewSessionEstablishedSaveSession;
+ private long onNewSessionEstablishedSessionNativePointer;
+
+ @Override
+ public void onNewSessionEstablished(long sslSessionNativePtr) {
+ if (DEBUG) {
+ System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ + " onNewSessionCreated"
+ + " ssl=0x" + Long.toString(sslSessionNativePtr, 16));
+ }
+ onNewSessionEstablishedInvoked = true;
+
+ if (onNewSessionEstablishedSaveSession) {
+ NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
+ onNewSessionEstablishedSessionNativePointer = sslSessionNativePtr;
+ }
+ }
+
+ @Override
+ public long serverSessionRequested(byte[] id) {
+ // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
+ return 0;
+ }
}
- public static class ClientHooks extends Hooks {
- protected String pskIdentity;
+ static class ClientHooks extends Hooks {
+ private String pskIdentity;
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
@@ -860,20 +957,20 @@
}
}
- public static class ServerHooks extends Hooks {
+ static class ServerHooks extends Hooks {
private final OpenSSLKey privateKey;
private final long[] certificates;
private boolean channelIdEnabled;
private byte[] channelIdAfterHandshake;
private Throwable channelIdAfterHandshakeException;
- protected String pskIdentityHint;
+ private String pskIdentityHint;
public ServerHooks() {
this(null, null);
}
- public ServerHooks(OpenSSLKey privateKey, long[] certificates) {
+ ServerHooks(OpenSSLKey privateKey, long[] certificates) {
this.privateKey = privateKey;
this.certificates = certificates;
}
@@ -934,45 +1031,53 @@
executor.submit(new Callable<TestSSLHandshakeCallbacks>() {
@Override
public TestSSLHandshakeCallbacks call() throws Exception {
- @SuppressWarnings("resource")
- // Socket needs to remain open after the handshake
- Socket socket = (client ? new Socket(listener.getInetAddress(),
- listener.getLocalPort())
- : listener.accept());
- if (timeout == -1) {
- return new TestSSLHandshakeCallbacks(socket, 0, null);
- }
- FileDescriptor fd =
- (FileDescriptor) m_Platform_getFileDescriptor.invoke(null, socket);
- long c = hooks.getContext();
- long s = hooks.beforeHandshake(c);
- TestSSLHandshakeCallbacks callback =
- new TestSSLHandshakeCallbacks(socket, s, hooks);
- hooks.configureCallbacks(callback);
- if (DEBUG) {
- System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
- + " context=0x" + Long.toString(c, 16) + " socket=" + socket
- + " fd=" + fd + " timeout=" + timeout + " client=" + client);
- }
- long session = NULL;
try {
- if (client) {
- NativeCrypto.SSL_set_connect_state(s);
- } else {
- NativeCrypto.SSL_set_accept_state(s);
+ @SuppressWarnings("resource")
+ // Socket needs to remain open after the handshake
+ Socket socket = (client ? new Socket(listener.getInetAddress(),
+ listener.getLocalPort())
+ : listener.accept());
+ if (timeout == -1) {
+ return new TestSSLHandshakeCallbacks(socket, 0, null);
}
- NativeCrypto.SSL_configure_alpn(s, client, alpnProtocols);
- NativeCrypto.SSL_do_handshake(s, fd, callback, timeout);
- session = NativeCrypto.SSL_get1_session(s);
+ FileDescriptor fd =
+ (FileDescriptor) m_Platform_getFileDescriptor
+ .invoke(null, socket);
+ long c = hooks.getContext();
+ long s = hooks.beforeHandshake(c);
+ TestSSLHandshakeCallbacks callback =
+ new TestSSLHandshakeCallbacks(socket, s, hooks);
+ hooks.configureCallbacks(callback);
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
- + " session=0x" + Long.toString(session, 16));
+ + " context=0x" + Long.toString(c, 16) + " socket=" + socket
+ + " fd=" + fd + " timeout=" + timeout + " client="
+ + client);
}
- } finally {
- // Ensure afterHandshake is called to free resources
- hooks.afterHandshake(session, s, c, socket, fd, callback);
+ long session = NULL;
+ try {
+ if (client) {
+ NativeCrypto.SSL_set_connect_state(s);
+ } else {
+ NativeCrypto.SSL_set_accept_state(s);
+ }
+ NativeCrypto.SSL_configure_alpn(s, client, alpnProtocols);
+ NativeCrypto.SSL_do_handshake(s, fd, callback, timeout);
+ session = NativeCrypto.SSL_get1_session(s);
+ if (DEBUG) {
+ System.out
+ .println("ssl=0x" + Long.toString(s, 16) + " handshake"
+ + " session=0x" + Long.toString(session, 16));
+ }
+ } finally {
+ // Ensure afterHandshake is called to free resources
+ hooks.afterHandshake(session, s, c, socket, fd, callback);
+ }
+ return callback;
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
}
- return callback;
}
});
executor.shutdown();
@@ -984,32 +1089,36 @@
NativeCrypto.SSL_do_handshake(NULL, null, null, 0);
}
- @Test
- public void test_SSL_do_handshake_null_args() throws Exception {
+ @Test(expected = NullPointerException.class)
+ public void test_SSL_do_handshake_withNullFdShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
NativeCrypto.SSL_set_connect_state(s);
-
try {
NativeCrypto.SSL_do_handshake(s, null, null, 0);
- fail();
- } catch (NullPointerException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
+ }
+ @Test(expected = NullPointerException.class)
+ public void test_SSL_do_handshake_withNullShcShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ NativeCrypto.SSL_set_connect_state(s);
try {
NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0);
- fail();
- } catch (NullPointerException expected) {
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
-
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
}
@Test
public void test_SSL_do_handshake_normal() throws Exception {
// normal client and server case
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
@@ -1026,14 +1135,96 @@
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@Test
+ public void test_SSL_do_handshake_reusedSession() throws Exception {
+ // normal client and server case
+ final ServerSocket listener = newServerSocket();
+
+ Future<TestSSLHandshakeCallbacks> client1 =
+ handshake(listener, 0, true, new ClientHooks() {
+ @Override
+ public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+ callbacks.onNewSessionEstablishedSaveSession = true;
+ }
+ }, null);
+ Future<TestSSLHandshakeCallbacks> server1 = handshake(listener, 0,
+ false, new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+ @Override
+ public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+ callbacks.onNewSessionEstablishedSaveSession = true;
+ }
+ }, null);
+ TestSSLHandshakeCallbacks clientCallback1 = client1.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback1 = server1.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue(clientCallback1.verifyCertificateChainCalled);
+ assertEqualCertificateChains(getServerCertificates(), clientCallback1.certificateChainRefs);
+ assertEquals("ECDHE_RSA", clientCallback1.authMethod);
+ assertFalse(serverCallback1.verifyCertificateChainCalled);
+ assertFalse(clientCallback1.clientCertificateRequestedCalled);
+ assertFalse(serverCallback1.clientCertificateRequestedCalled);
+ assertFalse(clientCallback1.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback1.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback1.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback1.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback1.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback1.onNewSessionEstablishedInvoked);
+ assertTrue(clientCallback1.handshakeCompletedCalled);
+ assertTrue(serverCallback1.handshakeCompletedCalled);
+
+ final long clientSessionContext =
+ clientCallback1.onNewSessionEstablishedSessionNativePointer;
+ final long serverSessionContext =
+ serverCallback1.onNewSessionEstablishedSessionNativePointer;
+
+ Future<TestSSLHandshakeCallbacks> client2 =
+ handshake(listener, 0, true, new ClientHooks() {
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long sslNativePtr = super.beforeHandshake(c);
+ NativeCrypto.SSL_set_session(sslNativePtr, clientSessionContext);
+ return sslNativePtr;
+ }
+ }, null);
+ Future<TestSSLHandshakeCallbacks> server2 = handshake(listener, 0,
+ false, new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long sslNativePtr = super.beforeHandshake(c);
+ NativeCrypto.SSL_set_session(sslNativePtr, serverSessionContext);
+ return sslNativePtr;
+ }
+ }, null);
+ TestSSLHandshakeCallbacks clientCallback2 = client2.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback2 = server2.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue(clientCallback2.verifyCertificateChainCalled);
+ assertEqualCertificateChains(getServerCertificates(), clientCallback2.certificateChainRefs);
+ assertEquals("ECDHE_RSA", clientCallback2.authMethod);
+ assertFalse(serverCallback2.verifyCertificateChainCalled);
+ assertFalse(clientCallback2.clientCertificateRequestedCalled);
+ assertFalse(serverCallback2.clientCertificateRequestedCalled);
+ assertFalse(clientCallback2.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback2.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback2.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback2.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback2.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback2.onNewSessionEstablishedInvoked);
+ assertTrue(clientCallback2.handshakeCompletedCalled);
+ assertTrue(serverCallback2.handshakeCompletedCalled);
+
+ NativeCrypto.SSL_SESSION_free(clientSessionContext);
+ NativeCrypto.SSL_SESSION_free(serverSessionContext);
+ }
+
+ @Test
public void test_SSL_do_handshake_optional_client_certificate() throws Exception {
// optional client certificate case
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
@@ -1065,8 +1256,8 @@
assertTrue(clientCallback.clientCertificateRequestedCalled);
assertNotNull(clientCallback.keyTypes);
- assertEquals(new HashSet<String>(Arrays.asList("EC", "RSA")),
- SSLParametersImpl.getSupportedClientKeyTypes(clientCallback.keyTypes));
+ assertEquals(new HashSet<>(Arrays.asList("EC", "RSA")),
+ SSLUtils.getSupportedClientKeyTypes(clientCallback.keyTypes));
assertEqualPrincipals(getCaPrincipals(), clientCallback.asn1DerEncodedX500Principals);
assertFalse(serverCallback.clientCertificateRequestedCalled);
@@ -1074,7 +1265,8 @@
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
-
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@@ -1082,7 +1274,7 @@
@Test
public void test_SSL_do_handshake_missing_required_certificate() throws Exception {
// required client certificate negative case
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
try {
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
@@ -1090,8 +1282,7 @@
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_client_CA_list(s, getCaPrincipals());
- NativeCrypto.SSL_set_verify(s,
- NativeCrypto.SSL_VERIFY_PEER
+ NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER
| NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
return s;
}
@@ -1106,74 +1297,10 @@
}
}
- /**
- * Usually if a RuntimeException is thrown by the
- * clientCertificateRequestedCalled callback, the caller sees it
- * during the call to NativeCrypto_SSL_do_handshake. However, IIS
- * does not request client certs until after the initial
- * handshake. It does an SSL renegotiation, which means we need to
- * be able to deliver the callback's exception in cases like
- * SSL_read, SSL_write, and SSL_shutdown.
- */
- @Ignore("TODO(nathanmittler): Determine why this doesn't pass on openjdk")
- @Test
- public void test_SSL_do_handshake_clientCertificateRequested_throws_after_renegotiate()
- throws Exception {
- final ServerSocket listener = new ServerSocket(0);
-
- Hooks cHooks = new Hooks() {
- @Override
- public long beforeHandshake(long context) throws SSLException {
- long s = super.beforeHandshake(context);
- NativeCrypto.SSL_clear_mode(s, SSL_MODE_ENABLE_FALSE_START);
- return s;
- }
- @Override
- public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
- SSLHandshakeCallbacks callback) throws Exception {
- NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0);
- fail();
- super.afterHandshake(session, s, c, sock, fd, callback);
- }
- @Override
- public void clientCertificateRequested(long s) {
- super.clientCertificateRequested(s);
- throw new RuntimeException("expected");
- }
- };
- Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
- @Override
- public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
- SSLHandshakeCallbacks callback) throws Exception {
- try {
- NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER);
- NativeCrypto.SSL_set_options(
- s, NativeConstants.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
- NativeCrypto.SSL_renegotiate(s);
- NativeCrypto.SSL_write(s, fd, callback, new byte[] {42}, 0, 1,
- (int) ((TIMEOUT_SECONDS * 1000) / 2));
- } catch (IOException expected) {
- } finally {
- super.afterHandshake(session, s, c, sock, fd, callback);
- }
- }
- };
- Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
- Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
- try {
- client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
- } catch (ExecutionException e) {
- if (!"expected".equals(e.getCause().getMessage())) {
- throw e;
- }
- }
- server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
- }
-
@Test
public void test_SSL_do_handshake_client_timeout() throws Exception {
// client timeout
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Socket serverSocket = null;
try {
Hooks cHooks = new Hooks();
@@ -1197,7 +1324,7 @@
@Test
public void test_SSL_do_handshake_server_timeout() throws Exception {
// server timeout
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Socket clientSocket = null;
try {
Hooks cHooks = new Hooks();
@@ -1220,11 +1347,11 @@
initChannelIdKey();
// Normal handshake with TLS Channel ID.
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
// TLS Channel ID currently requires ECDHE-based key exchanges.
- cHooks.enabledCipherSuites = Arrays.asList(new String[] {"ECDHE-RSA-AES128-SHA"});
+ cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
sHooks.channelIdEnabled = true;
sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
@@ -1242,6 +1369,8 @@
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
@@ -1253,11 +1382,11 @@
initChannelIdKey();
// Client tries to use TLS Channel ID but the server does not enable/offer the extension.
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
// TLS Channel ID currently requires ECDHE-based key exchanges.
- cHooks.enabledCipherSuites = Arrays.asList(new String[] {"ECDHE-RSA-AES128-SHA"});
+ cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
sHooks.channelIdEnabled = false;
sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
@@ -1275,6 +1404,8 @@
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
@@ -1286,11 +1417,11 @@
initChannelIdKey();
// Client does not use TLS Channel ID when the server has the extension enabled/offered.
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
cHooks.channelIdPrivateKey = null;
// TLS Channel ID currently requires ECDHE-based key exchanges.
- cHooks.enabledCipherSuites = Arrays.asList(new String[] {"ECDHE-RSA-AES128-SHA"});
+ cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
sHooks.channelIdEnabled = true;
sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
@@ -1308,6 +1439,8 @@
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
@@ -1317,7 +1450,7 @@
@Test
public void test_SSL_do_handshake_with_psk_normal() throws Exception {
// normal TLS-PSK client and server case
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
@@ -1332,6 +1465,8 @@
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
@@ -1349,7 +1484,7 @@
public void test_SSL_do_handshake_with_psk_with_identity_and_hint() throws Exception {
// normal TLS-PSK client and server case where the server provides the client with a PSK
// identity hint, and the client provides the server with a PSK identity.
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
@@ -1366,6 +1501,8 @@
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
@@ -1385,7 +1522,7 @@
throws Exception {
// normal TLS-PSK client and server case where the server provides the client with a PSK
// identity hint, and the client provides the server with a PSK identity.
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
@@ -1421,7 +1558,7 @@
@Test
public void test_SSL_do_handshake_with_psk_key_mismatch() throws Exception {
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
@@ -1441,7 +1578,7 @@
@Test
public void test_SSL_do_handshake_with_psk_with_no_client_key() throws Exception {
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
@@ -1461,7 +1598,7 @@
@Test
public void test_SSL_do_handshake_with_psk_with_no_server_key() throws Exception {
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
@@ -1482,7 +1619,7 @@
@Test
@SuppressWarnings("deprecation")
public void test_SSL_do_handshake_with_psk_key_too_long() throws Exception {
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks() {
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
@@ -1510,7 +1647,7 @@
public void test_SSL_do_handshake_with_ocsp_response() throws Exception {
final byte[] OCSP_TEST_DATA = new byte[] {1, 2, 3, 4};
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
@@ -1551,7 +1688,7 @@
// Each SCT entry has a length (unsigned 16-bit) and data.
final byte[] SCT_TEST_DATA = new byte[] {0, 6, 0, 4, 1, 2, 3, 4};
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
@@ -1583,6 +1720,8 @@
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+ assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@@ -1606,6 +1745,7 @@
NativeCrypto.SSL_use_psk_identity_hint(s, pskIdentityHint.toString());
fail();
} catch (SSLException expected) {
+ // Expected.
}
} finally {
NativeCrypto.SSL_free(s);
@@ -1613,26 +1753,23 @@
}
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_session_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_session(NULL, NULL);
+ }
+
@Test
public void test_SSL_set_session() throws Exception {
- try {
- NativeCrypto.SSL_set_session(NULL, NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- NativeCrypto.SSL_set_session(s, NULL);
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ NativeCrypto.SSL_set_session(s, NULL);
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
{
final long clientContext = NativeCrypto.SSL_CTX_new();
final long serverContext = NativeCrypto.SSL_CTX_new();
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
final long[] clientSession = new long[] {NULL};
final long[] serverSession = new long[] {NULL};
{
@@ -1713,24 +1850,21 @@
}
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_session_creation_enabled_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_session_creation_enabled(NULL, false);
+ }
+
@Test
public void test_SSL_set_session_creation_enabled() throws Exception {
- try {
- NativeCrypto.SSL_set_session_creation_enabled(NULL, false);
- fail();
- } catch (NullPointerException expected) {
- }
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ NativeCrypto.SSL_set_session_creation_enabled(s, false);
+ NativeCrypto.SSL_set_session_creation_enabled(s, true);
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- NativeCrypto.SSL_set_session_creation_enabled(s, false);
- NativeCrypto.SSL_set_session_creation_enabled(s, true);
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
// negative test case for SSL_set_session_creation_enabled(false) on client
{
@@ -1789,46 +1923,53 @@
}
}
- @Test
- public void test_SSL_set_tlsext_host_name() throws Exception {
- // NULL SSL
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_tlsext_host_name_withNullSslShouldThrow() throws Exception {
+ NativeCrypto.SSL_set_tlsext_host_name(NULL, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_set_tlsext_host_name_withNullHostnameShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+
try {
- NativeCrypto.SSL_set_tlsext_host_name(NULL, null);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final String hostname = "www.android.com";
-
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
-
- // null hostname
- try {
- NativeCrypto.SSL_set_tlsext_host_name(s, null);
- fail();
- } catch (NullPointerException expected) {
- }
-
- // too long hostname
- try {
- char[] longHostname = new char[256];
- Arrays.fill(longHostname, 'w');
- NativeCrypto.SSL_set_tlsext_host_name(s, new String(longHostname));
- fail();
- } catch (SSLException expected) {
- }
-
- assertNull(NativeCrypto.SSL_get_servername(s));
- NativeCrypto.SSL_set_tlsext_host_name(s, new String(hostname));
- assertEquals(hostname, NativeCrypto.SSL_get_servername(s));
-
+ NativeCrypto.SSL_set_tlsext_host_name(s, null);
+ } finally {
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
+ }
- final ServerSocket listener = new ServerSocket(0);
+ @Test(expected = SSLException.class)
+ public void SSL_set_tlsext_host_name_withTooLongHostnameShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+
+ try {
+ char[] longHostname = new char[256];
+ Arrays.fill(longHostname, 'w');
+ NativeCrypto.SSL_set_tlsext_host_name(s, new String(longHostname));
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
+ @Test
+ public void test_SSL_set_tlsext_host_name() throws Exception {
+ final String hostname = "www.android.com";
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+
+ assertNull(NativeCrypto.SSL_get_servername(s));
+ NativeCrypto.SSL_set_tlsext_host_name(s, hostname);
+ assertEquals(hostname, NativeCrypto.SSL_get_servername(s));
+
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+
+ final ServerSocket listener = newServerSocket();
// normal
Hooks cHooks = new Hooks() {
@@ -1882,7 +2023,7 @@
}
};
- ServerSocket listener = new ServerSocket(0);
+ ServerSocket listener = newServerSocket();
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, clientAlpnProtocols);
Future<TestSSLHandshakeCallbacks> server =
@@ -1891,15 +2032,13 @@
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
- @Test
- public void test_SSL_get_servername_null() throws Exception {
- // NULL SSL
- try {
- NativeCrypto.SSL_get_servername(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
+ @Test(expected = NullPointerException.class)
+ public void test_SSL_get_servername_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_get_servername(NULL);
+ }
+ @Test
+ public void SSL_get_servername_shouldReturnNull() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c);
assertNull(NativeCrypto.SSL_get_servername(s));
@@ -1909,50 +2048,14 @@
// additional positive testing by test_SSL_set_tlsext_host_name
}
- @Ignore("TODO(nathanmittler): Determine why this doesn't pass on openjdk")
- @Test
- public void test_SSL_renegotiate() throws Exception {
- try {
- NativeCrypto.SSL_renegotiate(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
- Hooks cHooks = new Hooks() {
- @Override
- public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
- SSLHandshakeCallbacks callback) throws Exception {
- byte[] buffer = new byte[1];
- NativeCrypto.SSL_read(s, fd, callback, buffer, 0, 1, 0);
- assertEquals(42, buffer[0]);
- super.afterHandshake(session, s, c, sock, fd, callback);
- }
- };
- Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
- @Override
- public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
- SSLHandshakeCallbacks callback) throws Exception {
- NativeCrypto.SSL_renegotiate(s);
- NativeCrypto.SSL_write(s, fd, callback, new byte[] {42}, 0, 1, 0);
- super.afterHandshake(session, s, c, sock, fd, callback);
- }
- };
- Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
- Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
- client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
- server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ @Test(expected = NullPointerException.class)
+ public void SSL_get_certificate_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_get_certificate(NULL);
}
@Test
public void test_SSL_get_certificate() throws Exception {
- try {
- NativeCrypto.SSL_get_certificate(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
@@ -1976,15 +2079,14 @@
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_get_peer_cert_chain_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_get_peer_cert_chain(NULL);
+ }
+
@Test
public void test_SSL_get_peer_cert_chain() throws Exception {
- try {
- NativeCrypto.SSL_get_peer_cert_chain(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
@@ -2005,70 +2107,82 @@
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
- final byte[] BYTES = new byte[] {2, -3, 5, 127, 0, -128};
+ @Test
+ public void test_SSL_cipher_names() throws Exception {
+ final ServerSocket listener = newServerSocket();
+ Hooks cHooks = new Hooks();
+ Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+ // Both legacy and standard names are accepted.
+ cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-GCM-SHA256");
+ sHooks.enabledCipherSuites =
+ Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+ Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+ client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ // The standard name is always reported.
+ assertEquals("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", cHooks.negotiatedCipherSuite);
+ assertEquals("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", sHooks.negotiatedCipherSuite);
+ }
+
+ private final byte[] BYTES = new byte[] {2, -3, 5, 127, 0, -128};
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_read_withNullSslShouldThrow() throws Exception {
+ NativeCrypto.SSL_read(NULL, null, null, null, 0, 0, 0);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_read_withNullFdShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_read(s, null, DUMMY_CB, null, 0, 0, 0);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_read_withNullCallbacksShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_read(s, INVALID_FD, null, null, 0, 0, 0);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_read_withNullBytesShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, null, 0, 0, 0);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
+ @Test(expected = SSLException.class)
+ public void SSL_read_beforeHandshakeShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
@Test
public void test_SSL_read() throws Exception {
- // NULL ssl
- try {
- NativeCrypto.SSL_read(NULL, null, null, null, 0, 0, 0);
- fail();
- } catch (NullPointerException expected) {
- }
-
- // null FileDescriptor
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_read(s, null, DUMMY_CB, null, 0, 0, 0);
- fail();
- } catch (NullPointerException expected) {
- }
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
-
- // null SSLHandshakeCallbacks
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_read(s, INVALID_FD, null, null, 0, 0, 0);
- fail();
- } catch (NullPointerException expected) {
- }
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
-
- // null byte array
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, null, 0, 0, 0);
- fail();
- } catch (NullPointerException expected) {
- }
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
-
- // handshaking not yet performed
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
- fail();
- } catch (SSLException expected) {
- }
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
// normal case
{
@@ -2127,84 +2241,78 @@
}
}
- @Test
- public void test_SSL_write() throws Exception {
+ @Test(expected = NullPointerException.class)
+ public void SSL_write_withNullSslShouldThrow() throws Exception {
+ NativeCrypto.SSL_write(NULL, null, null, null, 0, 0, 0);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void SSL_write_withNullFdShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
try {
- NativeCrypto.SSL_write(NULL, null, null, null, 0, 0, 0);
- fail();
- } catch (NullPointerException expected) {
- }
-
- // null FileDescriptor
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_write(s, null, DUMMY_CB, null, 0, 1, 0);
- fail();
- } catch (NullPointerException expected) {
- }
+ NativeCrypto.SSL_write(s, null, DUMMY_CB, null, 0, 1, 0);
+ } finally {
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
+ }
- // null SSLHandshakeCallbacks
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_write(s, INVALID_FD, null, null, 0, 1, 0);
- fail();
- } catch (NullPointerException expected) {
- }
+ @Test(expected = NullPointerException.class)
+ public void SSL_write_withNullCallbacksShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_write(s, INVALID_FD, null, null, 0, 1, 0);
+ } finally {
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
+ }
- // null byte array
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, null, 0, 1, 0);
- fail();
- } catch (NullPointerException expected) {
- }
+ @Test(expected = NullPointerException.class)
+ public void SSL_write_withNullBytesShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, null, 0, 1, 0);
+ } finally {
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
+ }
- // handshaking not yet performed
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
- fail();
- } catch (SSLException expected) {
- }
+ @Test(expected = SSLException.class)
+ public void SSL_write_beforeHandshakeShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
+ } finally {
NativeCrypto.SSL_free(s);
NativeCrypto.SSL_CTX_free(c);
}
+ }
- // positively tested by test_SSL_read
+ @Test
+ public void SSL_interrupt_withNullShouldSucceed() {
+ // SSL_interrupt is a rare case that tolerates a null SSL argument
+ NativeCrypto.SSL_interrupt(NULL);
+ }
+
+ @Test
+ public void SSL_interrupt_withoutHandshakeShouldSucceed() throws Exception {
+ // also works without handshaking
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ NativeCrypto.SSL_interrupt(s);
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
}
@Test
public void test_SSL_interrupt() throws Exception {
- // SSL_interrupt is a rare case that tolerates a null SSL argument
- NativeCrypto.SSL_interrupt(NULL);
-
- // also works without handshaking
- {
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- NativeCrypto.SSL_interrupt(s);
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
@@ -2222,9 +2330,10 @@
@Override
public void run() {
try {
- Thread.sleep(1 * 1000);
+ Thread.sleep(1000);
NativeCrypto.SSL_interrupt(s);
} catch (Exception e) {
+ // Expected.
}
}
}.start();
@@ -2254,7 +2363,7 @@
}
@Test
- public void test_SSL_shutdown() throws Exception {
+ public void SSL_shutdown_withNullFdShouldSucceed() throws Exception {
// We tolerate a null FileDescriptor
wrapWithSSLSession(new SSLSessionWrappedTask() {
@Override
@@ -2262,32 +2371,31 @@
NativeCrypto.SSL_shutdown(sslSession, null, DUMMY_CB);
}
});
+ }
- // null SSLHandshakeCallbacks
+ @Test(expected = NullPointerException.class)
+ public void SSL_shutdown_withNullCallbacksShouldThrow() throws Exception {
wrapWithSSLSession(new SSLSessionWrappedTask() {
@Override
public void run(long sslSession) throws Exception {
- try {
- NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, null);
- fail();
- } catch (NullPointerException expected) {
- // Ignored.
- }
+ NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, null);
}
});
+ }
+ @Test
+ public void SSL_shutdown_withNullSslShouldSucceed() throws Exception {
// SSL_shutdown is a rare case that tolerates a null SSL argument
NativeCrypto.SSL_shutdown(NULL, INVALID_FD, DUMMY_CB);
+ }
+ @Test(expected = SocketException.class)
+ public void SSL_shutdown_beforeHandshakeShouldThrow() throws Exception {
// handshaking not yet performed
wrapWithSSLSession(new SSLSessionWrappedTask() {
@Override
public void run(long sslSession) throws Exception {
- try {
- NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, DUMMY_CB);
- fail();
- } catch (SocketException expected) {
- }
+ NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, DUMMY_CB);
}
});
@@ -2295,14 +2403,13 @@
// SSL_shutdown to ensure SSL_SESSIONs are reused.
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_free_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_free(NULL);
+ }
+
@Test
public void test_SSL_free() throws Exception {
- try {
- NativeCrypto.SSL_free(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
long c = NativeCrypto.SSL_CTX_new();
NativeCrypto.SSL_free(NativeCrypto.SSL_new(c));
NativeCrypto.SSL_CTX_free(c);
@@ -2311,15 +2418,14 @@
// uses use SSL_free to cleanup in afterHandshake.
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_SESSION_session_id_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_SESSION_session_id(NULL);
+ }
+
@Test
public void test_SSL_SESSION_session_id() throws Exception {
- try {
- NativeCrypto.SSL_SESSION_session_id(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
@@ -2338,15 +2444,14 @@
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_SESSION_get_time_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_SESSION_get_time(NULL);
+ }
+
@Test
public void test_SSL_SESSION_get_time() throws Exception {
- try {
- NativeCrypto.SSL_SESSION_get_time(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
{
Hooks cHooks = new Hooks() {
@@ -2367,15 +2472,14 @@
}
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_SESSION_get_version_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_SESSION_get_version(NULL);
+ }
+
@Test
public void test_SSL_SESSION_get_version() throws Exception {
- try {
- NativeCrypto.SSL_SESSION_get_version(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
@@ -2393,22 +2497,24 @@
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
+ @Test(expected = NullPointerException.class)
+ public void SSL_SESSION_cipher_withNullShouldThrow() throws Exception {
+ NativeCrypto.SSL_SESSION_cipher(NULL);
+ }
+
@Test
public void test_SSL_SESSION_cipher() throws Exception {
- try {
- NativeCrypto.SSL_SESSION_cipher(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
SSLHandshakeCallbacks callback) throws Exception {
- String a = NativeCrypto.SSL_SESSION_cipher(session);
- assertTrue(NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.containsKey(a));
+ String nativeCipher = NativeCrypto.SSL_SESSION_cipher(session);
+ String javaCipher = NativeCrypto.cipherSuiteFromJava(nativeCipher);
+ assertTrue(NativeCrypto.SUPPORTED_CIPHER_SUITES_SET.contains(javaCipher));
+ // SSL_SESSION_cipher should return a standard name rather than an OpenSSL name.
+ assertTrue(nativeCipher.startsWith("TLS_"));
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
@@ -2428,15 +2534,14 @@
NativeCrypto.SSL_SESSION_free(NULL);
}
+ @Test(expected = NullPointerException.class)
+ public void i2d_SSL_Session_WithNullSessionShouldThrow() throws Exception {
+ NativeCrypto.i2d_SSL_SESSION(NULL);
+ }
+
@Test
public void test_i2d_SSL_SESSION() throws Exception {
- try {
- NativeCrypto.i2d_SSL_SESSION(NULL);
- fail();
- } catch (NullPointerException expected) {
- }
-
- final ServerSocket listener = new ServerSocket(0);
+ final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
@@ -2494,8 +2599,8 @@
NativeCrypto.RAND_bytes(output);
boolean isZero = true;
- for (int i = 0; i < output.length; i++) {
- isZero &= (output[i] == 0);
+ for (byte anOutput : output) {
+ isZero &= (anOutput == 0);
}
assertFalse("Random output was zero. This is a very low probability event (1 in 2^128) "
@@ -2503,14 +2608,9 @@
isZero);
}
- @Test
- public void test_RAND_bytes_Null_Failure() throws Exception {
- byte[] output = null;
- try {
- NativeCrypto.RAND_bytes(output);
- fail("Should be an error on null buffer input");
- } catch (RuntimeException expected) {
- }
+ @Test(expected = RuntimeException.class)
+ public void RAND_bytes_withNullShouldThrow() throws Exception {
+ NativeCrypto.RAND_bytes(null);
}
@Test(expected = NullPointerException.class)
@@ -2518,16 +2618,19 @@
NativeCrypto.EVP_get_digestbyname(null);
}
+ @Test(expected = RuntimeException.class)
+ public void EVP_get_digestbyname_withEmptyShouldThrow() throws Exception {
+ NativeCrypto.EVP_get_digestbyname("");
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void EVP_get_digestbyname_withInvalidDigestShouldThrow() throws Exception {
+ NativeCrypto.EVP_get_digestbyname("foobar");
+ }
+
@Test
public void test_EVP_get_digestbyname() throws Exception {
assertTrue(NativeCrypto.EVP_get_digestbyname("sha256") != NULL);
-
- try {
- NativeCrypto.EVP_get_digestbyname("");
- NativeCrypto.EVP_get_digestbyname("foobar");
- fail();
- } catch (RuntimeException expected) {
- }
}
@Test
@@ -2555,12 +2658,14 @@
NativeCrypto.EVP_DigestSignInit(ctx, 0, pkey);
fail();
} catch (RuntimeException expected) {
+ // Expected.
}
try {
NativeCrypto.EVP_DigestSignInit(ctx, evpMd, null);
fail();
} catch (RuntimeException expected) {
+ // Expected.
}
}
@@ -2569,18 +2674,14 @@
NativeCrypto.get_RSA_private_params(null);
}
- @Test
+ @Test(expected = RuntimeException.class)
public void test_get_RSA_private_params() throws Exception {
// Test getting params for the wrong kind of key.
final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
assertFalse(groupCtx == NULL);
NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
- try {
- NativeCrypto.get_RSA_private_params(ctx);
- fail();
- } catch (RuntimeException expected) {
- }
+ NativeCrypto.get_RSA_private_params(ctx);
}
@Test(expected = NullPointerException.class)
@@ -2588,18 +2689,14 @@
NativeCrypto.get_RSA_public_params(null);
}
- @Test
+ @Test(expected = RuntimeException.class)
public void test_get_RSA_public_params() throws Exception {
// Test getting params for the wrong kind of key.
final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
assertFalse(groupCtx == NULL);
NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
- try {
- NativeCrypto.get_RSA_public_params(ctx);
- fail();
- } catch (RuntimeException expected) {
- }
+ NativeCrypto.get_RSA_public_params(ctx);
}
@Test(expected = NullPointerException.class)
@@ -2654,8 +2751,6 @@
long groupRef = NativeCrypto.EC_GROUP_new_by_curve_name(name);
assertFalse(groupRef == NULL);
NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupRef);
- assertEquals(NativeCrypto.OBJ_txt2nid_longName(name),
- NativeCrypto.EC_GROUP_get_curve_name(group));
// prime
BigInteger p = new BigInteger(pStr, 16);
@@ -2744,6 +2839,7 @@
NativeCrypto.ECDH_compute_key(out, outOffset, null, pkey2Ref);
fail();
} catch (NullPointerException expected) {
+ // Expected.
}
// Assert that it fails when only the second key is null
@@ -2751,21 +2847,22 @@
NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, null);
fail();
} catch (NullPointerException expected) {
+ // Expected.
}
}
+ @Test(expected = NullPointerException.class)
+ public void EVP_CipherInit_ex_withNullCtxShouldThrow() throws Exception {
+ final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+ NativeCrypto.EVP_CipherInit_ex(null, evpCipher, null, null, true);
+ }
+
@Test
public void test_EVP_CipherInit_ex_Null_Failure() throws Exception {
final NativeRef.EVP_CIPHER_CTX ctx =
new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
- try {
- NativeCrypto.EVP_CipherInit_ex(null, evpCipher, null, null, true);
- fail("Null context should throw NullPointerException");
- } catch (NullPointerException expected) {
- }
-
/* Initialize encrypting. */
NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, true);
NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, true);
@@ -2936,7 +3033,7 @@
@Test(expected = NullPointerException.class)
public void EVP_PKEY_CTX_set_rsa_oaep_md_NullMdCtx() throws Exception {
long pkeyCtx = getRawPkeyCtxForEncrypt();
- NativeRef.EVP_PKEY_CTX holder = new NativeRef.EVP_PKEY_CTX(pkeyCtx);
+ new NativeRef.EVP_PKEY_CTX(pkeyCtx);
NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx, NULL);
}
@@ -2954,4 +3051,8 @@
}
fail("\"" + actualValue + "\" does not contain \"" + expectedSubstring + "\"");
}
+
+ private static ServerSocket newServerSocket() throws IOException {
+ return new ServerSocket(0, 50, InetAddress.getLoopbackAddress());
+ }
}
diff --git a/openjdk/src/test/java/org/conscrypt/NativeRefTest.java b/openjdk/src/test/java/org/conscrypt/NativeRefTest.java
index ed84813..e13297b 100644
--- a/openjdk/src/test/java/org/conscrypt/NativeRefTest.java
+++ b/openjdk/src/test/java/org/conscrypt/NativeRefTest.java
@@ -21,7 +21,11 @@
public class NativeRefTest extends TestCase {
public void test_zeroContextThrowsNullPointException() {
try {
- new NativeRef(0) {};
+ new NativeRef(0) {
+ @Override
+ void doFree(long context) {
+ }
+ };
fail("Should throw NullPointerException when arguments are NULL");
} catch (NullPointerException expected) {
}
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLExtendedSessionImplTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLExtendedSessionImplTest.java
deleted file mode 100644
index f3414a9..0000000
--- a/openjdk/src/test/java/org/conscrypt/OpenSSLExtendedSessionImplTest.java
+++ /dev/null
@@ -1,46 +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 impli$
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.conscrypt;
-
-import java.util.List;
-import javax.net.ssl.ExtendedSSLSession;
-import javax.net.ssl.SNIHostName;
-import javax.net.ssl.SNIServerName;
-import junit.framework.TestCase;
-
-/**
- * Test for OpenSSLExtendedSessionImpl
- */
-public class OpenSSLExtendedSessionImplTest extends TestCase {
- static class MockSSLSession extends OpenSSLSessionImpl {
- MockSSLSession() {
- super(0, null, null, null, null, null, 0, null);
- }
-
- @Override
- public String getRequestedServerName() {
- return "server.name";
- }
- }
-
- public void test_getRequestedServerNames() {
- AbstractOpenSSLSession session = new MockSSLSession();
- ExtendedSSLSession extendedSession = new OpenSSLExtendedSessionImpl(session);
- List<SNIServerName> names = extendedSession.getRequestedServerNames();
- assertEquals("server.name", ((SNIHostName) names.get(0)).getAsciiName());
- }
-}
diff --git a/openjdk/src/test/java/org/conscrypt/PlatformTest.java b/openjdk/src/test/java/org/conscrypt/PlatformTest.java
index c090d2d..1d7ea75 100644
--- a/openjdk/src/test/java/org/conscrypt/PlatformTest.java
+++ b/openjdk/src/test/java/org/conscrypt/PlatformTest.java
@@ -37,8 +37,8 @@
params.setServerNames(names);
params.setUseCipherSuitesOrder(false);
params.setEndpointIdentificationAlgorithm("ABC");
- Platform.setSSLParameters(params, impl, (OpenSSLSocketImpl)socket);
- assertEquals("some.host", ((OpenSSLSocketImpl)socket).getHostname());
+ Platform.setSSLParameters(params, impl, (AbstractConscryptSocket)socket);
+ assertEquals("some.host", ((AbstractConscryptSocket)socket).getHostname());
assertFalse(impl.getUseCipherSuitesOrder());
assertEquals("ABC", impl.getEndpointIdentificationAlgorithm());
}
@@ -49,8 +49,8 @@
SSLParameters params = new SSLParameters();
impl.setUseCipherSuitesOrder(false);
impl.setEndpointIdentificationAlgorithm("ABC");
- ((OpenSSLSocketImpl)socket).setHostname("some.host");
- Platform.getSSLParameters(params, impl, (OpenSSLSocketImpl)socket);
+ ((AbstractConscryptSocket)socket).setHostname("some.host");
+ Platform.getSSLParameters(params, impl, (AbstractConscryptSocket)socket);
assertEquals("some.host", ((SNIHostName)params.getServerNames().get(0)).getAsciiName());
assertFalse(params.getUseCipherSuitesOrder());
assertEquals("ABC", params.getEndpointIdentificationAlgorithm());
@@ -59,14 +59,14 @@
public void test_setSSLParameters_Engine() throws Exception {
SSLParametersImpl impl = SSLParametersImpl.getDefault();
SSLParameters params = new SSLParameters();
- OpenSSLEngineImpl engine = new OpenSSLEngineImpl(impl);
+ ConscryptEngine engine = new ConscryptEngine(impl);
List<SNIServerName> names = new ArrayList<SNIServerName>();
names.add(new SNIHostName("some.host"));
params.setServerNames(names);
params.setUseCipherSuitesOrder(false);
params.setEndpointIdentificationAlgorithm("ABC");
Platform.setSSLParameters(params, impl, engine);
- assertEquals("some.host", engine.getSniHostname());
+ assertEquals("some.host", engine.getHostname());
assertFalse(impl.getUseCipherSuitesOrder());
assertEquals("ABC", impl.getEndpointIdentificationAlgorithm());
}
@@ -74,10 +74,10 @@
public void test_getSSLParameters_Engine() throws Exception {
SSLParametersImpl impl = SSLParametersImpl.getDefault();
SSLParameters params = new SSLParameters();
- OpenSSLEngineImpl engine = new OpenSSLEngineImpl(impl);
+ ConscryptEngine engine = new ConscryptEngine(impl);
impl.setUseCipherSuitesOrder(false);
impl.setEndpointIdentificationAlgorithm("ABC");
- engine.setSniHostname("some.host");
+ engine.setHostname("some.host");
Platform.getSSLParameters(params, impl, engine);
assertEquals("some.host", ((SNIHostName)params.getServerNames().get(0)).getAsciiName());
assertFalse(params.getUseCipherSuitesOrder());
diff --git a/openjdk/src/test/java/org/conscrypt/SSLParametersImplTest.java b/openjdk/src/test/java/org/conscrypt/SSLParametersImplTest.java
deleted file mode 100644
index 47837c7..0000000
--- a/openjdk/src/test/java/org/conscrypt/SSLParametersImplTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 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.Arrays;
-import java.util.HashSet;
-import junit.framework.TestCase;
-
-public class SSLParametersImplTest extends TestCase {
-
- public void testGetClientKeyType() throws Exception {
- // See http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
- byte b = Byte.MIN_VALUE;
- do {
- String byteString = Byte.toString(b);
- String keyType = SSLParametersImpl.getClientKeyType(b);
- switch (b) {
- case 1:
- assertEquals(byteString, "RSA", keyType);
- break;
- case 3:
- assertEquals(byteString, "DH_RSA", keyType);
- break;
- case 64:
- assertEquals(byteString, "EC", keyType);
- break;
- case 65:
- assertEquals(byteString, "EC_RSA", keyType);
- break;
- case 66:
- assertEquals(byteString, "EC_EC", keyType);
- break;
- default:
- assertNull(byteString, keyType);
- }
- b++;
- } while (b != Byte.MIN_VALUE);
- }
-
- public void testGetSupportedClientKeyTypes() throws Exception {
- // Create an array with all possible values. Also, duplicate all values.
- byte[] allClientCertificateTypes = new byte[512];
- for (int i = 0; i < allClientCertificateTypes.length; i++) {
- allClientCertificateTypes[i] = (byte) i;
- }
- assertEquals(
- new HashSet<String>(Arrays.asList("RSA", "DH_RSA", "EC", "EC_RSA", "EC_EC")),
- SSLParametersImpl.getSupportedClientKeyTypes(allClientCertificateTypes));
- }
-}
diff --git a/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java b/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java
index c7f4d9d..dcffec7 100644
--- a/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java
+++ b/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java
@@ -18,7 +18,11 @@
import static org.conscrypt.TestUtils.UTF_8;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import java.util.Arrays;
+import java.util.HashSet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -62,6 +66,38 @@
assertArrayEquals(expected, actual);
}
+ @Test
+ public void testGetClientKeyType() throws Exception {
+ // See http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
+ byte b = Byte.MIN_VALUE;
+ do {
+ String byteString = Byte.toString(b);
+ String keyType = SSLUtils.getClientKeyType(b);
+ switch (b) {
+ case 1:
+ assertEquals(byteString, "RSA", keyType);
+ break;
+ case 64:
+ assertEquals(byteString, "EC", keyType);
+ break;
+ default:
+ assertNull(byteString, keyType);
+ }
+ b++;
+ } while (b != Byte.MIN_VALUE);
+ }
+
+ @Test
+ public void testGetSupportedClientKeyTypes() throws Exception {
+ // Create an array with all possible values. Also, duplicate all values.
+ byte[] allClientCertificateTypes = new byte[512];
+ for (int i = 0; i < allClientCertificateTypes.length; i++) {
+ allClientCertificateTypes[i] = (byte) i;
+ }
+ assertEquals(new HashSet<String>(Arrays.asList("RSA", "EC")),
+ SSLUtils.getSupportedClientKeyTypes(allClientCertificateTypes));
+ }
+
private static String[] toStrings(byte[][] protocols) {
int numProtocols = protocols.length;
String[] out = new String[numProtocols];
diff --git a/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java
new file mode 100644
index 0000000..7d1a8ce
--- /dev/null
+++ b/openjdk/src/test/java/org/conscrypt/ServerSessionContextTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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 java.util.Enumeration;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ServerSessionContextTest extends AbstractSessionContextTest<ServerSessionContext> {
+
+ @Override
+ ServerSessionContext newContext() {
+ return new ServerSessionContext();
+ }
+
+ @Override
+ SslSessionWrapper getCachedSession(ServerSessionContext context, SslSessionWrapper s) {
+ return context.getSessionFromCache(s.getId());
+ }
+
+ @Override
+ int size(ServerSessionContext context) {
+ int count = 0;
+ Enumeration<byte[]> ids = context.getIds();
+ while (ids.hasMoreElements()) {
+ ids.nextElement();
+ count++;
+ }
+ return count;
+ }
+}
diff --git a/openjdk/src/test/java/org/conscrypt/SslSessionWrapperTest.java b/openjdk/src/test/java/org/conscrypt/SslSessionWrapperTest.java
new file mode 100644
index 0000000..dc600d4
--- /dev/null
+++ b/openjdk/src/test/java/org/conscrypt/SslSessionWrapperTest.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License", "www.google.com", 443);
+ * 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SslSessionWrapperTest {
+ /*
+ * Taken from external/boringssl/src/ssl/ssl_test.cc: kOpenSSLSession is a
+ * serialized SSL_SESSION.
+ */
+ private static final byte[] kOpenSSLSession = new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x05,
+ (byte) 0xAA, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x02,
+ (byte) 0x03, (byte) 0x03, (byte) 0x04, (byte) 0x02, (byte) 0xC0, (byte) 0x2F,
+ (byte) 0x04, (byte) 0x20, (byte) 0x06, (byte) 0xE5, (byte) 0x0D, (byte) 0x67,
+ (byte) 0x76, (byte) 0xAE, (byte) 0x18, (byte) 0x7E, (byte) 0x66, (byte) 0xDE,
+ (byte) 0xA3, (byte) 0x5C, (byte) 0xF0, (byte) 0x2E, (byte) 0x43, (byte) 0x51,
+ (byte) 0x2A, (byte) 0x60, (byte) 0x97, (byte) 0x19, (byte) 0xD3, (byte) 0x60,
+ (byte) 0x5A, (byte) 0xF1, (byte) 0x93, (byte) 0xDD, (byte) 0xCB, (byte) 0x24,
+ (byte) 0x57, (byte) 0x4C, (byte) 0x90, (byte) 0x90, (byte) 0x04, (byte) 0x30,
+ (byte) 0x26, (byte) 0x5A, (byte) 0xE5, (byte) 0xCE, (byte) 0x40, (byte) 0x16,
+ (byte) 0x04, (byte) 0xE5, (byte) 0xA2, (byte) 0x2E, (byte) 0x3F, (byte) 0xE3,
+ (byte) 0x27, (byte) 0xBE, (byte) 0x83, (byte) 0xEE, (byte) 0x5F, (byte) 0x94,
+ (byte) 0x5E, (byte) 0x88, (byte) 0xB3, (byte) 0x3F, (byte) 0x62, (byte) 0x88,
+ (byte) 0xD8, (byte) 0x2E, (byte) 0xC8, (byte) 0xD8, (byte) 0x57, (byte) 0x1C,
+ (byte) 0xA8, (byte) 0xC9, (byte) 0x88, (byte) 0x7C, (byte) 0x59, (byte) 0xA6,
+ (byte) 0x91, (byte) 0x4C, (byte) 0xB7, (byte) 0xDA, (byte) 0x72, (byte) 0x09,
+ (byte) 0xD2, (byte) 0x66, (byte) 0x47, (byte) 0x21, (byte) 0x6A, (byte) 0x09,
+ (byte) 0xA1, (byte) 0x06, (byte) 0x02, (byte) 0x04, (byte) 0x54, (byte) 0x43,
+ (byte) 0x3B, (byte) 0x8E, (byte) 0xA2, (byte) 0x04, (byte) 0x02, (byte) 0x02,
+ (byte) 0x01, (byte) 0x2C, (byte) 0xA3, (byte) 0x82, (byte) 0x04, (byte) 0x7A,
+ (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x76, (byte) 0x30, (byte) 0x82,
+ (byte) 0x03, (byte) 0x5E, (byte) 0xA0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x08, (byte) 0x2B, (byte) 0xD7, (byte) 0x54,
+ (byte) 0xBE, (byte) 0xC3, (byte) 0xD6, (byte) 0x4A, (byte) 0x55, (byte) 0x30,
+ (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48,
+ (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+ (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x49, (byte) 0x31, (byte) 0x0B,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
+ (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0A, (byte) 0x13, (byte) 0x0A, (byte) 0x47, (byte) 0x6F,
+ (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x20, (byte) 0x49,
+ (byte) 0x6E, (byte) 0x63, (byte) 0x31, (byte) 0x25, (byte) 0x30, (byte) 0x23,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x13,
+ (byte) 0x1C, (byte) 0x47, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
+ (byte) 0x65, (byte) 0x20, (byte) 0x49, (byte) 0x6E, (byte) 0x74, (byte) 0x65,
+ (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x41,
+ (byte) 0x75, (byte) 0x74, (byte) 0x68, (byte) 0x6F, (byte) 0x72, (byte) 0x69,
+ (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x47, (byte) 0x32, (byte) 0x30,
+ (byte) 0x1E, (byte) 0x17, (byte) 0x0D, (byte) 0x31, (byte) 0x34, (byte) 0x31,
+ (byte) 0x30, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x30,
+ (byte) 0x37, (byte) 0x35, (byte) 0x37, (byte) 0x5A, (byte) 0x17, (byte) 0x0D,
+ (byte) 0x31, (byte) 0x35, (byte) 0x30, (byte) 0x31, (byte) 0x30, (byte) 0x36,
+ (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
+ (byte) 0x5A, (byte) 0x30, (byte) 0x68, (byte) 0x31, (byte) 0x0B, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13,
+ (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x0C, (byte) 0x0A, (byte) 0x43, (byte) 0x61, (byte) 0x6C,
+ (byte) 0x69, (byte) 0x66, (byte) 0x6F, (byte) 0x72, (byte) 0x6E, (byte) 0x69,
+ (byte) 0x61, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0C, (byte) 0x0D,
+ (byte) 0x4D, (byte) 0x6F, (byte) 0x75, (byte) 0x6E, (byte) 0x74, (byte) 0x61,
+ (byte) 0x69, (byte) 0x6E, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
+ (byte) 0x77, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0A, (byte) 0x0C, (byte) 0x0A,
+ (byte) 0x47, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65,
+ (byte) 0x20, (byte) 0x49, (byte) 0x6E, (byte) 0x63, (byte) 0x31, (byte) 0x17,
+ (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x03, (byte) 0x0C, (byte) 0x0E, (byte) 0x77, (byte) 0x77, (byte) 0x77,
+ (byte) 0x2E, (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
+ (byte) 0x65, (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0x22, (byte) 0x30, (byte) 0x0D, (byte) 0x06,
+ (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7,
+ (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00,
+ (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x0F, (byte) 0x00, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0x0A, (byte) 0x02, (byte) 0x82, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00, (byte) 0x9C, (byte) 0x29, (byte) 0xE2, (byte) 0xEB,
+ (byte) 0xA6, (byte) 0x50, (byte) 0x02, (byte) 0xF8, (byte) 0xBA, (byte) 0x1F,
+ (byte) 0xCB, (byte) 0xCB, (byte) 0x7F, (byte) 0xC0, (byte) 0x3C, (byte) 0x2D,
+ (byte) 0x07, (byte) 0xA7, (byte) 0xAE, (byte) 0xEF, (byte) 0x60, (byte) 0x95,
+ (byte) 0xA7, (byte) 0x47, (byte) 0x09, (byte) 0xE1, (byte) 0x5D, (byte) 0xE5,
+ (byte) 0x92, (byte) 0x73, (byte) 0x7A, (byte) 0x86, (byte) 0xE1, (byte) 0xFD,
+ (byte) 0x72, (byte) 0xDE, (byte) 0x85, (byte) 0x16, (byte) 0x4E, (byte) 0xF4,
+ (byte) 0xA1, (byte) 0x12, (byte) 0x21, (byte) 0xFD, (byte) 0x50, (byte) 0x4D,
+ (byte) 0x04, (byte) 0x1C, (byte) 0xFD, (byte) 0xD3, (byte) 0x48, (byte) 0xD8,
+ (byte) 0xCB, (byte) 0xEE, (byte) 0xF5, (byte) 0xD7, (byte) 0x52, (byte) 0x66,
+ (byte) 0xD5, (byte) 0xBF, (byte) 0x22, (byte) 0xA8, (byte) 0xE4, (byte) 0xD0,
+ (byte) 0xF5, (byte) 0xA4, (byte) 0xF9, (byte) 0x0B, (byte) 0xB4, (byte) 0x84,
+ (byte) 0x84, (byte) 0xD7, (byte) 0x10, (byte) 0x14, (byte) 0x9B, (byte) 0xEA,
+ (byte) 0xCC, (byte) 0x7D, (byte) 0xDE, (byte) 0x30, (byte) 0xF9, (byte) 0x1B,
+ (byte) 0xE9, (byte) 0x94, (byte) 0x96, (byte) 0x1A, (byte) 0x6D, (byte) 0x72,
+ (byte) 0x18, (byte) 0x5E, (byte) 0xCC, (byte) 0x09, (byte) 0x04, (byte) 0xC6,
+ (byte) 0x41, (byte) 0x71, (byte) 0x76, (byte) 0xD1, (byte) 0x29, (byte) 0x3F,
+ (byte) 0x3B, (byte) 0x5E, (byte) 0x85, (byte) 0x4A, (byte) 0x30, (byte) 0x32,
+ (byte) 0x9D, (byte) 0x4F, (byte) 0xDB, (byte) 0xDE, (byte) 0x82, (byte) 0x66,
+ (byte) 0x39, (byte) 0xCB, (byte) 0x5C, (byte) 0xC9, (byte) 0xC5, (byte) 0x98,
+ (byte) 0x91, (byte) 0x8D, (byte) 0x32, (byte) 0xB5, (byte) 0x2F, (byte) 0xE4,
+ (byte) 0xDC, (byte) 0xB0, (byte) 0x6E, (byte) 0x21, (byte) 0xDE, (byte) 0x39,
+ (byte) 0x3C, (byte) 0x96, (byte) 0xA8, (byte) 0x32, (byte) 0xA8, (byte) 0xC1,
+ (byte) 0xD1, (byte) 0x6C, (byte) 0xA9, (byte) 0xAA, (byte) 0xF3, (byte) 0x5E,
+ (byte) 0x24, (byte) 0x70, (byte) 0xB7, (byte) 0xAB, (byte) 0x92, (byte) 0x63,
+ (byte) 0x08, (byte) 0x1E, (byte) 0x11, (byte) 0x3F, (byte) 0xB3, (byte) 0x5F,
+ (byte) 0xC7, (byte) 0x98, (byte) 0xE3, (byte) 0x1D, (byte) 0x2A, (byte) 0xC2,
+ (byte) 0x32, (byte) 0x1C, (byte) 0x3C, (byte) 0x95, (byte) 0x43, (byte) 0x16,
+ (byte) 0xE0, (byte) 0x46, (byte) 0x83, (byte) 0xC6, (byte) 0x36, (byte) 0x91,
+ (byte) 0xF4, (byte) 0xA0, (byte) 0xE1, (byte) 0x3C, (byte) 0xB8, (byte) 0x23,
+ (byte) 0xB2, (byte) 0x4F, (byte) 0x8B, (byte) 0x0C, (byte) 0x8C, (byte) 0x92,
+ (byte) 0x45, (byte) 0x24, (byte) 0x43, (byte) 0x68, (byte) 0x24, (byte) 0x06,
+ (byte) 0x84, (byte) 0x43, (byte) 0x96, (byte) 0x2C, (byte) 0x96, (byte) 0x55,
+ (byte) 0x2F, (byte) 0x32, (byte) 0xE8, (byte) 0xE0, (byte) 0xDE, (byte) 0xBF,
+ (byte) 0x52, (byte) 0x57, (byte) 0x2D, (byte) 0x08, (byte) 0x71, (byte) 0x25,
+ (byte) 0x96, (byte) 0x90, (byte) 0x54, (byte) 0x4A, (byte) 0xF1, (byte) 0x0E,
+ (byte) 0xC8, (byte) 0x58, (byte) 0x1A, (byte) 0xE7, (byte) 0x6A, (byte) 0xAB,
+ (byte) 0xA0, (byte) 0x68, (byte) 0xE0, (byte) 0xAD, (byte) 0xFD, (byte) 0xD6,
+ (byte) 0x39, (byte) 0x0F, (byte) 0x76, (byte) 0xE4, (byte) 0xC1, (byte) 0x70,
+ (byte) 0xCD, (byte) 0xDE, (byte) 0x80, (byte) 0x2B, (byte) 0xE2, (byte) 0x1C,
+ (byte) 0x87, (byte) 0x48, (byte) 0x03, (byte) 0x46, (byte) 0x0F, (byte) 0x2C,
+ (byte) 0x41, (byte) 0xF7, (byte) 0x4B, (byte) 0x1F, (byte) 0x93, (byte) 0xAE,
+ (byte) 0x3F, (byte) 0x57, (byte) 0x1F, (byte) 0x2D, (byte) 0xF5, (byte) 0x35,
+ (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xA3,
+ (byte) 0x82, (byte) 0x01, (byte) 0x41, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+ (byte) 0x3D, (byte) 0x30, (byte) 0x1D, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1D, (byte) 0x25, (byte) 0x04, (byte) 0x16, (byte) 0x30, (byte) 0x14,
+ (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05,
+ (byte) 0x05, (byte) 0x07, (byte) 0x03, (byte) 0x01, (byte) 0x06, (byte) 0x08,
+ (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07,
+ (byte) 0x03, (byte) 0x02, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x1D, (byte) 0x11, (byte) 0x04, (byte) 0x12, (byte) 0x30,
+ (byte) 0x10, (byte) 0x82, (byte) 0x0E, (byte) 0x77, (byte) 0x77, (byte) 0x77,
+ (byte) 0x2E, (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
+ (byte) 0x65, (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x30,
+ (byte) 0x68, (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x07, (byte) 0x01, (byte) 0x01, (byte) 0x04,
+ (byte) 0x5C, (byte) 0x30, (byte) 0x5A, (byte) 0x30, (byte) 0x2B, (byte) 0x06,
+ (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+ (byte) 0x07, (byte) 0x30, (byte) 0x02, (byte) 0x86, (byte) 0x1F, (byte) 0x68,
+ (byte) 0x74, (byte) 0x74, (byte) 0x70, (byte) 0x3A, (byte) 0x2F, (byte) 0x2F,
+ (byte) 0x70, (byte) 0x6B, (byte) 0x69, (byte) 0x2E, (byte) 0x67, (byte) 0x6F,
+ (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x2E, (byte) 0x63,
+ (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x47, (byte) 0x49, (byte) 0x41,
+ (byte) 0x47, (byte) 0x32, (byte) 0x2E, (byte) 0x63, (byte) 0x72, (byte) 0x74,
+ (byte) 0x30, (byte) 0x2B, (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06,
+ (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07, (byte) 0x30, (byte) 0x01,
+ (byte) 0x86, (byte) 0x1F, (byte) 0x68, (byte) 0x74, (byte) 0x74, (byte) 0x70,
+ (byte) 0x3A, (byte) 0x2F, (byte) 0x2F, (byte) 0x63, (byte) 0x6C, (byte) 0x69,
+ (byte) 0x65, (byte) 0x6E, (byte) 0x74, (byte) 0x73, (byte) 0x31, (byte) 0x2E,
+ (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65,
+ (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x6F,
+ (byte) 0x63, (byte) 0x73, (byte) 0x70, (byte) 0x30, (byte) 0x1D, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x0E, (byte) 0x04, (byte) 0x16,
+ (byte) 0x04, (byte) 0x14, (byte) 0x3B, (byte) 0x6B, (byte) 0xE0, (byte) 0x9C,
+ (byte) 0xC6, (byte) 0xC6, (byte) 0x41, (byte) 0xC8, (byte) 0xEA, (byte) 0x5C,
+ (byte) 0xFB, (byte) 0x1A, (byte) 0x58, (byte) 0x15, (byte) 0xC2, (byte) 0x1B,
+ (byte) 0x9D, (byte) 0x43, (byte) 0x19, (byte) 0x85, (byte) 0x30, (byte) 0x0C,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x13, (byte) 0x01,
+ (byte) 0x01, (byte) 0xFF, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
+ (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D,
+ (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80,
+ (byte) 0x14, (byte) 0x4A, (byte) 0xDD, (byte) 0x06, (byte) 0x16, (byte) 0x1B,
+ (byte) 0xBC, (byte) 0xF6, (byte) 0x68, (byte) 0xB5, (byte) 0x76, (byte) 0xF5,
+ (byte) 0x81, (byte) 0xB6, (byte) 0xBB, (byte) 0x62, (byte) 0x1A, (byte) 0xBA,
+ (byte) 0x5A, (byte) 0x81, (byte) 0x2F, (byte) 0x30, (byte) 0x17, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x20, (byte) 0x04, (byte) 0x10,
+ (byte) 0x30, (byte) 0x0E, (byte) 0x30, (byte) 0x0C, (byte) 0x06, (byte) 0x0A,
+ (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x04, (byte) 0x01, (byte) 0xD6,
+ (byte) 0x79, (byte) 0x02, (byte) 0x05, (byte) 0x01, (byte) 0x30, (byte) 0x30,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x1F, (byte) 0x04,
+ (byte) 0x29, (byte) 0x30, (byte) 0x27, (byte) 0x30, (byte) 0x25, (byte) 0xA0,
+ (byte) 0x23, (byte) 0xA0, (byte) 0x21, (byte) 0x86, (byte) 0x1F, (byte) 0x68,
+ (byte) 0x74, (byte) 0x74, (byte) 0x70, (byte) 0x3A, (byte) 0x2F, (byte) 0x2F,
+ (byte) 0x70, (byte) 0x6B, (byte) 0x69, (byte) 0x2E, (byte) 0x67, (byte) 0x6F,
+ (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x2E, (byte) 0x63,
+ (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x47, (byte) 0x49, (byte) 0x41,
+ (byte) 0x47, (byte) 0x32, (byte) 0x2E, (byte) 0x63, (byte) 0x72, (byte) 0x6C,
+ (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01,
+ (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00, (byte) 0x9A, (byte) 0x39, (byte) 0x70, (byte) 0x81,
+ (byte) 0x76, (byte) 0x8A, (byte) 0x94, (byte) 0xCB, (byte) 0x96, (byte) 0xF1,
+ (byte) 0xCA, (byte) 0xAF, (byte) 0x96, (byte) 0xAE, (byte) 0x1D, (byte) 0x73,
+ (byte) 0xB3, (byte) 0x2C, (byte) 0x82, (byte) 0x16, (byte) 0x29, (byte) 0xB5,
+ (byte) 0x3C, (byte) 0x7E, (byte) 0x55, (byte) 0x53, (byte) 0x6F, (byte) 0xB2,
+ (byte) 0xBC, (byte) 0x34, (byte) 0x96, (byte) 0xAE, (byte) 0x00, (byte) 0xD8,
+ (byte) 0xF2, (byte) 0x26, (byte) 0xD1, (byte) 0x18, (byte) 0x99, (byte) 0x9F,
+ (byte) 0x7D, (byte) 0xFD, (byte) 0xEB, (byte) 0xE0, (byte) 0xBB, (byte) 0x9D,
+ (byte) 0xE6, (byte) 0x46, (byte) 0xA5, (byte) 0x74, (byte) 0xAB, (byte) 0x3D,
+ (byte) 0x93, (byte) 0xC6, (byte) 0x25, (byte) 0x28, (byte) 0x3D, (byte) 0xC8,
+ (byte) 0x4C, (byte) 0x6E, (byte) 0xCF, (byte) 0xD1, (byte) 0x84, (byte) 0xFF,
+ (byte) 0x46, (byte) 0x4F, (byte) 0x21, (byte) 0x2E, (byte) 0x07, (byte) 0xC4,
+ (byte) 0xB8, (byte) 0xB7, (byte) 0x2A, (byte) 0xE5, (byte) 0xC7, (byte) 0x34,
+ (byte) 0xC6, (byte) 0xA9, (byte) 0x84, (byte) 0xE3, (byte) 0x6C, (byte) 0x49,
+ (byte) 0xF8, (byte) 0x4A, (byte) 0x36, (byte) 0xBB, (byte) 0x3A, (byte) 0xBD,
+ (byte) 0xAD, (byte) 0x8A, (byte) 0x2B, (byte) 0x73, (byte) 0x97, (byte) 0xA6,
+ (byte) 0x30, (byte) 0x2C, (byte) 0x5F, (byte) 0xE4, (byte) 0xBD, (byte) 0x13,
+ (byte) 0x24, (byte) 0xE5, (byte) 0xD9, (byte) 0xA8, (byte) 0x74, (byte) 0x29,
+ (byte) 0x38, (byte) 0x47, (byte) 0x2E, (byte) 0xA6, (byte) 0xD6, (byte) 0x50,
+ (byte) 0xE0, (byte) 0xE8, (byte) 0xDD, (byte) 0x60, (byte) 0xC7, (byte) 0xD2,
+ (byte) 0xC6, (byte) 0x4E, (byte) 0x54, (byte) 0xCE, (byte) 0xE7, (byte) 0x94,
+ (byte) 0x84, (byte) 0x0D, (byte) 0xE8, (byte) 0x81, (byte) 0x92, (byte) 0x91,
+ (byte) 0x71, (byte) 0x19, (byte) 0x1D, (byte) 0x07, (byte) 0x75, (byte) 0x9E,
+ (byte) 0x59, (byte) 0x1A, (byte) 0x7E, (byte) 0x9D, (byte) 0x84, (byte) 0x61,
+ (byte) 0xC7, (byte) 0x84, (byte) 0xAD, (byte) 0xA3, (byte) 0x6A, (byte) 0xED,
+ (byte) 0xD8, (byte) 0x0D, (byte) 0x0C, (byte) 0x2A, (byte) 0x66, (byte) 0x3D,
+ (byte) 0xD7, (byte) 0xAE, (byte) 0x46, (byte) 0x1D, (byte) 0x4A, (byte) 0x8C,
+ (byte) 0x2B, (byte) 0xD6, (byte) 0x1A, (byte) 0x69, (byte) 0x71, (byte) 0xC3,
+ (byte) 0x5E, (byte) 0xA0, (byte) 0x6E, (byte) 0xED, (byte) 0x27, (byte) 0x9F,
+ (byte) 0xAF, (byte) 0x5B, (byte) 0x92, (byte) 0xA0, (byte) 0x03, (byte) 0xFD,
+ (byte) 0x83, (byte) 0x22, (byte) 0x09, (byte) 0x29, (byte) 0xE8, (byte) 0xA1,
+ (byte) 0x32, (byte) 0x2B, (byte) 0xEC, (byte) 0x1A, (byte) 0xA2, (byte) 0x75,
+ (byte) 0x4C, (byte) 0x3E, (byte) 0x99, (byte) 0x71, (byte) 0xCE, (byte) 0x8B,
+ (byte) 0x31, (byte) 0xEF, (byte) 0x9D, (byte) 0x37, (byte) 0x63, (byte) 0xFC,
+ (byte) 0x71, (byte) 0x91, (byte) 0x10, (byte) 0x1E, (byte) 0xD0, (byte) 0xF5,
+ (byte) 0xCB, (byte) 0x6F, (byte) 0x7A, (byte) 0xBA, (byte) 0x5E, (byte) 0x0C,
+ (byte) 0x8A, (byte) 0xFA, (byte) 0xA4, (byte) 0xDE, (byte) 0x36, (byte) 0xAD,
+ (byte) 0x51, (byte) 0x52, (byte) 0xFC, (byte) 0xFE, (byte) 0x10, (byte) 0xB0,
+ (byte) 0x81, (byte) 0xC8, (byte) 0x7D, (byte) 0x03, (byte) 0xC3, (byte) 0xB8,
+ (byte) 0x3C, (byte) 0x66, (byte) 0x6A, (byte) 0xF5, (byte) 0x6A, (byte) 0x81,
+ (byte) 0x7C, (byte) 0x45, (byte) 0xA6, (byte) 0x23, (byte) 0x21, (byte) 0xE1,
+ (byte) 0xD5, (byte) 0xD3, (byte) 0xED, (byte) 0x6E, (byte) 0x0D, (byte) 0x65,
+ (byte) 0x39, (byte) 0x77, (byte) 0x58, (byte) 0x09, (byte) 0x6B, (byte) 0x63,
+ (byte) 0xA4, (byte) 0x02, (byte) 0x04, (byte) 0x00, (byte) 0xA5, (byte) 0x03,
+ (byte) 0x02, (byte) 0x01, (byte) 0x14, (byte) 0xA9, (byte) 0x05, (byte) 0x02,
+ (byte) 0x03, (byte) 0x01, (byte) 0x89, (byte) 0xC0, (byte) 0xAA, (byte) 0x81,
+ (byte) 0xA7, (byte) 0x04, (byte) 0x81, (byte) 0xA4, (byte) 0x1C, (byte) 0x14,
+ (byte) 0x42, (byte) 0xFA, (byte) 0x1E, (byte) 0x3A, (byte) 0x4D, (byte) 0x0A,
+ (byte) 0x83, (byte) 0x7E, (byte) 0x92, (byte) 0x61, (byte) 0x37, (byte) 0x0B,
+ (byte) 0x12, (byte) 0x45, (byte) 0xEA, (byte) 0x2B, (byte) 0x03, (byte) 0x81,
+ (byte) 0x7C, (byte) 0x5F, (byte) 0x6F, (byte) 0x13, (byte) 0x82, (byte) 0x97,
+ (byte) 0xD0, (byte) 0xDC, (byte) 0x5E, (byte) 0x2F, (byte) 0x08, (byte) 0xDC,
+ (byte) 0x0D, (byte) 0x3A, (byte) 0x6C, (byte) 0xBA, (byte) 0x1D, (byte) 0xEA,
+ (byte) 0x5C, (byte) 0x46, (byte) 0x99, (byte) 0xF7, (byte) 0xDD, (byte) 0xAB,
+ (byte) 0xD4, (byte) 0xDD, (byte) 0xFC, (byte) 0x54, (byte) 0x37, (byte) 0x32,
+ (byte) 0x4B, (byte) 0xA3, (byte) 0xFB, (byte) 0x23, (byte) 0xA1, (byte) 0xC1,
+ (byte) 0x60, (byte) 0xDF, (byte) 0x41, (byte) 0xB0, (byte) 0xD1, (byte) 0xCC,
+ (byte) 0xDF, (byte) 0xAD, (byte) 0xB3, (byte) 0x66, (byte) 0x76, (byte) 0x36,
+ (byte) 0xEC, (byte) 0x6A, (byte) 0x53, (byte) 0xC3, (byte) 0xE2, (byte) 0xB0,
+ (byte) 0x77, (byte) 0xBE, (byte) 0x75, (byte) 0x08, (byte) 0xBA, (byte) 0x17,
+ (byte) 0x14, (byte) 0xFA, (byte) 0x1A, (byte) 0x30, (byte) 0xE7, (byte) 0xB9,
+ (byte) 0xED, (byte) 0xD6, (byte) 0xC1, (byte) 0xA5, (byte) 0x7A, (byte) 0x2B,
+ (byte) 0xA3, (byte) 0xA3, (byte) 0xDD, (byte) 0xDC, (byte) 0x14, (byte) 0xDB,
+ (byte) 0x7F, (byte) 0xF4, (byte) 0xF3, (byte) 0xAF, (byte) 0xCF, (byte) 0x0A,
+ (byte) 0xD3, (byte) 0xAC, (byte) 0x84, (byte) 0x39, (byte) 0x30, (byte) 0xCA,
+ (byte) 0x3C, (byte) 0xD8, (byte) 0xF7, (byte) 0xFA, (byte) 0x29, (byte) 0xDB,
+ (byte) 0x31, (byte) 0xA5, (byte) 0x62, (byte) 0x82, (byte) 0xD2, (byte) 0xB8,
+ (byte) 0x3C, (byte) 0xBC, (byte) 0x8F, (byte) 0xAB, (byte) 0xE4, (byte) 0xE8,
+ (byte) 0xA7, (byte) 0x2C, (byte) 0xEF, (byte) 0xC7, (byte) 0xD5, (byte) 0x12,
+ (byte) 0x16, (byte) 0x04, (byte) 0x6F, (byte) 0xCA, (byte) 0xEA, (byte) 0x31,
+ (byte) 0x9F, (byte) 0x41, (byte) 0xE0, (byte) 0x6F, (byte) 0xE4, (byte) 0x74,
+ (byte) 0x03, (byte) 0x78, (byte) 0xFA, (byte) 0x48, (byte) 0xB4, (byte) 0x6E,
+ (byte) 0xC8, (byte) 0xE7, (byte) 0x40, (byte) 0x8B, (byte) 0x88, (byte) 0x2F,
+ (byte) 0xED, (byte) 0x8E, (byte) 0x68, (byte) 0x96, (byte) 0x2C, (byte) 0xA7,
+ (byte) 0xB6, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x00};
+
+ private static final byte[] DUMMY_CERT =
+ new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x58, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0xC1, (byte) 0xA0, (byte) 0x03, (byte) 0x02,
+ (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xFB,
+ (byte) 0xB0, (byte) 0x4C, (byte) 0x2E, (byte) 0xAB, (byte) 0x10, (byte) 0x9B,
+ (byte) 0x0C, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
+ (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x45,
+ (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+ (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0C, (byte) 0x0A,
+ (byte) 0x53, (byte) 0x6F, (byte) 0x6D, (byte) 0x65, (byte) 0x2D, (byte) 0x53,
+ (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21,
+ (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x0A, (byte) 0x0C, (byte) 0x18, (byte) 0x49, (byte) 0x6E, (byte) 0x74,
+ (byte) 0x65, (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x20,
+ (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74,
+ (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20,
+ (byte) 0x4C, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x1E, (byte) 0x17,
+ (byte) 0x0D, (byte) 0x31, (byte) 0x34, (byte) 0x30, (byte) 0x34, (byte) 0x32,
+ (byte) 0x33, (byte) 0x32, (byte) 0x30, (byte) 0x35, (byte) 0x30, (byte) 0x34,
+ (byte) 0x30, (byte) 0x5A, (byte) 0x17, (byte) 0x0D, (byte) 0x31, (byte) 0x37,
+ (byte) 0x30, (byte) 0x34, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x30,
+ (byte) 0x35, (byte) 0x30, (byte) 0x34, (byte) 0x30, (byte) 0x5A, (byte) 0x30,
+ (byte) 0x45, (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02,
+ (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0C,
+ (byte) 0x0A, (byte) 0x53, (byte) 0x6F, (byte) 0x6D, (byte) 0x65, (byte) 0x2D,
+ (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31,
+ (byte) 0x21, (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0A, (byte) 0x0C, (byte) 0x18, (byte) 0x49, (byte) 0x6E,
+ (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74,
+ (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69,
+ (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79,
+ (byte) 0x20, (byte) 0x4C, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x81,
+ (byte) 0x9F, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81,
+ (byte) 0x8D, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02,
+ (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xD8, (byte) 0x2B, (byte) 0xC8,
+ (byte) 0xA6, (byte) 0x32, (byte) 0xE4, (byte) 0x62, (byte) 0xFF, (byte) 0x4D,
+ (byte) 0xF3, (byte) 0xD0, (byte) 0xAD, (byte) 0x59, (byte) 0x8B, (byte) 0x45,
+ (byte) 0xA7, (byte) 0xBD, (byte) 0xF1, (byte) 0x47, (byte) 0xBF, (byte) 0x09,
+ (byte) 0x58, (byte) 0x7B, (byte) 0x22, (byte) 0xBD, (byte) 0x35, (byte) 0xAE,
+ (byte) 0x97, (byte) 0x25, (byte) 0x86, (byte) 0x94, (byte) 0xA0, (byte) 0x80,
+ (byte) 0xC0, (byte) 0xB4, (byte) 0x1F, (byte) 0x76, (byte) 0x91, (byte) 0x67,
+ (byte) 0x46, (byte) 0x31, (byte) 0xD0, (byte) 0x10, (byte) 0x84, (byte) 0xB7,
+ (byte) 0x22, (byte) 0x1E, (byte) 0x70, (byte) 0x23, (byte) 0x91, (byte) 0x72,
+ (byte) 0xC8, (byte) 0xE9, (byte) 0x6D, (byte) 0x79, (byte) 0x3A, (byte) 0x85,
+ (byte) 0x77, (byte) 0x80, (byte) 0x0F, (byte) 0xC4, (byte) 0x95, (byte) 0x16,
+ (byte) 0x75, (byte) 0xC5, (byte) 0x4A, (byte) 0x71, (byte) 0x4C, (byte) 0xC8,
+ (byte) 0x63, (byte) 0x3F, (byte) 0xA3, (byte) 0xF2, (byte) 0x63, (byte) 0x9C,
+ (byte) 0x2A, (byte) 0x4F, (byte) 0x9A, (byte) 0xFA, (byte) 0xCB, (byte) 0xC1,
+ (byte) 0x71, (byte) 0x6E, (byte) 0x28, (byte) 0x85, (byte) 0x28, (byte) 0xA0,
+ (byte) 0x27, (byte) 0x1E, (byte) 0x65, (byte) 0x1C, (byte) 0xAE, (byte) 0x07,
+ (byte) 0xD5, (byte) 0x5B, (byte) 0x6F, (byte) 0x2D, (byte) 0x43, (byte) 0xED,
+ (byte) 0x2B, (byte) 0x90, (byte) 0xB1, (byte) 0x8C, (byte) 0xAF, (byte) 0x24,
+ (byte) 0x6D, (byte) 0xAE, (byte) 0xE9, (byte) 0x17, (byte) 0x3A, (byte) 0x05,
+ (byte) 0xC1, (byte) 0xBF, (byte) 0xB8, (byte) 0x1C, (byte) 0xAE, (byte) 0x65,
+ (byte) 0x3B, (byte) 0x1B, (byte) 0x58, (byte) 0xC2, (byte) 0xD9, (byte) 0xAE,
+ (byte) 0xD6, (byte) 0xAA, (byte) 0x67, (byte) 0x88, (byte) 0xF1, (byte) 0x02,
+ (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xA3, (byte) 0x50,
+ (byte) 0x30, (byte) 0x4E, (byte) 0x30, (byte) 0x1D, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x1D, (byte) 0x0E, (byte) 0x04, (byte) 0x16, (byte) 0x04,
+ (byte) 0x14, (byte) 0x8B, (byte) 0x75, (byte) 0xD5, (byte) 0xAC, (byte) 0xCB,
+ (byte) 0x08, (byte) 0xBE, (byte) 0x0E, (byte) 0x1F, (byte) 0x65, (byte) 0xB7,
+ (byte) 0xFA, (byte) 0x56, (byte) 0xBE, (byte) 0x6C, (byte) 0xA7, (byte) 0x75,
+ (byte) 0xDA, (byte) 0x85, (byte) 0xAF, (byte) 0x30, (byte) 0x1F, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x23, (byte) 0x04, (byte) 0x18,
+ (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x8B, (byte) 0x75,
+ (byte) 0xD5, (byte) 0xAC, (byte) 0xCB, (byte) 0x08, (byte) 0xBE, (byte) 0x0E,
+ (byte) 0x1F, (byte) 0x65, (byte) 0xB7, (byte) 0xFA, (byte) 0x56, (byte) 0xBE,
+ (byte) 0x6C, (byte) 0xA7, (byte) 0x75, (byte) 0xDA, (byte) 0x85, (byte) 0xAF,
+ (byte) 0x30, (byte) 0x0C, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D,
+ (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01,
+ (byte) 0x01, (byte) 0xFF, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09,
+ (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+ (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x3B, (byte) 0xE8, (byte) 0x78,
+ (byte) 0x6D, (byte) 0x95, (byte) 0xD6, (byte) 0x3D, (byte) 0x6A, (byte) 0xF7,
+ (byte) 0x13, (byte) 0x19, (byte) 0x2C, (byte) 0x1B, (byte) 0xC2, (byte) 0x88,
+ (byte) 0xAE, (byte) 0x22, (byte) 0xAB, (byte) 0xF4, (byte) 0x8D, (byte) 0x32,
+ (byte) 0xF5, (byte) 0x7C, (byte) 0x71, (byte) 0x67, (byte) 0xCF, (byte) 0x2D,
+ (byte) 0xD1, (byte) 0x1C, (byte) 0xC2, (byte) 0xC3, (byte) 0x87, (byte) 0xE2,
+ (byte) 0xE9, (byte) 0xBE, (byte) 0x89, (byte) 0x5C, (byte) 0xE4, (byte) 0x34,
+ (byte) 0xAB, (byte) 0x48, (byte) 0x91, (byte) 0xC2, (byte) 0x3F, (byte) 0x95,
+ (byte) 0xAE, (byte) 0x2B, (byte) 0x47, (byte) 0x9E, (byte) 0x25, (byte) 0x78,
+ (byte) 0x6B, (byte) 0x4F, (byte) 0x9A, (byte) 0x10, (byte) 0xA4, (byte) 0x72,
+ (byte) 0xFD, (byte) 0xCF, (byte) 0xF7, (byte) 0x02, (byte) 0x0C, (byte) 0xB0,
+ (byte) 0x0A, (byte) 0x08, (byte) 0xA4, (byte) 0x5A, (byte) 0xE2, (byte) 0xE5,
+ (byte) 0x74, (byte) 0x7E, (byte) 0x11, (byte) 0x1D, (byte) 0x39, (byte) 0x60,
+ (byte) 0x6A, (byte) 0xC9, (byte) 0x1F, (byte) 0x69, (byte) 0xF3, (byte) 0x2E,
+ (byte) 0x63, (byte) 0x26, (byte) 0xDC, (byte) 0x9E, (byte) 0xEF, (byte) 0x6B,
+ (byte) 0x7A, (byte) 0x0A, (byte) 0xE1, (byte) 0x54, (byte) 0x57, (byte) 0x98,
+ (byte) 0xAA, (byte) 0x72, (byte) 0x91, (byte) 0x78, (byte) 0x04, (byte) 0x7E,
+ (byte) 0x1F, (byte) 0x8F, (byte) 0x65, (byte) 0x4D, (byte) 0x1F, (byte) 0x0B,
+ (byte) 0x12, (byte) 0xAC, (byte) 0x9C, (byte) 0x24, (byte) 0x0F, (byte) 0x84,
+ (byte) 0x14, (byte) 0x1A, (byte) 0x55, (byte) 0x2D, (byte) 0x1F, (byte) 0xBB,
+ (byte) 0xF0, (byte) 0x9D, (byte) 0x09, (byte) 0xB2, (byte) 0x08, (byte) 0x5C,
+ (byte) 0x59, (byte) 0x32, (byte) 0x65, (byte) 0x80, (byte) 0x26};
+
+ private static final byte[] DUMMY_OCSP_DATA = new byte[1];
+
+ private static final byte[] DUMMY_TLS_SCT_DATA = new byte[1];
+
+ @After
+ public void tearDown() throws Exception {
+ assertEquals(0, NativeCrypto.ERR_peek_last_error());
+ }
+
+ private static TestSessionBuilder getType1() {
+ return new TestSessionBuilder()
+ .setType(0x01)
+ .setSessionData(kOpenSSLSession)
+ .addCertificate(DUMMY_CERT);
+ }
+
+ private static TestSessionBuilder getType2() {
+ return new TestSessionBuilder()
+ .setType(0x02)
+ .setSessionData(kOpenSSLSession)
+ .addCertificate(DUMMY_CERT)
+ .addOcspData(DUMMY_OCSP_DATA);
+ }
+
+ private static TestSessionBuilder getType3() {
+ return new TestSessionBuilder()
+ .setType(0x03)
+ .setSessionData(kOpenSSLSession)
+ .addCertificate(DUMMY_CERT)
+ .addOcspData(DUMMY_OCSP_DATA)
+ .setTlsSctData(DUMMY_TLS_SCT_DATA);
+ }
+
+ @Test
+ public void toSession_EmptyArray_Invalid_Failure() throws Exception {
+ assertInvalidSession(new byte[0]);
+ }
+
+ @Test
+ public void toSession_Type1_Valid_Success() throws Exception {
+ assertValidSession(getType1().build());
+ }
+
+ @Test
+ public void toSession_Type2_Valid_Success() throws Exception {
+ assertValidSession(getType2().build());
+ }
+
+ @Test
+ public void toSession_Type3_Valid_Success() throws Exception {
+ assertValidSession(getType3().build());
+ }
+
+ private void assertTruncatedSessionFails(byte[] validSession) {
+ for (int i = 0; i < validSession.length - 1; i++) {
+ byte[] truncatedSession = new byte[i];
+ System.arraycopy(validSession, 0, truncatedSession, 0, i);
+ assertNull("Truncating to " + i + " bytes of " + validSession.length
+ + " should not succeed",
+ SslSessionWrapper.newInstance(null, truncatedSession, "www.google.com", 443));
+ }
+ }
+
+ @Test
+ public void toSession_Type3_Truncated_Failure() throws Exception {
+ assertTruncatedSessionFails(getType3().build());
+ }
+
+ private static void assertValidSession(byte[] data) {
+ assertNotNull(SslSessionWrapper.newInstance(null, data, "www.google.com", 443));
+ }
+
+ private static void assertInvalidSession(byte[] data) {
+ assertNull(SslSessionWrapper.newInstance(null, data, "www.google.com", 443));
+ }
+
+ @Test
+ public void toSession_UnknownType_Failure() throws Exception {
+ assertInvalidSession(getType3().setType((byte) 0xEE).build());
+ }
+
+ @Test
+ public void toSession_CertificatesCountTooLarge_Failure() throws Exception {
+ assertInvalidSession(getType3().setCertificatesLength(16834).build());
+ }
+
+ @Test
+ public void toSession_CertificatesCountNegative_Failure() throws Exception {
+ assertInvalidSession(getType3().setCertificatesLength(-1).build());
+ }
+
+ @Test
+ public void toSession_CertificateSizeNegative_Failure() throws Exception {
+ assertInvalidSession(getType3().setCertificateLength(0, -1).build());
+ }
+
+ @Test
+ public void toSession_CertificateSizeTooLarge_Failure() throws Exception {
+ assertInvalidSession(getType3().setCertificateLength(0, 16834).build());
+ }
+
+ @Test
+ public void toSession_SessionDataSizeTooLarge_Failure() throws Exception {
+ assertInvalidSession(getType3().setSessionDataLength(16834).build());
+ }
+
+ @Test
+ public void toSession_SessionDataSizeNegative_Failure() throws Exception {
+ assertInvalidSession(getType3().setSessionDataLength(-1).build());
+ }
+
+ @Test
+ public void toSession_OcspDatasNumberTooMany_Failure() throws Exception {
+ assertInvalidSession(getType3().setOcspDatasLength(32791).build());
+ }
+
+ @Test
+ public void toSession_OcspDatasNumberNegative_Failure() throws Exception {
+ assertInvalidSession(getType3().setOcspDatasLength(-1).build());
+ }
+
+ @Test
+ public void toSession_OcspDataSizeNegative_Failure() throws Exception {
+ assertInvalidSession(getType3().setOcspDataLength(0, -1).build());
+ }
+
+ @Test
+ public void toSession_OcspDataSizeTooLarge_Failure() throws Exception {
+ assertInvalidSession(getType3().setOcspDataLength(0, 92948).build());
+ }
+
+ @Test
+ public void toSession_TlsSctDataSizeNegative_Failure() throws Exception {
+ assertInvalidSession(getType3().setTlsSctDataLength(-1).build());
+ }
+
+ @Test
+ public void toSession_TlsSctDataSizeTooLarge_Failure() throws Exception {
+ assertInvalidSession(getType3().setTlsSctDataLength(931148).build());
+ }
+
+ @Test
+ public void toSession_Type2OcspDataEmpty_Success() throws Exception {
+ assertValidSession(getType1().setType(0x02).setOcspDataEmpty().build());
+ }
+
+ @Test
+ public void toSession_Type3TlsSctDataEmpty_Success() throws Exception {
+ assertValidSession(getType2().setType(0x03).setTlsSctDataEmpty().build());
+ }
+
+ @Test
+ public void toSession_Type3OcspAndTlsSctDataEmpty_Success() throws Exception {
+ assertValidSession(
+ getType1().setType(0x03).setOcspDataEmpty().setTlsSctDataEmpty().build());
+ }
+
+ private static void assertTrailingDataFails(byte[] validSession) {
+ byte[] invalidSession = new byte[validSession.length + 1];
+ System.arraycopy(validSession, 0, invalidSession, 0, validSession.length);
+ assertInvalidSession(invalidSession);
+ }
+
+ @Test
+ public void toSession_Type1TrailingData_Failure() throws Exception {
+ assertTrailingDataFails(getType1().build());
+ }
+
+ @Test
+ public void toSession_Type2TrailingData_Failure() throws Exception {
+ assertTrailingDataFails(getType2().build());
+ }
+
+ @Test
+ public void toSession_Type3TrailingData_Failure() throws Exception {
+ assertTrailingDataFails(getType3().build());
+ }
+
+ @Test
+ public void test_reserializableFromByteArray_roundTrip_type1() throws Exception {
+ // Converting OPEN_SSL (type 1) -> OPEN_SSL_WITH_TLS_SCT (type 3) adds
+ // eight zero-bytes:
+ // 1.) 4 bytes for int32 value 0 == countOcspResponses
+ // 2.) 4 bytes for int32 value 0 == tlsSctDataLength
+ // since OPEN_SSL (type 1) cannot contain OSCP or TLS SCT data.
+ check_reserializableFromByteArray_roundTrip(getType1().build(), new byte[8]);
+ }
+
+ @Test
+ public void test_reserializableFromByteArray_roundTrip_type2() throws Exception {
+ // Converting OPEN_SSL_WITH_OCSP (type 2) -> OPEN_SSL_WITH_TLS_SCT (type 3) adds
+ // four zero-bytes for int32 value 0 == tlsSctDataLength
+ // since OPEN_SSL_WITH_OCSP (type 2) cannot contain TLS SCT data.
+ check_reserializableFromByteArray_roundTrip(getType2().build(), new byte[4]);
+ }
+
+ @Test
+ public void test_reserializableFromByteArray_roundTrip_type3() throws Exception {
+ check_reserializableFromByteArray_roundTrip(getType3().build(), new byte[0]);
+ }
+
+ private static void check_reserializableFromByteArray_roundTrip(
+ byte[] data, byte[] expectedTrailingBytesAfterReserialization) throws Exception {
+ SslSessionWrapper session =
+ SslSessionWrapper.newInstance(null, data, "www.example.com", 12345);
+ byte[] sessionBytes = session.toBytes();
+
+ SslSessionWrapper session2 =
+ SslSessionWrapper.newInstance(null, sessionBytes, "www.example.com", 12345);
+ byte[] sessionBytes2 = session2.toBytes();
+
+ assertSSLSessionEquals(session, session2);
+ assertByteArrayEquals(sessionBytes, sessionBytes2);
+
+ assertEquals("www.example.com", session.getPeerHost());
+ assertEquals(12345, session.getPeerPort());
+ assertTrue(sessionBytes.length >= data.length);
+
+ byte[] expectedReserializedData = concat(data, expectedTrailingBytesAfterReserialization);
+ // AbstractSessionContext.toBytes() always writes type 3 == OPEN_SSL_WITH_TLS_SCT
+ expectedReserializedData[3] = 3;
+ assertByteArrayEquals(expectedReserializedData, sessionBytes);
+ }
+
+ private static byte[] concat(byte[] a, byte[] b) {
+ byte[] result = new byte[a.length + b.length];
+ System.arraycopy(a, 0, result, 0, a.length);
+ System.arraycopy(b, 0, result, a.length, b.length);
+ return result;
+ }
+
+ private static void assertSSLSessionEquals(SslSessionWrapper a, SslSessionWrapper b)
+ throws Exception {
+ assertEquals(a.getCipherSuite(), b.getCipherSuite());
+ assertByteArrayEquals(a.getId(), b.getId());
+ assertEquals(a.getPeerHost(), b.getPeerHost());
+ assertEquals(a.getPeerPort(), b.getPeerPort());
+ assertEquals(a.getProtocol(), b.getProtocol());
+ }
+
+ private static void assertByteArrayEquals(byte[] expected, byte[] actual) {
+ // If running on OpenJDK 8+, could use java.util.Base64 for better failure messages:
+ // assertEquals(Base64.encode(expected), Base64.encode(actual));
+ assertTrue("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+}
diff --git a/platform/build.gradle b/platform/build.gradle
index 78c8f1b..242012e 100644
--- a/platform/build.gradle
+++ b/platform/build.gradle
@@ -92,7 +92,12 @@
compile project(path: ':conscrypt-openjdk', configuration: 'platform')
publicApiDocs project(':conscrypt-api-doclet')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
- exclude group: 'com.android.support', module: 'support-annotations'
+ exclude module: 'support-annotations'
+ exclude module: 'support-v4'
+ exclude module: 'support-v13'
+ exclude module: 'recyclerview-v7'
+ exclude module: 'appcompat-v7'
+ exclude module: 'design'
})
testCompile project(':conscrypt-testing'),
libraries.junit
diff --git a/platform/proguard-rules.pro b/platform/proguard-rules.pro
index c3bdd2c..3bc75b2 100644
--- a/platform/proguard-rules.pro
+++ b/platform/proguard-rules.pro
@@ -20,5 +20,7 @@
-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/InternalUtil.java b/platform/src/main/java/org/conscrypt/InternalUtil.java
index 40937c8..dbc6137 100644
--- a/platform/src/main/java/org/conscrypt/InternalUtil.java
+++ b/platform/src/main/java/org/conscrypt/InternalUtil.java
@@ -30,7 +30,7 @@
@Internal
public final class InternalUtil {
public static PublicKey logKeyToPublicKey(byte[] logKey) throws NoSuchAlgorithmException {
- return new OpenSSLKey(NativeCrypto.d2i_PUBKEY(logKey)).getPublicKey();
+ return new OpenSSLKey(NativeCrypto.EVP_parse_public_key(logKey)).getPublicKey();
}
public static PublicKey readPublicKeyPem(InputStream pem) throws InvalidKeyException, NoSuchAlgorithmException {
diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java
index 1d2942e..d53e69a 100644
--- a/platform/src/main/java/org/conscrypt/Platform.java
+++ b/platform/src/main/java/org/conscrypt/Platform.java
@@ -55,9 +55,7 @@
import sun.security.x509.AlgorithmId;
final class Platform {
- private static class NoPreloadHolder {
- public static final Platform MAPPER = new Platform();
- }
+ private static class NoPreloadHolder { public static final Platform MAPPER = new Platform(); }
/**
* Runs all the setup for the platform that only needs to run once.
@@ -69,21 +67,19 @@
/**
* Just a placeholder to make sure the class is initialized.
*/
- private void ping() {
- }
+ private void ping() {}
- private Platform() {
- }
+ private Platform() {}
static FileDescriptor getFileDescriptor(Socket s) {
return s.getFileDescriptor$();
}
- static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
+ static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
try {
Field f_impl = Socket.class.getDeclaredField("impl");
f_impl.setAccessible(true);
- Object socketImpl = f_impl.get(openSSLSocketImpl);
+ Object socketImpl = f_impl.get(socket);
Field f_fd = SocketImpl.class.getDeclaredField("fd");
f_fd.setAccessible(true);
return (FileDescriptor) f_fd.get(socketImpl);
@@ -109,8 +105,8 @@
}
}
- static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
- OpenSSLSocketImpl socket) {
+ static void setSSLParameters(
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
List<SNIServerName> serverNames = params.getServerNames();
@@ -124,25 +120,25 @@
}
}
- static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
- OpenSSLSocketImpl socket) {
+ static void getSSLParameters(
+ SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
- params.setServerNames(Collections.<SNIServerName> singletonList(
+ params.setServerNames(Collections.<SNIServerName>singletonList(
new SNIHostName(socket.getHostname())));
}
}
static void setSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
List<SNIServerName> serverNames = params.getServerNames();
if (serverNames != null) {
for (SNIServerName serverName : serverNames) {
if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
- engine.setSniHostname(((SNIHostName) serverName).getAsciiName());
+ engine.setHostname(((SNIHostName) serverName).getAsciiName());
break;
}
}
@@ -150,12 +146,12 @@
}
static void getSSLParameters(
- SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
+ SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
- if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) {
+ if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
params.setServerNames(Collections.<SNIServerName>singletonList(
- new SNIHostName(engine.getSniHostname())));
+ new SNIHostName(engine.getHostname())));
}
}
@@ -168,66 +164,64 @@
Object argumentInstance) throws CertificateException {
// Use duck-typing to try and call the hostname-aware method if available.
try {
- Method method = tm.getClass().getMethod(methodName,
- X509Certificate[].class,
- String.class,
- argumentClass);
+ Method method = tm.getClass().getMethod(
+ methodName, X509Certificate[].class, String.class, argumentClass);
method.invoke(tm, chain, authType, argumentInstance);
return true;
} catch (NoSuchMethodException | IllegalAccessException ignored) {
} catch (InvocationTargetException e) {
if (e.getCause() instanceof CertificateException) {
- throw (CertificateException) e.getCause();
+ throw(CertificateException) e.getCause();
}
throw new RuntimeException(e.getCause());
}
return false;
}
- static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLSocketImpl socket) throws CertificateException {
+ static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+ AbstractConscryptSocket socket) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkClientTrusted(chain, authType, socket);
} else if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
- socket.getHandshakeSession().getPeerHost())) {
+ socket.getHandshakeSession().getPeerHost())) {
tm.checkClientTrusted(chain, authType);
}
}
- static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLSocketImpl socket) throws CertificateException {
+ static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+ AbstractConscryptSocket socket) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, socket);
} else if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
- socket.getHandshakeSession().getPeerHost())) {
+ socket.getHandshakeSession().getPeerHost())) {
tm.checkServerTrusted(chain, authType);
}
}
- static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLEngineImpl engine) throws CertificateException {
+ static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+ ConscryptEngine engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkClientTrusted(chain, authType, engine);
} else if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
- engine.getHandshakeSession().getPeerHost())) {
+ engine.getHandshakeSession().getPeerHost())) {
tm.checkClientTrusted(chain, authType);
}
}
- static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
- String authType, OpenSSLEngineImpl engine) throws CertificateException {
+ static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+ ConscryptEngine engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, engine);
} else if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
- engine.getHandshakeSession().getPeerHost())) {
+ engine.getHandshakeSession().getPeerHost())) {
tm.checkServerTrusted(chain, authType);
}
}
@@ -252,10 +246,10 @@
Class eventLogClass = Class.forName("android.util.EventLog");
Object eventLogInstance = eventLogClass.newInstance();
- Method writeEventMethod = eventLogClass.getMethod("writeEvent",
- new Class[] { Integer.TYPE, Object[].class });
+ Method writeEventMethod = eventLogClass.getMethod(
+ "writeEvent", new Class[] {Integer.TYPE, Object[].class});
writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
- new Object[] { "conscrypt", uid, message });
+ new Object[] {"conscrypt", uid, message});
} catch (Exception e) {
// Do not log and fail silently
}
@@ -340,14 +334,15 @@
* Pre-Java 8 backward compatibility.
*/
- static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
- return new OpenSSLExtendedSessionImpl(sslSession);
+ static SSLSession wrapSSLSession(ActiveSession sslSession) {
+ return new DelegatingExtendedSSLSession(sslSession);
}
static SSLSession unwrapSSLSession(SSLSession sslSession) {
- if (sslSession instanceof OpenSSLExtendedSessionImpl) {
- return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
+ if (sslSession instanceof DelegatingExtendedSSLSession) {
+ return ((DelegatingExtendedSSLSession) sslSession).getDelegate();
}
+
return sslSession;
}
@@ -360,7 +355,7 @@
}
static boolean isCTVerificationRequired(String hostname) {
- return NetworkSecurityPolicy.getInstance()
- .isCertificateTransparencyVerificationRequired(hostname);
+ return NetworkSecurityPolicy.getInstance().isCertificateTransparencyVerificationRequired(
+ hostname);
}
}
diff --git a/platform/src/main/java/org/conscrypt/TrustManagerImpl.java b/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
index 010c75a..95f7f48 100644
--- a/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
+++ b/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
@@ -420,8 +420,8 @@
private byte[] getOcspDataFromSession(SSLSession session) {
List<byte[]> ocspResponses = null;
- if (session instanceof AbstractOpenSSLSession) {
- AbstractOpenSSLSession opensslSession = (AbstractOpenSSLSession) session;
+ if (session instanceof ActiveSession) {
+ ActiveSession opensslSession = (ActiveSession) session;
ocspResponses = opensslSession.getStatusResponses();
} else {
Method m_getResponses;
@@ -447,14 +447,14 @@
}
private byte[] getTlsSctDataFromSession(SSLSession session) {
- if (session instanceof AbstractOpenSSLSession) {
- AbstractOpenSSLSession opensslSession = (AbstractOpenSSLSession) session;
- return opensslSession.getTlsSctData();
+ if (session instanceof ActiveSession) {
+ ActiveSession opensslSession = (ActiveSession) session;
+ return opensslSession.getPeerSignedCertificateTimestamp();
}
byte[] data = null;
try {
- Method m_getTlsSctData = session.getClass().getDeclaredMethod("getTlsSctData");
+ Method m_getTlsSctData = session.getClass().getDeclaredMethod("getPeerSignedCertificateTimestamp");
m_getTlsSctData.setAccessible(true);
Object rawData = m_getTlsSctData.invoke(session);
if (rawData instanceof byte[]) {
@@ -789,7 +789,7 @@
/**
- * Comparator for sorting {@link TrustAnchor}s using a {@link CertificateComparator}.
+ * Comparator for sorting {@link TrustAnchor}s using a {@link CertificatePriorityComparator}.
*/
private static class TrustAnchorComparator implements Comparator<TrustAnchor> {
private static final CertificatePriorityComparator CERT_COMPARATOR =
diff --git a/settings.gradle b/settings.gradle
index f2cadcb..69f320a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,11 +3,12 @@
include ":conscrypt-android-platform"
include ":conscrypt-android-stub"
include ":conscrypt-api-doclet"
+include ":conscrypt-benchmark-base"
include ":conscrypt-benchmark-graphs"
+include ":conscrypt-benchmark-jmh"
include ":conscrypt-constants"
include ":conscrypt-libcore-stub"
include ":conscrypt-openjdk"
-include ":conscrypt-openjdk-benchmarks"
include ":conscrypt-openjdk-integ-tests"
include ":conscrypt-openjdk-uber"
include ":conscrypt-testing"
@@ -16,11 +17,12 @@
project(':conscrypt-android-platform').projectDir = "$rootDir/platform" as File
project(':conscrypt-android-stub').projectDir = "$rootDir/android-stub" as File
project(':conscrypt-api-doclet').projectDir = "$rootDir/api-doclet" as File
+project(':conscrypt-benchmark-base').projectDir = "$rootDir/benchmark-base" as File
project(':conscrypt-benchmark-graphs').projectDir = "$rootDir/benchmark-graphs" as File
+project(':conscrypt-benchmark-jmh').projectDir = "$rootDir/benchmark-jmh" as File
project(':conscrypt-constants').projectDir = "$rootDir/constants" as File
project(':conscrypt-libcore-stub').projectDir = "$rootDir/libcore-stub" as File
project(':conscrypt-openjdk').projectDir = "$rootDir/openjdk" as File
-project(':conscrypt-openjdk-benchmarks').projectDir = "$rootDir/openjdk-benchmarks" as File
project(':conscrypt-openjdk-integ-tests').projectDir = "$rootDir/openjdk-integ-tests" as File
project(':conscrypt-openjdk-uber').projectDir = "$rootDir/openjdk-uber" as File
project(':conscrypt-testing').projectDir = "$rootDir/testing" as File
diff --git a/testing/build.gradle b/testing/build.gradle
index a4fd599..12d08be 100644
--- a/testing/build.gradle
+++ b/testing/build.gradle
@@ -13,6 +13,8 @@
compileOnly project(':conscrypt-constants')
compile project(':conscrypt-libcore-stub'),
+ libraries.bouncycastle_apis,
+ libraries.bouncycastle_provider,
libraries.netty_handler
}
diff --git a/testing/src/main/java/libcore/java/security/TestKeyStore.java b/testing/src/main/java/libcore/java/security/TestKeyStore.java
index d6f4ad8..060a023 100644
--- a/testing/src/main/java/libcore/java/security/TestKeyStore.java
+++ b/testing/src/main/java/libcore/java/security/TestKeyStore.java
@@ -159,6 +159,7 @@
private static final byte[] LOCAL_HOST_ADDRESS = {127, 0, 0, 1};
private static final String LOCAL_HOST_NAME = "localhost";
+ private static final String LOCAL_HOST_NAME_IPV6 = "ip6-localhost";
public final KeyStore keyStore;
public final char[] storePassword;
@@ -376,7 +377,6 @@
private BigInteger certificateSerialNumber = null;
public Builder() {
- subject = localhost();
}
/**
@@ -448,6 +448,11 @@
return this;
}
+ public Builder addSubjectAltNameDnsName(String dnsName) {
+ return addSubjectAltName(
+ new GeneralName(GeneralName.dNSName, dnsName));
+ }
+
public Builder addSubjectAltNameIpAddress(byte[] ipAddress) {
return addSubjectAltName(
new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress)));
@@ -554,6 +559,13 @@
caCertChain = (X509Certificate[]) signer.getCertificateChain();
}
+ // Default to localhost if nothing was specified.
+ if (subject == null) {
+ subject = localhost();
+ addSubjectAltNameDnsName(LOCAL_HOST_NAME);
+ addSubjectAltNameDnsName(LOCAL_HOST_NAME_IPV6);
+ }
+
final PrivateKey privateKey;
final PublicKey publicKey;
X509Certificate x509c;
@@ -709,9 +721,9 @@
x509cg.addExtension(
Extension.extendedKeyUsage, critical, new ExtendedKeyUsage(keyPurposeId));
}
- for (GeneralName subjectAltName : subjectAltNames) {
+ if (!subjectAltNames.isEmpty()) {
x509cg.addExtension(Extension.subjectAlternativeName, false,
- new GeneralNames(subjectAltName).getEncoded());
+ new GeneralNames(subjectAltNames.toArray(new GeneralName[0])).getEncoded());
}
if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) {
x509cg.addExtension(Extension.nameConstraints, true,
diff --git a/testing/src/main/java/libcore/javax/net/ssl/TestSSLEnginePair.java b/testing/src/main/java/libcore/javax/net/ssl/TestSSLEnginePair.java
index 1146c69..69bef89 100644
--- a/testing/src/main/java/libcore/javax/net/ssl/TestSSLEnginePair.java
+++ b/testing/src/main/java/libcore/javax/net/ssl/TestSSLEnginePair.java
@@ -35,6 +35,7 @@
public final TestSSLContext c;
public final SSLEngine server;
public final SSLEngine client;
+
private TestSSLEnginePair(TestSSLContext c,
SSLEngine server,
SSLEngine client) {
@@ -42,23 +43,29 @@
this.server = server;
this.client = client;
}
+
public static TestSSLEnginePair create() throws IOException {
return create(null);
}
+
public static TestSSLEnginePair create(Hooks hooks) throws IOException {
return create(TestSSLContext.create(), hooks);
}
+
public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks) throws IOException {
return create(c, hooks, null);
}
+
public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks, boolean[] finished)
throws IOException {
SSLEngine[] engines = connect(c, hooks, finished);
return new TestSSLEnginePair(c, engines[0], engines[1]);
}
+
public static SSLEngine[] connect(TestSSLContext c, Hooks hooks) throws IOException {
return connect(c, hooks, null);
}
+
/**
* Create a new connected server/client engine pair within a
* existing SSLContext. Optionally specify clientCipherSuites to
@@ -72,15 +79,20 @@
if (hooks == null) {
hooks = new Hooks();
}
+
// FINISHED state should be returned only once.
boolean[] clientFinished = new boolean[1];
boolean[] serverFinished = new boolean[1];
+
SSLSession session = c.clientContext.createSSLEngine().getSession();
+
int packetBufferSize = session.getPacketBufferSize();
ByteBuffer clientToServer = ByteBuffer.allocate(packetBufferSize);
ByteBuffer serverToClient = ByteBuffer.allocate(packetBufferSize);
+
int applicationBufferSize = session.getApplicationBufferSize();
ByteBuffer scratch = ByteBuffer.allocate(applicationBufferSize);
+
SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port);
SSLEngine server = c.serverContext.createSSLEngine();
client.setUseClientMode(true);
@@ -88,12 +100,14 @@
hooks.beforeBeginHandshake(client, server);
client.beginHandshake();
server.beginHandshake();
+
while (true) {
boolean clientDone = client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
boolean serverDone = server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
if (clientDone && serverDone) {
break;
}
+
boolean progress = false;
if (!clientDone) {
progress = handshakeCompleted(client,
@@ -113,6 +127,7 @@
break;
}
}
+
if (finished != null) {
assertEquals(2, finished.length);
finished[0] = clientFinished[0];
@@ -120,13 +135,16 @@
}
return new SSLEngine[] { server, client };
}
+
public static class Hooks {
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {}
}
+
@Override
public void close() throws SSLException {
close(new SSLEngine[] { client, server });
}
+
public static void close(SSLEngine[] engines) {
try {
for (SSLEngine engine : engines) {
@@ -139,6 +157,7 @@
throw new RuntimeException(e);
}
}
+
private static boolean handshakeCompleted(SSLEngine engine,
ByteBuffer output,
ByteBuffer input,
@@ -147,8 +166,10 @@
try {
// make the other side's output into our input
input.flip();
+
HandshakeStatus status = engine.getHandshakeStatus();
switch (status) {
+
case NEED_TASK: {
boolean progress = false;
while (true) {
@@ -160,6 +181,7 @@
progress = true;
}
}
+
case NEED_UNWRAP: {
// avoid underflow
if (input.remaining() == 0) {
@@ -174,6 +196,7 @@
assertFinishedOnce(finished, unwrapResult);
return true;
}
+
case NEED_WRAP: {
// avoid possible overflow
if (output.remaining() != output.capacity()) {
@@ -191,6 +214,7 @@
assertFinishedOnce(finished, wrapResult);
return true;
}
+
case NOT_HANDSHAKING:
// should have been checked by caller before calling
case FINISHED:
@@ -204,6 +228,7 @@
input.compact();
}
}
+
private static void assertFinishedOnce(boolean[] finishedOut, SSLEngineResult result) {
if (result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
assertFalse("should only return FINISHED once", finishedOut[0]);
diff --git a/testing/src/main/java/libcore/javax/net/ssl/TestSSLSessions.java b/testing/src/main/java/libcore/javax/net/ssl/TestSSLSessions.java
index 34596bc..8f45a2e 100644
--- a/testing/src/main/java/libcore/javax/net/ssl/TestSSLSessions.java
+++ b/testing/src/main/java/libcore/javax/net/ssl/TestSSLSessions.java
@@ -27,19 +27,23 @@
* An invalid session that is not connected
*/
public final SSLSession invalid;
+
/**
* The server side of a connected session
*/
public final SSLSession server;
+
/**
* The client side of a connected session
*/
public final SSLSession client;
+
/**
* The associated SSLSocketTest.Helper that is the source of
* the client and server SSLSessions.
*/
public final TestSSLSocketPair s;
+
private TestSSLSessions(SSLSession invalid,
SSLSession server,
SSLSession client,
@@ -49,15 +53,17 @@
this.client = client;
this.s = s;
}
+
public void close() {
s.close();
}
- public static final TestSSLSessions create() {
+
+ public static TestSSLSessions create() {
try {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
SSLSession invalid = ssl.getSession();
- TestSSLSocketPair s = TestSSLSocketPair.create();
+ TestSSLSocketPair s = TestSSLSocketPair.create().connect();
return new TestSSLSessions(invalid, s.server.getSession(), s.client.getSession(), s);
} catch (Exception e) {
throw new RuntimeException(e);
diff --git a/testing/src/main/java/libcore/javax/net/ssl/TestSSLSocketPair.java b/testing/src/main/java/libcore/javax/net/ssl/TestSSLSocketPair.java
index 66e7ac7..c6d59fe 100644
--- a/testing/src/main/java/libcore/javax/net/ssl/TestSSLSocketPair.java
+++ b/testing/src/main/java/libcore/javax/net/ssl/TestSSLSocketPair.java
@@ -15,6 +15,7 @@
*/
package libcore.javax.net.ssl;
+import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -31,9 +32,7 @@
public final TestSSLContext c;
public final SSLSocket server;
public final SSLSocket client;
- private TestSSLSocketPair (TestSSLContext c,
- SSLSocket server,
- SSLSocket client) {
+ private TestSSLSocketPair(TestSSLContext c, SSLSocket server, SSLSocket client) {
this.c = c;
this.server = server;
this.client = client;
@@ -47,14 +46,15 @@
throw new RuntimeException(e);
}
}
- /**
- * based on test_SSLSocket_startHandshake
- */
- public static TestSSLSocketPair create () {
- TestSSLContext c = TestSSLContext.create();
- SSLSocket[] sockets = connect(c, null, null);
- return new TestSSLSocketPair(c, sockets[0], sockets[1]);
+
+ public SSLSocket[] sockets() {
+ return new SSLSocket[] {server, client};
}
+
+ public TestSSLSocketPair connect() {
+ return connect(null, null);
+ }
+
/**
* Create a new connected server/client socket pair within a
* existing SSLContext. Optionally specify clientCipherSuites to
@@ -62,13 +62,9 @@
* caching. Optionally specify serverCipherSuites for testing
* cipher suite negotiation.
*/
- public static SSLSocket[] connect (final TestSSLContext context,
- final String[] clientCipherSuites,
- final String[] serverCipherSuites) {
+ public TestSSLSocketPair connect(
+ final String[] clientCipherSuites, final String[] serverCipherSuites) {
try {
- final SSLSocket client = (SSLSocket)
- context.clientContext.getSocketFactory().createSocket(context.host, context.port);
- final SSLSocket server = (SSLSocket) context.serverSocket.accept();
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Void> s = executor.submit(new Callable<Void>() {
@Override
@@ -115,11 +111,29 @@
if (clientException != null) {
throw clientException;
}
- return new SSLSocket[] { server, client };
+ return this;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
+
+ public static TestSSLSocketPair create() {
+ return create(TestSSLContext.create());
+ }
+
+ /**
+ * based on test_SSLSocket_startHandshake
+ */
+ public static TestSSLSocketPair create(TestSSLContext context) {
+ try {
+ SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+ context.host, context.port);
+ SSLSocket server = (SSLSocket) context.serverSocket.accept();
+ return new TestSSLSocketPair(context, server, client);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/testing/src/main/java/org/conscrypt/ChannelType.java b/testing/src/main/java/org/conscrypt/ChannelType.java
new file mode 100644
index 0000000..09dd582
--- /dev/null
+++ b/testing/src/main/java/org/conscrypt/ChannelType.java
@@ -0,0 +1,130 @@
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * The type of socket to be wrapped by the Conscrypt socket.
+ */
+@SuppressWarnings("unused")
+public enum ChannelType {
+ NONE {
+ @Override
+ SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+ throws IOException {
+ return clientMode(factory.createSocket(address, port));
+ }
+
+ @Override
+ ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException {
+ return factory.createServerSocket(0, 50, InetAddress.getLoopbackAddress());
+ }
+
+ @Override
+ SSLSocket accept(ServerSocket socket, SSLSocketFactory unused) throws IOException {
+ return serverMode(socket.accept());
+ }
+ },
+ NO_CHANNEL {
+ @Override
+ SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+ throws IOException {
+ Socket wrapped = new Socket(address, port);
+ assertNull(wrapped.getChannel());
+
+ return clientMode(factory.createSocket(wrapped, address.getHostName(), port, true));
+ }
+
+ @Override
+ ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
+ return ServerSocketFactory.getDefault().createServerSocket(
+ 0, 50, InetAddress.getLoopbackAddress());
+ }
+
+ @Override
+ SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
+ assertFalse(serverSocket instanceof SSLServerSocket);
+ Socket wrapped = serverSocket.accept();
+ assertNull(wrapped.getChannel());
+
+ return serverMode(factory.createSocket(
+ wrapped, wrapped.getInetAddress().getHostAddress(), wrapped.getPort(), true));
+ }
+ },
+ CHANNEL {
+ @Override
+ SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+ throws IOException {
+ Socket wrapped = SocketChannel.open(new InetSocketAddress(address, port)).socket();
+ return clientMode(factory.createSocket(wrapped, address.getHostName(), port, true));
+ }
+
+ @Override
+ ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
+ return ServerSocketChannel.open()
+ .bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
+ .socket();
+ }
+
+ @Override
+ SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
+ assertFalse(serverSocket instanceof SSLServerSocket);
+ ServerSocketChannel serverChannel = serverSocket.getChannel();
+
+ // Just loop until the accept completes.
+ SocketChannel channel;
+ do {
+ channel = serverChannel.accept();
+ } while (channel == null);
+
+ Socket wrapped = channel.socket();
+ return serverMode(factory.createSocket(
+ wrapped, wrapped.getInetAddress().getHostAddress(), wrapped.getPort(), true));
+ }
+ };
+
+ abstract SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+ throws IOException;
+ abstract ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException;
+ abstract SSLSocket accept(ServerSocket socket, SSLSocketFactory factory) throws IOException;
+
+ private static SSLSocket clientMode(Socket socket) {
+ SSLSocket sslSocket = (SSLSocket) socket;
+ sslSocket.setUseClientMode(true);
+ return sslSocket;
+ }
+
+ private static SSLSocket serverMode(Socket socket) {
+ SSLSocket sslSocket = (SSLSocket) socket;
+ sslSocket.setUseClientMode(false);
+ return sslSocket;
+ }
+}
diff --git a/testing/src/main/java/org/conscrypt/TestUtils.java b/testing/src/main/java/org/conscrypt/TestUtils.java
index ef14e4b..eaa4f2e 100644
--- a/testing/src/main/java/org/conscrypt/TestUtils.java
+++ b/testing/src/main/java/org/conscrypt/TestUtils.java
@@ -47,11 +47,14 @@
private static final Provider JDK_PROVIDER = getDefaultTlsProvider();
private static final byte[] CHARS =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes(UTF_8);
+ private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0);
public static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
public static final String PROVIDER_PROPERTY = "SSLContext.TLSv1.2";
public static final String LOCALHOST = "localhost";
+ static final String TEST_CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+
private TestUtils() {}
private static Provider getDefaultTlsProvider() {
@@ -63,6 +66,10 @@
throw new RuntimeException("Unable to find a default provider for " + PROVIDER_PROPERTY);
}
+ static Provider getJdkProvider() {
+ return JDK_PROVIDER;
+ }
+
public static Provider getConscryptProvider() {
try {
return (Provider) conscryptClass("OpenSSLProvider")
@@ -127,32 +134,36 @@
return getServerSocketFactory(JDK_PROVIDER);
}
- public static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) {
+ static SSLSocketFactory setUseEngineSocket(SSLSocketFactory conscryptFactory, boolean useEngineSocket) {
try {
Class<?> clazz = conscryptClass("Conscrypt$SocketFactories");
Method method = clazz.getMethod("setUseEngineSocket", SSLSocketFactory.class, boolean.class);
-
- SSLSocketFactory socketFactory = getSocketFactory(getConscryptProvider());
- method.invoke(null, socketFactory, useEngineSocket);
- return socketFactory;
+ method.invoke(null, conscryptFactory, useEngineSocket);
+ return conscryptFactory;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
- public static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) {
+ static SSLServerSocketFactory setUseEngineSocket(SSLServerSocketFactory conscryptFactory, boolean useEngineSocket) {
try {
Class<?> clazz = conscryptClass("Conscrypt$ServerSocketFactories");
Method method = clazz.getMethod("setUseEngineSocket", SSLServerSocketFactory.class, boolean.class);
-
- SSLServerSocketFactory socketFactory = getServerSocketFactory(getConscryptProvider());
- method.invoke(null, socketFactory, useEngineSocket);
- return socketFactory;
+ method.invoke(null, conscryptFactory, useEngineSocket);
+ return conscryptFactory;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
+ public static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) {
+ return setUseEngineSocket(getSocketFactory(getConscryptProvider()), useEngineSocket);
+ }
+
+ public static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) {
+ return setUseEngineSocket(getServerSocketFactory(getConscryptProvider()), useEngineSocket);
+ }
+
private static SSLSocketFactory getSocketFactory(Provider provider) {
SSLContext clientContext = initClientSslContext(newContext(provider));
return clientContext.getSocketFactory();
@@ -205,13 +216,23 @@
/**
* Initializes the given engine with the cipher and client mode.
*/
- public static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
+ static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
engine.setEnabledProtocols(getProtocols());
engine.setEnabledCipherSuites(new String[] {cipher});
engine.setUseClientMode(client);
return engine;
}
+ static SSLContext newClientSslContext(Provider provider) {
+ SSLContext context = newContext(provider);
+ return initClientSslContext(context);
+ }
+
+ static SSLContext newServerSslContext(Provider provider) {
+ SSLContext context = newContext(provider);
+ return initServerSslContext(context);
+ }
+
/**
* Initializes the given client-side {@code context} with a default cert.
*/
@@ -241,21 +262,12 @@
/**
* Performs the intial TLS handshake between the two {@link SSLEngine} instances.
*/
- public static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine)
- throws SSLException {
- ByteBuffer cTOs = ByteBuffer.allocate(clientEngine.getSession().getPacketBufferSize());
- ByteBuffer sTOc = ByteBuffer.allocate(serverEngine.getSession().getPacketBufferSize());
-
- ByteBuffer serverAppReadBuffer =
- ByteBuffer.allocate(serverEngine.getSession().getApplicationBufferSize());
- ByteBuffer clientAppReadBuffer =
- ByteBuffer.allocate(clientEngine.getSession().getApplicationBufferSize());
-
+ public static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine,
+ ByteBuffer clientAppBuffer, ByteBuffer clientPacketBuffer, ByteBuffer serverAppBuffer,
+ ByteBuffer serverPacketBuffer) throws SSLException {
clientEngine.beginHandshake();
serverEngine.beginHandshake();
- ByteBuffer empty = ByteBuffer.allocate(0);
-
SSLEngineResult clientResult;
SSLEngineResult serverResult;
@@ -263,22 +275,22 @@
boolean serverHandshakeFinished = false;
do {
- int cTOsPos = cTOs.position();
- int sTOcPos = sTOc.position();
+ int cTOsPos = clientPacketBuffer.position();
+ int sTOcPos = serverPacketBuffer.position();
- clientResult = clientEngine.wrap(empty, cTOs);
+ clientResult = clientEngine.wrap(EMPTY_BUFFER, clientPacketBuffer);
runDelegatedTasks(clientResult, clientEngine);
- serverResult = serverEngine.wrap(empty, sTOc);
+ serverResult = serverEngine.wrap(EMPTY_BUFFER, serverPacketBuffer);
runDelegatedTasks(serverResult, serverEngine);
// Verify that the consumed and produced number match what is in the buffers now.
- assertEquals(empty.remaining(), clientResult.bytesConsumed());
- assertEquals(empty.remaining(), serverResult.bytesConsumed());
- assertEquals(cTOs.position() - cTOsPos, clientResult.bytesProduced());
- assertEquals(sTOc.position() - sTOcPos, serverResult.bytesProduced());
+ assertEquals(0, clientResult.bytesConsumed());
+ assertEquals(0, serverResult.bytesConsumed());
+ assertEquals(clientPacketBuffer.position() - cTOsPos, clientResult.bytesProduced());
+ assertEquals(serverPacketBuffer.position() - sTOcPos, serverResult.bytesProduced());
- cTOs.flip();
- sTOc.flip();
+ clientPacketBuffer.flip();
+ serverPacketBuffer.flip();
// Verify that we only had one SSLEngineResult.HandshakeStatus.FINISHED
if (isHandshakeFinished(clientResult)) {
@@ -290,27 +302,27 @@
serverHandshakeFinished = true;
}
- cTOsPos = cTOs.position();
- sTOcPos = sTOc.position();
+ cTOsPos = clientPacketBuffer.position();
+ sTOcPos = serverPacketBuffer.position();
- int clientAppReadBufferPos = clientAppReadBuffer.position();
- int serverAppReadBufferPos = serverAppReadBuffer.position();
+ int clientAppReadBufferPos = clientAppBuffer.position();
+ int serverAppReadBufferPos = serverAppBuffer.position();
- clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer);
+ clientResult = clientEngine.unwrap(serverPacketBuffer, clientAppBuffer);
runDelegatedTasks(clientResult, clientEngine);
- serverResult = serverEngine.unwrap(cTOs, serverAppReadBuffer);
+ serverResult = serverEngine.unwrap(clientPacketBuffer, serverAppBuffer);
runDelegatedTasks(serverResult, serverEngine);
// Verify that the consumed and produced number match what is in the buffers now.
- assertEquals(sTOc.position() - sTOcPos, clientResult.bytesConsumed());
- assertEquals(cTOs.position() - cTOsPos, serverResult.bytesConsumed());
- assertEquals(clientAppReadBuffer.position() - clientAppReadBufferPos,
+ assertEquals(serverPacketBuffer.position() - sTOcPos, clientResult.bytesConsumed());
+ assertEquals(clientPacketBuffer.position() - cTOsPos, serverResult.bytesConsumed());
+ assertEquals(clientAppBuffer.position() - clientAppReadBufferPos,
clientResult.bytesProduced());
- assertEquals(serverAppReadBuffer.position() - serverAppReadBufferPos,
+ assertEquals(serverAppBuffer.position() - serverAppReadBufferPos,
serverResult.bytesProduced());
- cTOs.compact();
- sTOc.compact();
+ clientPacketBuffer.compact();
+ serverPacketBuffer.compact();
// Verify that we only had one SSLEngineResult.HandshakeStatus.FINISHED
if (isHandshakeFinished(clientResult)) {