am ea961ada: Apply conscrypt changes from merge commit
* commit 'ea961ada17d50ec1f2006c2843d1d4164d0e1342':
Apply conscrypt changes from merge commit
diff --git a/Android.mk b/Android.mk
index b6dcefa..11ba82f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,7 +54,8 @@
# Create the conscrypt library
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
-LOCAL_JAVA_LIBRARIES := core
+LOCAL_SRC_FILES += $(call all-java-files-under,src/platform/java)
+LOCAL_JAVA_LIBRARIES := core-libart
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
@@ -67,7 +68,8 @@
# Create the conscrypt library without jarjar for tests
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
-LOCAL_JAVA_LIBRARIES := core
+LOCAL_SRC_FILES += $(call all-java-files-under,src/platform/java)
+LOCAL_JAVA_LIBRARIES := core-libart
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVACFLAGS := $(local_javac_flags)
LOCAL_MODULE_TAGS := optional
@@ -80,7 +82,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src/test/java)
LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := bouncycastle core core-junit
+LOCAL_JAVA_LIBRARIES := core-libart core-junit bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support conscrypt-nojarjar
LOCAL_JAVACFLAGS := $(local_javac_flags)
LOCAL_MODULE_TAGS := optional
@@ -100,6 +102,7 @@
src/main/native/org_conscrypt_NativeCrypto.cpp
LOCAL_C_INCLUDES += \
external/openssl/include \
+ external/openssl \
libcore/include \
libcore/luni/src/main/native
LOCAL_SHARED_LIBRARIES := libcrypto libjavacore liblog libnativehelper libssl libz
@@ -108,34 +111,82 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_SHARED_LIBRARY)
+# Unbundled Conscrypt jar
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
+LOCAL_SRC_FILES += $(call all-java-files-under,src/compat/java)
+LOCAL_SDK_VERSION := 9
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := conscrypt_unbundled
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Unbundled Conscrypt crypto JNI library
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags)
+LOCAL_SRC_FILES := \
+ src/main/native/org_conscrypt_NativeCrypto.cpp \
+ src/compat/native/JNIHelp.cpp
+LOCAL_C_INCLUDES += \
+ external/openssl/include \
+ external/openssl \
+ external/conscrypt/src/compat/native
+LOCAL_LDFLAGS := -llog -lz -ldl
+LOCAL_STATIC_LIBRARIES := libssl_static libcrypto_static
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libconscrypt_jni
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_SDK_VERSION := 9
+include $(BUILD_SHARED_LIBRARY)
+
+# Static unbundled Conscrypt crypto JNI library
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += $(core_cflags)
+LOCAL_CPPFLAGS += $(core_cppflags) -DJNI_JARJAR_PREFIX="com/google/android/gms/" -DCONSCRYPT_UNBUNDLED -DSTATIC_LIB
+LOCAL_SRC_FILES := \
+ src/main/native/org_conscrypt_NativeCrypto.cpp \
+ src/compat/native/JNIHelp.cpp
+LOCAL_C_INCLUDES += \
+ external/openssl/include \
+ external/openssl \
+ external/conscrypt/src/compat/native
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libconscrypt_static
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_SDK_VERSION := 9
+include $(BUILD_STATIC_LIBRARY)
+
#
# Build for the host.
#
-ifeq ($(WITH_HOST_DALVIK),true)
- # Make the conscrypt-hostdex library
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
- LOCAL_JAVACFLAGS := $(local_javac_flags)
- LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := conscrypt-hostdex
- LOCAL_REQUIRED_MODULES := libjavacrypto
- LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
- include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+# Make the conscrypt-hostdex library
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
+LOCAL_SRC_FILES += $(call all-java-files-under,src/platform/java)
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := conscrypt-hostdex
+LOCAL_REQUIRED_MODULES := libjavacrypto
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
- # Make the conscrypt-hostdex-nojarjar for tests
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
- LOCAL_JAVACFLAGS := $(local_javac_flags)
- LOCAL_BUILD_HOST_DEX := true
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := conscrypt-hostdex-nojarjar
- LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
- include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+# Make the conscrypt-hostdex-nojarjar for tests
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
+LOCAL_SRC_FILES += $(call all-java-files-under,src/platform/java)
+LOCAL_JAVACFLAGS := $(local_javac_flags)
+LOCAL_BUILD_HOST_DEX := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := conscrypt-hostdex-nojarjar
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
- # Make the conscrypt-tests library.
- ifeq ($(LIBCORE_SKIP_TESTS),)
+# Make the conscrypt-tests library.
+ifeq ($(LIBCORE_SKIP_TESTS),)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src/test/java)
LOCAL_JAVA_LIBRARIES := bouncycastle-hostdex core-junit-hostdex core-tests-support-hostdex conscrypt-hostdex-nojarjar
@@ -145,34 +196,40 @@
LOCAL_REQUIRED_MODULES := libjavacrypto
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
- endif
+endif
- # Conscrypt native library for host
+# Conscrypt native library for host
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_SRC_FILES += \
+ src/main/native/org_conscrypt_NativeCrypto.cpp
+LOCAL_C_INCLUDES += \
+ external/openssl/include \
+ external/openssl \
+ libcore/include \
+ libcore/luni/src/main/native
+LOCAL_CPPFLAGS += $(core_cppflags)
+LOCAL_LDLIBS += -lpthread
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libjavacrypto
+LOCAL_CFLAGS += -DJNI_JARJAR_PREFIX="com/android/"
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_SHARED_LIBRARIES := libcrypto-host libjavacore liblog libnativehelper libssl-host
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Conscrypt native library for nojarjar'd version
+# Don't build this for unbundled conscrypt build
+ifeq (,$(TARGET_BUILD_APPS))
include $(CLEAR_VARS)
+ LOCAL_CLANG := true
LOCAL_SRC_FILES += \
src/main/native/org_conscrypt_NativeCrypto.cpp
LOCAL_C_INCLUDES += \
external/openssl/include \
+ external/openssl \
libcore/include \
libcore/luni/src/main/native
- LOCAL_CPPFLAGS += $(core_cppflags)
- LOCAL_LDLIBS += -lpthread
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := libjavacrypto
- LOCAL_CFLAGS += -DJNI_JARJAR_PREFIX="com/android/"
- LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
- LOCAL_SHARED_LIBRARIES := libcrypto-host libjavacore liblog libnativehelper libssl-host
- include $(BUILD_HOST_SHARED_LIBRARY)
-
- # Conscrypt native library for nojarjar'd version
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES += \
- src/main/native/org_conscrypt_NativeCrypto.cpp
- LOCAL_C_INCLUDES += \
- external/openssl/include \
- libcore/include \
- libcore/luni/src/main/native
- LOCAL_CPPFLAGS += $(core_cppflags)
+ LOCAL_CPPFLAGS += $(core_cppflags) -DCONSCRYPT_NOT_UNBUNDLED
LOCAL_LDLIBS += -lpthread
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libconscrypt_jni
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..12d7b99
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'java'
+
+// this is the "Unbundled Conscrypt jar"
+sourceSets.main {
+ java.srcDirs = [
+ 'src/main/java',
+ 'src/compat/java',
+ ]
+}
+
+compileJava.options.encoding = 'UTF-8'
+compileJava.options.compilerArgs += ['-Xmaxwarns', '9999999']
+
+dependencies {
+ compile getAndroidPrebuilt('9')
+}
diff --git a/src/compat/java/dalvik/system/BlockGuard.java b/src/compat/java/dalvik/system/BlockGuard.java
new file mode 100644
index 0000000..e2911d8
--- /dev/null
+++ b/src/compat/java/dalvik/system/BlockGuard.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.lang.reflect.Method;
+
+public class BlockGuard {
+ private static Method m_getThreadPolicy;
+ private static Method m_onNetwork;
+ static {
+ try {
+ ClassLoader cl = ClassLoader.getSystemClassLoader();
+ Class<?> c_closeGuard = cl.loadClass("dalvik.system.BlockGuard");
+
+ m_getThreadPolicy = c_closeGuard.getDeclaredMethod("getThreadPolicy");
+ m_getThreadPolicy.setAccessible(true);
+
+ Class<?> c_policy = cl.loadClass("dalvik.system.BlockGuard.Policy");
+
+ m_onNetwork = c_policy.getDeclaredMethod("onNetwork");
+ m_onNetwork.setAccessible(true);
+ } catch (Exception ignored) {
+ }
+ }
+
+ private BlockGuard() {
+ }
+
+ public static Policy getThreadPolicy() {
+ if (m_getThreadPolicy != null) {
+ try {
+ Object wrappedPolicy = m_getThreadPolicy.invoke(null);
+ return new PolicyWrapper(wrappedPolicy);
+ } catch (Exception ignored) {
+ }
+ }
+ return new PolicyWrapper(null);
+ }
+
+ public interface Policy {
+ void onNetwork();
+ }
+
+ public static class PolicyWrapper implements Policy {
+ private final Object wrappedPolicy;
+
+ private PolicyWrapper(Object wrappedPolicy) {
+ this.wrappedPolicy = wrappedPolicy;
+ }
+
+ public void onNetwork() {
+ if (m_onNetwork != null && wrappedPolicy != null) {
+ try {
+ m_onNetwork.invoke(wrappedPolicy);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/compat/java/dalvik/system/CloseGuard.java b/src/compat/java/dalvik/system/CloseGuard.java
new file mode 100644
index 0000000..1063e07
--- /dev/null
+++ b/src/compat/java/dalvik/system/CloseGuard.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+import java.lang.reflect.Method;
+
+public class CloseGuard {
+ private static Method m_get;
+ private static Method m_open;
+ private static Method m_close;
+ private static Method m_warnIfOpen;
+ static {
+ try {
+ Class<?> c_closeGuard = Class.forName("dalvik.system.CloseGuard");
+
+ m_get = c_closeGuard.getDeclaredMethod("get");
+ m_get.setAccessible(true);
+
+ m_open = c_closeGuard.getDeclaredMethod("open", String.class);
+ m_open.setAccessible(true);
+
+ m_close = c_closeGuard.getDeclaredMethod("close");
+ m_close.setAccessible(true);
+
+ m_warnIfOpen = c_closeGuard.getDeclaredMethod("warnIfOpen");
+ m_warnIfOpen.setAccessible(true);
+ } catch (Exception ignored) {
+ }
+ }
+
+ private final Object wrappedGuard;
+
+ private CloseGuard(Object wrappedGuard) {
+ this.wrappedGuard = wrappedGuard;
+ }
+
+ public static CloseGuard get() {
+ if (m_get != null) {
+ try {
+ return new CloseGuard(m_get.invoke(null));
+ } catch (Exception ignored) {
+ }
+ }
+ return new CloseGuard(null);
+ }
+
+ public void open(String message) {
+ if (wrappedGuard != null && m_open != null) {
+ try {
+ m_open.invoke(wrappedGuard, message);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ public void close() {
+ if (wrappedGuard != null && m_close != null) {
+ try {
+ m_close.invoke(wrappedGuard);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ public void warnIfOpen() {
+ if (wrappedGuard != null && m_warnIfOpen != null) {
+ try {
+ m_warnIfOpen.invoke(wrappedGuard);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+}
diff --git a/src/compat/java/org/apache/harmony/security/utils/AlgNameMapper.java b/src/compat/java/org/apache/harmony/security/utils/AlgNameMapper.java
new file mode 100644
index 0000000..054ed87
--- /dev/null
+++ b/src/compat/java/org/apache/harmony/security/utils/AlgNameMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.security.utils;
+
+import org.conscrypt.NativeCrypto;
+
+public class AlgNameMapper {
+ private AlgNameMapper() {
+ }
+
+ public static String map2AlgName(String oid) {
+ return NativeCrypto.OBJ_txt2nid_longName(oid);
+ }
+
+ public static void setSource(Object o) {
+ }
+}
diff --git a/src/compat/java/org/apache/harmony/security/utils/AlgNameMapperSource.java b/src/compat/java/org/apache/harmony/security/utils/AlgNameMapperSource.java
new file mode 100644
index 0000000..abca36c
--- /dev/null
+++ b/src/compat/java/org/apache/harmony/security/utils/AlgNameMapperSource.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.apache.harmony.security.utils;
+
+/**
+ * Provides a mapping source that the {@link AlgNameMapper} can query for
+ * mapping between algorithm names and OIDs.
+ */
+public interface AlgNameMapperSource {
+ public String mapNameToOid(String algName);
+
+ public String mapOidToName(String oid);
+}
diff --git a/src/compat/java/org/conscrypt/Platform.java b/src/compat/java/org/conscrypt/Platform.java
new file mode 100644
index 0000000..d840a14
--- /dev/null
+++ b/src/compat/java/org/conscrypt/Platform.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import android.os.Build;
+import android.util.Log;
+import java.io.FileDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.ECParameterSpec;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ *
+ */
+public class Platform {
+ private static final String TAG = "Conscrypt";
+
+ private static Method m_getCurveName;
+ static {
+ try {
+ m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName");
+ m_getCurveName.setAccessible(true);
+ } catch (Exception ignored) {
+ }
+ }
+
+ public static void setup() {
+ }
+
+ public static FileDescriptor getFileDescriptor(Socket s) {
+ try {
+ Field f_impl = Socket.class.getDeclaredField("impl");
+ f_impl.setAccessible(true);
+ Object socketImpl = f_impl.get(s);
+ Class<?> c_socketImpl = Class.forName("java.net.SocketImpl");
+ Field f_fd = c_socketImpl.getDeclaredField("fd");
+ f_fd.setAccessible(true);
+ return (FileDescriptor) f_fd.get(socketImpl);
+ } catch (Exception e) {
+ throw new RuntimeException("Can't get FileDescriptor from socket", e);
+ }
+ }
+
+ public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
+ return getFileDescriptor(openSSLSocketImpl);
+ }
+
+ public static String getCurveName(ECParameterSpec spec) {
+ if (m_getCurveName == null) {
+ return null;
+ }
+ try {
+ return (String) m_getCurveName.invoke(spec);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public static void setCurveName(ECParameterSpec spec, String curveName) {
+ try {
+ Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class);
+ setCurveName.invoke(spec, curveName);
+ } catch (Exception ignored) {
+ }
+ }
+
+ public static void setSocketTimeout(Socket s, long timeoutMillis) {
+ // TODO: implement this for unbundled
+ }
+
+ public static void setEndpointIdentificationAlgorithm(SSLParameters params,
+ String endpointIdentificationAlgorithm) {
+ // TODO: implement this for unbundled
+ }
+
+ public static String getEndpointIdentificationAlgorithm(SSLParameters params) {
+ // TODO: implement this for unbundled
+ return null;
+ }
+
+ public static void checkServerTrusted(X509TrustManager x509tm, X509Certificate[] chain,
+ String authType, String host) throws CertificateException {
+ // TODO: use reflection to find whether we have TrustManagerImpl
+ /*
+ if (x509tm instanceof TrustManagerImpl) {
+ TrustManagerImpl tm = (TrustManagerImpl) x509tm;
+ tm.checkServerTrusted(chain, authType, host);
+ } else {
+ */
+ x509tm.checkServerTrusted(chain, authType);
+ /*
+ }
+ */
+ }
+
+ /**
+ * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
+ * builds since we didn't backport, so return null. This code is from
+ * Chromium's net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java
+ */
+ public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) throws InvalidKeyException {
+ // This fixup only applies to pre-JB-MR1
+ if (Build.VERSION.SDK_INT >= 17) {
+ return null;
+ }
+
+ // First, check that this is a proper instance of OpenSSLRSAPrivateKey
+ // or one of its sub-classes.
+ Class<?> superClass;
+ try {
+ superClass = Class
+ .forName("org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
+ } catch (Exception e) {
+ // This may happen if the target device has a completely different
+ // implementation of the java.security APIs, compared to vanilla
+ // Android. Highly unlikely, but still possible.
+ Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
+ return null;
+ }
+ if (!superClass.isInstance(javaKey)) {
+ // This may happen if the PrivateKey was not created by the
+ // "AndroidOpenSSL"
+ // 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:"
+ + javaKey.getClass().getCanonicalName());
+ return null;
+ }
+
+ try {
+ // Use reflection to invoke the 'getOpenSSLKey()' method on
+ // the private key. This returns another Java object that wraps
+ // a native EVP_PKEY. Note that the method is final, so calling
+ // the superclass implementation is ok.
+ Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
+ getKey.setAccessible(true);
+ Object opensslKey = null;
+ try {
+ opensslKey = getKey.invoke(javaKey);
+ } finally {
+ getKey.setAccessible(false);
+ }
+ if (opensslKey == null) {
+ // Bail when detecting OEM "enhancement".
+ Log.e(TAG, "Could not getOpenSSLKey on instance: " + javaKey.toString());
+ return null;
+ }
+
+ // Use reflection to invoke the 'getPkeyContext' method on the
+ // result of the getOpenSSLKey(). This is an 32-bit integer
+ // which is the address of an EVP_PKEY object. Note that this
+ // method these days returns a 64-bit long, but since this code
+ // path is used for older Android versions, it may still return
+ // a 32-bit int here. To be on the safe side, we cast the return
+ // value via Number rather than directly to Integer or Long.
+ Method getPkeyContext;
+ try {
+ getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
+ } catch (Exception e) {
+ // Bail here too, something really not working as expected.
+ Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
+ return null;
+ }
+ getPkeyContext.setAccessible(true);
+ long evp_pkey = 0;
+ try {
+ evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
+ } finally {
+ getPkeyContext.setAccessible(false);
+ }
+ if (evp_pkey == 0) {
+ // The PrivateKey is probably rotten for some reason.
+ Log.e(TAG, "getPkeyContext() returned null");
+ return null;
+ }
+ return new OpenSSLKey(evp_pkey);
+ } catch (Exception e) {
+ Log.e(TAG, "Error during conversion of privatekey instance: " + javaKey.toString(), e);
+ return null;
+ }
+ }
+
+ /**
+ * Logs to the system EventLog system.
+ */
+ public static void logEvent(String message) {
+ try {
+ Class processClass = Class.forName("android.os.Process");
+ Object processInstance = processClass.newInstance();
+ Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
+ int uid = (Integer) myUidMethod.invoke(processInstance);
+
+ Class eventLogClass = Class.forName("android.util.EventLog");
+ Object eventLogInstance = eventLogClass.newInstance();
+ Method writeEventMethod = eventLogClass.getMethod("writeEvent",
+ new Class[] { Integer.TYPE, Object[].class });
+ writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
+ new Object[] { "conscrypt", uid, message });
+ } catch (Exception e) {
+ // Fail silently
+ }
+ }
+}
diff --git a/src/compat/native/JNIHelp.cpp b/src/compat/native/JNIHelp.cpp
new file mode 100644
index 0000000..a4aa996
--- /dev/null
+++ b/src/compat/native/JNIHelp.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+#define LOG_TAG "JNIHelp"
+
+#include "JNIHelp.h"
+
+#include <android/log.h>
+#include "log_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/**
+ * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
+ */
+template<typename T>
+class scoped_local_ref {
+public:
+ scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
+ : mEnv(env), mLocalRef(localRef)
+ {
+ }
+
+ ~scoped_local_ref() {
+ reset();
+ }
+
+ void reset(T localRef = NULL) {
+ if (mLocalRef != NULL) {
+ (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
+ mLocalRef = localRef;
+ }
+ }
+
+ T get() const {
+ return mLocalRef;
+ }
+
+private:
+ C_JNIEnv* mEnv;
+ T mLocalRef;
+
+ // Disallow copy and assignment.
+ scoped_local_ref(const scoped_local_ref&);
+ void operator=(const scoped_local_ref&);
+};
+
+static jclass findClass(C_JNIEnv* env, const char* className) {
+ JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+ return (*env)->FindClass(e, className);
+}
+
+extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
+ const JNINativeMethod* gMethods, int numMethods)
+{
+ JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+
+ ALOGV("Registering %s's %d native methods...", className, numMethods);
+
+ scoped_local_ref<jclass> c(env, findClass(env, className));
+ if (c.get() == NULL) {
+ char* msg;
+ asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
+ e->FatalError(msg);
+ }
+
+ if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
+ char* msg;
+ asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
+ e->FatalError(msg);
+ }
+
+ return 0;
+}
+
+extern "C" int jniThrowException(C_JNIEnv* c_env, const char* className, const char* msg) {
+ JNIEnv* env = reinterpret_cast<JNIEnv*>(c_env);
+ jclass exceptionClass = env->FindClass(className);
+
+ if (exceptionClass == NULL) {
+ ALOGD("Unable to find exception class %s", className);
+ /* ClassNotFoundException now pending */
+ return -1;
+ }
+
+ if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {
+ ALOGD("Failed throwing '%s' '%s'", className, msg);
+ /* an exception, most likely OOM, will now be pending */
+ return -1;
+ }
+
+ env->DeleteLocalRef(exceptionClass);
+ return 0;
+}
+
+int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
+ char msgBuf[512];
+ vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+ return jniThrowException(env, className, msgBuf);
+}
+
+int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
+ return jniThrowException(env, "java/lang/NullPointerException", msg);
+}
+
+int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
+ return jniThrowException(env, "java/lang/RuntimeException", msg);
+}
+
+int jniThrowIOException(C_JNIEnv* env, int errnum) {
+ char buffer[80];
+ const char* message = jniStrError(errnum, buffer, sizeof(buffer));
+ return jniThrowException(env, "java/io/IOException", message);
+}
+
+const char* jniStrError(int errnum, char* buf, size_t buflen) {
+#if __GLIBC__
+ // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
+ // char *strerror_r(int errnum, char *buf, size_t n);
+ return strerror_r(errnum, buf, buflen);
+#else
+ int rc = strerror_r(errnum, buf, buflen);
+ if (rc != 0) {
+ // (POSIX only guarantees a value other than 0. The safest
+ // way to implement this function is to use C++ and overload on the
+ // type of strerror_r to accurately distinguish GNU from POSIX.)
+ snprintf(buf, buflen, "errno %d", errnum);
+ }
+ return buf;
+#endif
+}
+
+int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
+ JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+ scoped_local_ref<jclass> localClass(env, e->FindClass("java/io/FileDescriptor"));
+ static jfieldID fid = e->GetFieldID(localClass.get(), "descriptor", "I");
+ if (fileDescriptor != NULL) {
+ return (*env)->GetIntField(e, fileDescriptor, fid);
+ } else {
+ return -1;
+ }
+}
diff --git a/src/compat/native/JNIHelp.h b/src/compat/native/JNIHelp.h
new file mode 100644
index 0000000..cfab8f7
--- /dev/null
+++ b/src/compat/native/JNIHelp.h
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+/*
+ * JNI helper functions.
+ *
+ * This file may be included by C or C++ code, which is trouble because jni.h
+ * uses different typedefs for JNIEnv in each language.
+ *
+ * TODO: remove C support.
+ */
+#ifndef NATIVEHELPER_JNIHELP_H_
+#define NATIVEHELPER_JNIHELP_H_
+
+#include "jni.h"
+#include <errno.h>
+#include <unistd.h>
+
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Register one or more native methods with a particular class.
+ * "className" looks like "java/lang/String". Aborts on failure.
+ * TODO: fix all callers and change the return type to void.
+ */
+int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
+
+/*
+ * Throw an exception with the specified class and an optional message.
+ *
+ * The "className" argument will be passed directly to FindClass, which
+ * takes strings with slashes (e.g. "java/lang/Object").
+ *
+ * If an exception is currently pending, we log a warning message and
+ * clear it.
+ *
+ * Returns 0 on success, nonzero if something failed (e.g. the exception
+ * class couldn't be found, so *an* exception will still be pending).
+ *
+ * Currently aborts the VM if it can't throw the exception.
+ */
+int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
+
+/*
+ * Throw a java.lang.NullPointerException, with an optional message.
+ */
+int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
+ */
+int jniThrowIOException(C_JNIEnv* env, int errnum);
+
+/*
+ * Return a pointer to a locale-dependent error string explaining errno
+ * value 'errnum'. The returned pointer may or may not be equal to 'buf'.
+ * This function is thread-safe (unlike strerror) and portable (unlike
+ * strerror_r).
+ */
+const char* jniStrError(int errnum, char* buf, size_t buflen);
+
+/*
+ * Returns a new java.io.FileDescriptor for the given int fd.
+ */
+jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
+
+/*
+ * Returns the int fd from a java.io.FileDescriptor.
+ */
+int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
+
+/*
+ * Sets the int fd in a java.io.FileDescriptor.
+ */
+void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
+
+/*
+ * Returns the reference from a java.lang.ref.Reference.
+ */
+jobject jniGetReferent(C_JNIEnv* env, jobject ref);
+
+/*
+ * Log a message and an exception.
+ * If exception is NULL, logs the current exception in the JNI environment.
+ */
+void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * For C++ code, we provide inlines that map to the C functions. g++ always
+ * inlines these, even on non-optimized builds.
+ */
+#if defined(__cplusplus)
+inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
+ return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods);
+}
+
+inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
+ return jniThrowException(&env->functions, className, msg);
+}
+
+extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
+
+/*
+ * Equivalent to jniThrowException but with a printf-like format string and
+ * variable-length argument list. This is only available in C++.
+ */
+inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ return jniThrowExceptionFmt(&env->functions, className, fmt, args);
+ va_end(args);
+}
+
+inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
+ return jniThrowNullPointerException(&env->functions, msg);
+}
+
+inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
+ return jniThrowRuntimeException(&env->functions, msg);
+}
+
+inline int jniThrowIOException(JNIEnv* env, int errnum) {
+ return jniThrowIOException(&env->functions, errnum);
+}
+
+inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
+ return jniCreateFileDescriptor(&env->functions, fd);
+}
+
+inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
+ return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
+}
+
+inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
+ jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
+}
+
+inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
+ return jniGetReferent(&env->functions, ref);
+}
+
+inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) {
+ jniLogException(&env->functions, priority, tag, exception);
+}
+
+#endif
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#endif /* NATIVEHELPER_JNIHELP_H_ */
diff --git a/src/compat/native/NetFd.h b/src/compat/native/NetFd.h
new file mode 100644
index 0000000..235b057
--- /dev/null
+++ b/src/compat/native/NetFd.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef NET_FD_H_included
+#define NET_FD_H_included
+
+/**
+ * Wraps access to the int inside a java.io.FileDescriptor, taking care of throwing exceptions.
+ */
+class NetFd {
+public:
+ NetFd(JNIEnv* env, jobject fileDescriptor)
+ : mEnv(env), mFileDescriptor(fileDescriptor), mFd(-1)
+ {
+ }
+
+ bool isClosed() {
+ mFd = jniGetFDFromFileDescriptor(mEnv, mFileDescriptor);
+ bool closed = (mFd == -1);
+ if (closed) {
+ jniThrowException(mEnv, "java/net/SocketException", "Socket closed");
+ }
+ return closed;
+ }
+
+ int get() const {
+ return mFd;
+ }
+
+private:
+ JNIEnv* mEnv;
+ jobject mFileDescriptor;
+ int mFd;
+
+ // Disallow copy and assignment.
+ NetFd(const NetFd&);
+ void operator=(const NetFd&);
+};
+
+/**
+ * Used to retry syscalls that can return EINTR. This differs from TEMP_FAILURE_RETRY in that
+ * it also considers the case where the reason for failure is that another thread called
+ * Socket.close.
+ */
+#define NET_FAILURE_RETRY(fd, exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ if (_rc == -1) { \
+ if (fd.isClosed() || errno != EINTR) { \
+ break; \
+ } \
+ } \
+ } while (_rc == -1); \
+ _rc; })
+
+#endif // NET_FD_H_included
diff --git a/src/compat/native/ScopedLocalRef.h b/src/compat/native/ScopedLocalRef.h
new file mode 100644
index 0000000..71d5776
--- /dev/null
+++ b/src/compat/native/ScopedLocalRef.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_LOCAL_REF_H_included
+#define SCOPED_LOCAL_REF_H_included
+
+#include "jni.h"
+
+#include <stddef.h>
+
+// A smart pointer that deletes a JNI local reference when it goes out of scope.
+template<typename T>
+class ScopedLocalRef {
+public:
+ ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
+ }
+
+ ~ScopedLocalRef() {
+ reset();
+ }
+
+ void reset(T ptr = NULL) {
+ if (ptr != mLocalRef) {
+ if (mLocalRef != NULL) {
+ mEnv->DeleteLocalRef(mLocalRef);
+ }
+ mLocalRef = ptr;
+ }
+ }
+
+ T release() __attribute__((warn_unused_result)) {
+ T localRef = mLocalRef;
+ mLocalRef = NULL;
+ return localRef;
+ }
+
+ T get() const {
+ return mLocalRef;
+ }
+
+private:
+ JNIEnv* mEnv;
+ T mLocalRef;
+
+ // Disallow copy and assignment.
+ ScopedLocalRef(const ScopedLocalRef&);
+ void operator=(const ScopedLocalRef&);
+};
+
+#endif // SCOPED_LOCAL_REF_H_included
diff --git a/src/compat/native/ScopedPrimitiveArray.h b/src/compat/native/ScopedPrimitiveArray.h
new file mode 100644
index 0000000..d797b9d
--- /dev/null
+++ b/src/compat/native/ScopedPrimitiveArray.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_PRIMITIVE_ARRAY_H_included
+#define SCOPED_PRIMITIVE_ARRAY_H_included
+
+#include "JNIHelp.h"
+
+// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO,
+// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide
+// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write
+// access and should be used by default.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \
+ class Scoped ## NAME ## ArrayRO { \
+ public: \
+ explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \
+ : mEnv(env), mJavaArray(NULL), mRawArray(NULL) {} \
+ Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+ : mEnv(env), mJavaArray(javaArray), mRawArray(NULL) { \
+ if (mJavaArray == NULL) { \
+ jniThrowNullPointerException(mEnv, NULL); \
+ } else { \
+ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+ } \
+ } \
+ ~Scoped ## NAME ## ArrayRO() { \
+ if (mRawArray) { \
+ mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \
+ } \
+ } \
+ void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+ mJavaArray = javaArray; \
+ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+ } \
+ const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+ PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+ const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+ size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
+ private: \
+ JNIEnv* mEnv; \
+ PRIMITIVE_TYPE ## Array mJavaArray; \
+ PRIMITIVE_TYPE* mRawArray; \
+ Scoped ## NAME ## ArrayRO(const Scoped ## NAME ## ArrayRO&); \
+ void operator=(const Scoped ## NAME ## ArrayRO&); \
+ }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO
+
+// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW,
+// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide
+// convenient read-write access to Java arrays from JNI code. These are more expensive,
+// since they entail a copy back onto the Java heap, and should only be used when necessary.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \
+ class Scoped ## NAME ## ArrayRW { \
+ public: \
+ explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \
+ : mEnv(env), mJavaArray(NULL), mRawArray(NULL) {} \
+ Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+ : mEnv(env), mJavaArray(javaArray), mRawArray(NULL) { \
+ if (mJavaArray == NULL) { \
+ jniThrowNullPointerException(mEnv, NULL); \
+ } else { \
+ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+ } \
+ } \
+ ~Scoped ## NAME ## ArrayRW() { \
+ if (mRawArray) { \
+ mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \
+ } \
+ } \
+ void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+ mJavaArray = javaArray; \
+ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+ } \
+ const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+ PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+ const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+ PRIMITIVE_TYPE* get() { return mRawArray; } \
+ PRIMITIVE_TYPE& operator[](size_t n) { return mRawArray[n]; } \
+ size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
+ private: \
+ JNIEnv* mEnv; \
+ PRIMITIVE_TYPE ## Array mJavaArray; \
+ PRIMITIVE_TYPE* mRawArray; \
+ Scoped ## NAME ## ArrayRW(const Scoped ## NAME ## ArrayRW&); \
+ void operator=(const Scoped ## NAME ## ArrayRW&); \
+ }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW
+
+#endif // SCOPED_PRIMITIVE_ARRAY_H_included
diff --git a/src/compat/native/ScopedUtfChars.h b/src/compat/native/ScopedUtfChars.h
new file mode 100644
index 0000000..7761450
--- /dev/null
+++ b/src/compat/native/ScopedUtfChars.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_UTF_CHARS_H_included
+#define SCOPED_UTF_CHARS_H_included
+
+#include "JNIHelp.h"
+#include <string.h>
+
+// A smart pointer that provides read-only access to a Java string's UTF chars.
+// Unlike GetStringUTFChars, we throw NullPointerException rather than abort if
+// passed a null jstring, and c_str will return NULL.
+// This makes the correct idiom very simple:
+//
+// ScopedUtfChars name(env, java_name);
+// if (name.c_str() == NULL) {
+// return NULL;
+// }
+class ScopedUtfChars {
+ public:
+ ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+ if (s == NULL) {
+ utf_chars_ = NULL;
+ jniThrowNullPointerException(env, NULL);
+ } else {
+ utf_chars_ = env->GetStringUTFChars(s, NULL);
+ }
+ }
+
+ ~ScopedUtfChars() {
+ if (utf_chars_) {
+ env_->ReleaseStringUTFChars(string_, utf_chars_);
+ }
+ }
+
+ const char* c_str() const {
+ return utf_chars_;
+ }
+
+ size_t size() const {
+ return strlen(utf_chars_);
+ }
+
+ const char& operator[](size_t n) const {
+ return utf_chars_[n];
+ }
+
+ private:
+ JNIEnv* env_;
+ jstring string_;
+ const char* utf_chars_;
+
+ // Disallow copy and assignment.
+ ScopedUtfChars(const ScopedUtfChars&);
+ void operator=(const ScopedUtfChars&);
+};
+
+#endif // SCOPED_UTF_CHARS_H_included
diff --git a/src/compat/native/UniquePtr.h b/src/compat/native/UniquePtr.h
new file mode 100644
index 0000000..50f75b2
--- /dev/null
+++ b/src/compat/native/UniquePtr.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef UNIQUE_PTR_H_included
+#define UNIQUE_PTR_H_included
+
+#include <cstdlib> // For NULL.
+
+// This is a fake declaration of std::swap to avoid including <algorithm>
+namespace std {
+template <class T> void swap(T&, T&);
+}
+
+// Default deleter for pointer types.
+template <typename T>
+struct DefaultDelete {
+ enum { type_must_be_complete = sizeof(T) };
+ DefaultDelete() {}
+ void operator()(T* p) const {
+ delete p;
+ }
+};
+
+// Default deleter for array types.
+template <typename T>
+struct DefaultDelete<T[]> {
+ enum { type_must_be_complete = sizeof(T) };
+ void operator()(T* p) const {
+ delete[] p;
+ }
+};
+
+// A smart pointer that deletes the given pointer on destruction.
+// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr
+// and boost::scoped_array).
+// Named to be in keeping with Android style but also to avoid
+// collision with any other implementation, until we can switch over
+// to unique_ptr.
+// Use thus:
+// UniquePtr<C> c(new C);
+template <typename T, typename D = DefaultDelete<T> >
+class UniquePtr {
+public:
+ // Construct a new UniquePtr, taking ownership of the given raw pointer.
+ explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
+ }
+
+ ~UniquePtr() {
+ reset();
+ }
+
+ // Accessors.
+ T& operator*() const { return *mPtr; }
+ T* operator->() const { return mPtr; }
+ T* get() const { return mPtr; }
+
+ // Returns the raw pointer and hands over ownership to the caller.
+ // The pointer will not be deleted by UniquePtr.
+ T* release() __attribute__((warn_unused_result)) {
+ T* result = mPtr;
+ mPtr = NULL;
+ return result;
+ }
+
+ // Takes ownership of the given raw pointer.
+ // If this smart pointer previously owned a different raw pointer, that
+ // raw pointer will be freed.
+ void reset(T* ptr = NULL) {
+ if (ptr != mPtr) {
+ D()(mPtr);
+ mPtr = ptr;
+ }
+ }
+
+ // Swap with another unique pointer.
+ void swap(UniquePtr<T>& other) {
+ std::swap(mPtr, other.mPtr);
+ }
+
+private:
+ // The raw pointer.
+ T* mPtr;
+
+ // Comparing unique pointers is probably a mistake, since they're unique.
+ template <typename T2> bool operator==(const UniquePtr<T2>& p) const;
+ template <typename T2> bool operator!=(const UniquePtr<T2>& p) const;
+
+ // Disallow copy and assignment.
+ UniquePtr(const UniquePtr&);
+ void operator=(const UniquePtr&);
+};
+
+// Partial specialization for array types. Like std::unique_ptr, this removes
+// operator* and operator-> but adds operator[].
+template <typename T, typename D>
+class UniquePtr<T[], D> {
+public:
+ explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
+ }
+
+ ~UniquePtr() {
+ reset();
+ }
+
+ T& operator[](size_t i) const {
+ return mPtr[i];
+ }
+ T* get() const { return mPtr; }
+
+ T* release() __attribute__((warn_unused_result)) {
+ T* result = mPtr;
+ mPtr = NULL;
+ return result;
+ }
+
+ void reset(T* ptr = NULL) {
+ if (ptr != mPtr) {
+ D()(mPtr);
+ mPtr = ptr;
+ }
+ }
+
+private:
+ T* mPtr;
+
+ // Disallow copy and assignment.
+ UniquePtr(const UniquePtr&);
+ void operator=(const UniquePtr&);
+};
+
+#if UNIQUE_PTR_TESTS
+
+// Run these tests with:
+// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out
+
+#include <stdio.h>
+
+static void assert(bool b) {
+ if (!b) {
+ fprintf(stderr, "FAIL\n");
+ abort();
+ }
+ fprintf(stderr, "OK\n");
+}
+static int cCount = 0;
+struct C {
+ C() { ++cCount; }
+ ~C() { --cCount; }
+};
+static bool freed = false;
+struct Freer {
+ void operator()(int* p) {
+ assert(*p == 123);
+ free(p);
+ freed = true;
+ }
+};
+
+int main(int argc, char* argv[]) {
+ //
+ // UniquePtr<T> tests...
+ //
+
+ // Can we free a single object?
+ {
+ UniquePtr<C> c(new C);
+ assert(cCount == 1);
+ }
+ assert(cCount == 0);
+ // Does release work?
+ C* rawC;
+ {
+ UniquePtr<C> c(new C);
+ assert(cCount == 1);
+ rawC = c.release();
+ }
+ assert(cCount == 1);
+ delete rawC;
+ // Does reset work?
+ {
+ UniquePtr<C> c(new C);
+ assert(cCount == 1);
+ c.reset(new C);
+ assert(cCount == 1);
+ }
+ assert(cCount == 0);
+
+ //
+ // UniquePtr<T[]> tests...
+ //
+
+ // Can we free an array?
+ {
+ UniquePtr<C[]> cs(new C[4]);
+ assert(cCount == 4);
+ }
+ assert(cCount == 0);
+ // Does release work?
+ {
+ UniquePtr<C[]> c(new C[4]);
+ assert(cCount == 4);
+ rawC = c.release();
+ }
+ assert(cCount == 4);
+ delete[] rawC;
+ // Does reset work?
+ {
+ UniquePtr<C[]> c(new C[4]);
+ assert(cCount == 4);
+ c.reset(new C[2]);
+ assert(cCount == 2);
+ }
+ assert(cCount == 0);
+
+ //
+ // Custom deleter tests...
+ //
+ assert(!freed);
+ {
+ UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int))));
+ *i = 123;
+ }
+ assert(freed);
+ return 0;
+}
+#endif
+
+#endif // UNIQUE_PTR_H_included
diff --git a/src/compat/native/log_compat.h b/src/compat/native/log_compat.h
new file mode 100644
index 0000000..2bd57c4
--- /dev/null
+++ b/src/compat/native/log_compat.h
@@ -0,0 +1,6 @@
+#define ALOGD(...) \
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);
+#define ALOGE(...) \
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);
+#define ALOGV(...) \
+ __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__);
diff --git a/src/main/java/org/conscrypt/AbstractSessionContext.java b/src/main/java/org/conscrypt/AbstractSessionContext.java
index cac1fb7..0ba06ea 100644
--- a/src/main/java/org/conscrypt/AbstractSessionContext.java
+++ b/src/main/java/org/conscrypt/AbstractSessionContext.java
@@ -295,7 +295,7 @@
}
static void log(Throwable t) {
- System.logW("Error converting session.", t);
+ new Exception("Error converting session", t).printStackTrace();
}
@Override
diff --git a/src/main/java/org/conscrypt/AlertException.java b/src/main/java/org/conscrypt/AlertException.java
deleted file mode 100644
index a483021..0000000
--- a/src/main/java/org/conscrypt/AlertException.java
+++ /dev/null
@@ -1,66 +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 javax.net.ssl.SSLException;
-
-/**
- * This exception is used to signal that a fatal alert has occurred while working through the
- * protocol.
- */
-public class AlertException extends RuntimeException {
-
- private static final long serialVersionUID = -4448327177165687581L;
- // SSLException to be thrown to application side
- private final SSLException reason;
- // alert description code
- private final byte description;
-
- /**
- * Constructs the instance.
- *
- * @param description The alert description code from {@link AlertProtocol}
- * @param reason The SSLException to be thrown to application side after alert processing
- * (sending the record with alert, shutdown work, etc).
- * @see AlertProtocol
- */
- protected AlertException(byte description, SSLException reason) {
- super(reason);
- this.reason = reason;
- this.description = description;
- }
-
- /**
- * Returns the reason of alert. This reason should be rethrown after alert processing.
- *
- * @return the reason of alert.
- */
- protected SSLException getReason() {
- return reason;
- }
-
- /**
- * Returns alert's description code.
- *
- * @return alert description code from {@link AlertProtocol}
- * @see AlertProtocol for more information about possible reason codes.
- */
- protected byte getDescriptionCode() {
- return description;
- }
-}
diff --git a/src/main/java/org/conscrypt/AlertProtocol.java b/src/main/java/org/conscrypt/AlertProtocol.java
deleted file mode 100644
index 0330e79..0000000
--- a/src/main/java/org/conscrypt/AlertProtocol.java
+++ /dev/null
@@ -1,281 +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;
-
-/**
- * This class encapsulates the functionality of Alert Protocol.
- * Constant values are taken according to the TLS v1 specification
- * (http://www.ietf.org/rfc/rfc2246.txt), p 7.2.
- */
-public class AlertProtocol {
-
- // ------------------------ AlertLevel codes --------------------------
- /**
- * Defines the severity of alert as warning
- */
- protected static final byte WARNING = 1;
- /**
- * Defines the severity of alert as fatal
- */
- protected static final byte FATAL = 2;
-
- // --------------------- AlertDescription codes -----------------------
- /**
- * Defines the description code of the close_notify alert
- */
- protected static final byte CLOSE_NOTIFY = 0;
- /**
- * Defines the description code of the unexpected_message alert
- */
- protected static final byte UNEXPECTED_MESSAGE = 10;
- /**
- * Defines the description code of the bad_record_mac alert
- */
- protected static final byte BAD_RECORD_MAC = 20;
- /**
- * Defines the description code of the decryption_failed alert
- */
- protected static final byte DECRYPTION_FAILED = 21;
- /**
- * Defines the description code of the record_overflow alert
- */
- protected static final byte RECORD_OVERFLOW = 22;
- /**
- * Defines the description code of the decompression_failure alert
- */
- protected static final byte DECOMPRESSION_FAILURE = 30;
- /**
- * Defines the description code of the handshake_failure alert
- */
- protected static final byte HANDSHAKE_FAILURE = 40;
- /**
- * Defines the description code of the bad_certificate alert
- */
- protected static final byte BAD_CERTIFICATE = 42;
- /**
- * Defines the description code of the unsupported_certificate alert
- */
- protected static final byte UNSUPPORTED_CERTIFICATE = 43;
- /**
- * Defines the description code of the certificate_revoked alert
- */
- protected static final byte CERTIFICATE_REVOKED = 44;
- /**
- * Defines the description code of the certificate_expired alert
- */
- protected static final byte CERTIFICATE_EXPIRED = 45;
- /**
- * Defines the description code of the certificate_unknown alert
- */
- protected static final byte CERTIFICATE_UNKNOWN = 46;
- /**
- * Defines the description code of the illegal_parameter alert
- */
- protected static final byte ILLEGAL_PARAMETER = 47;
- /**
- * Defines the description code of the unknown_ca alert
- */
- protected static final byte UNKNOWN_CA = 48;
- /**
- * Defines the description code of the access_denied alert
- */
- protected static final byte ACCESS_DENIED = 49;
- /**
- * Defines the description code of the decode_error alert
- */
- protected static final byte DECODE_ERROR = 50;
- /**
- * Defines the description code of the decrypt_error alert
- */
- protected static final byte DECRYPT_ERROR = 51;
- /**
- * Defines the description code of the export_restriction alert
- */
- protected static final byte EXPORT_RESTRICTION = 60;
- /**
- * Defines the description code of the protocol_version alert
- */
- protected static final byte PROTOCOL_VERSION = 70;
- /**
- * Defines the description code of the insufficient_security alert
- */
- protected static final byte INSUFFICIENT_SECURITY = 71;
- /**
- * Defines the description code of the internal_error alert
- */
- protected static final byte INTERNAL_ERROR = 80;
- /**
- * Defines the description code of the user_canceled alert
- */
- protected static final byte USER_CANCELED = 90;
- /**
- * Defines the description code of the no_renegotiation alert
- */
- protected static final byte NO_RENEGOTIATION = 100;
- // holds level and description codes
- private final byte[] alert = new byte[2];
- // record protocol to be used to wrap the alerts
- private SSLRecordProtocol recordProtocol;
-
- private Logger.Stream logger = Logger.getStream("alert");
-
- /**
- * Creates the instance of AlertProtocol.
- * Note that class is not ready to work without providing of
- * record protocol
- * @see #setRecordProtocol
- */
- protected AlertProtocol() {}
-
- /**
- * Sets up the record protocol to be used by this allert protocol.
- */
- protected void setRecordProtocol(SSLRecordProtocol recordProtocol) {
- this.recordProtocol = recordProtocol;
- }
-
- /**
- * Reports an alert to be sent/received by transport.
- * This method is usually called during processing
- * of the income TSL record: if it contains alert message from another
- * peer, or if warning alert occured during the processing of the
- * message and this warning should be sent to another peer.
- * @param level alert level code
- * @param description alert description code
- */
- protected void alert(byte level, byte description) {
- if (logger != null) {
- logger.println("Alert.alert: "+level+" "+description);
- }
- this.alert[0] = level;
- this.alert[1] = description;
- }
-
- /**
- * Returns the description code of alert or -100 if there
- * is no alert.
- */
- protected byte getDescriptionCode() {
- return (alert[0] != 0) ? alert[1] : -100;
- }
-
- /**
- * Resets the protocol to be in "no alert" state.
- * This method shoud be called after processing of the reported alert.
- */
- protected void setProcessed() {
- // free the info about alert
- if (logger != null) {
- logger.println("Alert.setProcessed");
- }
- this.alert[0] = 0;
- }
-
- /**
- * Checks if any alert has occured.
- */
- protected boolean hasAlert() {
- return (alert[0] != 0);
- }
-
- /**
- * Checks if occured alert is fatal alert.
- */
- protected boolean isFatalAlert() {
- return (alert[0] == 2);
- }
-
- /**
- * Returns the string representation of occured alert.
- * If no alert has occured null is returned.
- */
- protected String getAlertDescription() {
- switch (alert[1]) {
- case CLOSE_NOTIFY:
- return "close_notify";
- case UNEXPECTED_MESSAGE:
- return "unexpected_message";
- case BAD_RECORD_MAC:
- return "bad_record_mac";
- case DECRYPTION_FAILED:
- return "decryption_failed";
- case RECORD_OVERFLOW:
- return "record_overflow";
- case DECOMPRESSION_FAILURE:
- return "decompression_failure";
- case HANDSHAKE_FAILURE:
- return "handshake_failure";
- case BAD_CERTIFICATE:
- return "bad_certificate";
- case UNSUPPORTED_CERTIFICATE:
- return "unsupported_certificate";
- case CERTIFICATE_REVOKED:
- return "certificate_revoked";
- case CERTIFICATE_EXPIRED:
- return "certificate_expired";
- case CERTIFICATE_UNKNOWN:
- return "certificate_unknown";
- case ILLEGAL_PARAMETER:
- return "illegal_parameter";
- case UNKNOWN_CA:
- return "unknown_ca";
- case ACCESS_DENIED:
- return "access_denied";
- case DECODE_ERROR:
- return "decode_error";
- case DECRYPT_ERROR:
- return "decrypt_error";
- case EXPORT_RESTRICTION:
- return "export_restriction";
- case PROTOCOL_VERSION:
- return "protocol_version";
- case INSUFFICIENT_SECURITY:
- return "insufficient_security";
- case INTERNAL_ERROR:
- return "internal_error";
- case USER_CANCELED:
- return "user_canceled";
- case NO_RENEGOTIATION:
- return "no_renegotiation";
- }
- return null;
- }
-
- /**
- * Returns the record with reported alert message.
- * The returned array of bytes is ready to be sent to another peer.
- * Note, that this method does not automatically set the state of alert
- * protocol in "no alert" state, so after wrapping the method setProcessed
- * should be called.
- */
- protected byte[] wrap() {
- byte[] res = recordProtocol.wrap(ContentType.ALERT, alert, 0, 2);
- return res;
- }
-
- /**
- * Shutdown the protocol. It will be impossible to use the instance
- * after the calling of this method.
- */
- protected void shutdown() {
- alert[0] = 0;
- alert[1] = 0;
- recordProtocol = null;
- }
-}
-
diff --git a/src/main/java/org/conscrypt/Appendable.java b/src/main/java/org/conscrypt/Appendable.java
deleted file mode 100644
index d22c5a8..0000000
--- a/src/main/java/org/conscrypt/Appendable.java
+++ /dev/null
@@ -1,33 +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;
-
-/**
- * This interface represents the ability of the input stream related classes to provide additional
- * data to be read.
- */
-public interface Appendable {
-
- /**
- * Provides the additional data to be read.
- *
- * @param src the source data to be appended.
- */
- public void append(byte[] src);
-
-}
diff --git a/src/main/java/org/conscrypt/CertificateMessage.java b/src/main/java/org/conscrypt/CertificateMessage.java
deleted file mode 100644
index 0c0e092..0000000
--- a/src/main/java/org/conscrypt/CertificateMessage.java
+++ /dev/null
@@ -1,166 +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.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-
-/**
- * Represents server/client certificate message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS
- * 1.0 spec., 7.4.2. Server certificate; 7.4.6. Client certificate</a>
- *
- */
-public class CertificateMessage extends Message {
-
- /**
- * Certificates
- */
- X509Certificate[] certs;
-
- /**
- * Certificates in encoded form
- */
- byte[][] encoded_certs;
-
- /**
- * Creates inbound message
- *
- * @param in
- * @param length
- * @throws IOException
- */
- public CertificateMessage(HandshakeIODataStream in, int length) throws IOException {
- int l = in.readUint24(); // total_length
- if (l == 0) { // message contais no certificates
- if (length != 3) { // no more bytes after total_length
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect CertificateMessage");
- }
- certs = new X509Certificate[0];
- encoded_certs = new byte[0][0];
- this.length = 3;
- return;
- }
- CertificateFactory cf;
- try {
- cf = CertificateFactory.getInstance("X509");
- } catch (CertificateException e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
- return;
- }
- ArrayList<X509Certificate> certsList = new ArrayList<X509Certificate>();
- int size = 0;
- int enc_size = 0;
- while (l > 0) {
- size = in.readUint24();
- l -= 3;
- try {
- certsList.add((X509Certificate) cf.generateCertificate(in));
- } catch (CertificateException e) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR", e);
- }
- l -= size;
- enc_size += size;
- }
- certs = certsList.toArray(new X509Certificate[certsList.size()]);
- this.length = 3 + 3 * certs.length + enc_size;
- if (this.length != length) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect CertificateMessage");
- }
- }
-
- /**
- * Creates outbound message
- *
- * @param certs
- */
- public CertificateMessage(X509Certificate[] certs) {
- if (certs == null) {
- this.certs = new X509Certificate[0];
- encoded_certs = new byte[0][0];
- length = 3;
- return;
- }
- this.certs = certs;
- if (encoded_certs == null) {
- encoded_certs = new byte[certs.length][];
- for (int i = 0; i < certs.length; i++) {
- try {
- encoded_certs[i] = certs[i].getEncoded();
- } catch (CertificateEncodingException e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR",
- e);
- }
- }
- }
- length = 3 + 3 * encoded_certs.length;
- for (int i = 0; i < encoded_certs.length; i++) {
- length += encoded_certs[i].length;
- }
- }
-
- /**
- * Sends message
- *
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
-
- int total_length = 0;
- if (encoded_certs == null) {
- encoded_certs = new byte[certs.length][];
- for (int i = 0; i < certs.length; i++) {
- try {
- encoded_certs[i] = certs[i].getEncoded();
- } catch (CertificateEncodingException e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR",
- e);
- }
- }
- }
- total_length = 3 * encoded_certs.length;
- for (int i = 0; i < encoded_certs.length; i++) {
- total_length += encoded_certs[i].length;
- }
- out.writeUint24(total_length);
- for (int i = 0; i < encoded_certs.length; i++) {
- out.writeUint24(encoded_certs[i].length);
- out.write(encoded_certs[i]);
- }
-
- }
-
- public String getAuthType() {
- return certs[0].getPublicKey().getAlgorithm();
- }
-
- /**
- * Returns message type
- */
- @Override
- public int getType() {
- return Handshake.CERTIFICATE;
- }
-
-}
diff --git a/src/main/java/org/conscrypt/CertificateRequest.java b/src/main/java/org/conscrypt/CertificateRequest.java
deleted file mode 100644
index 6d08cc2..0000000
--- a/src/main/java/org/conscrypt/CertificateRequest.java
+++ /dev/null
@@ -1,159 +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.security.cert.X509Certificate;
-import java.util.ArrayList;
-import javax.security.auth.x500.X500Principal;
-import libcore.io.Streams;
-
-/**
- *
- * Represents certificate request message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.4.
- * Certificate request</a>
- */
-public class CertificateRequest extends Message {
-
- /**
- * Requested certificate types
- */
- final byte[] certificate_types;
-
- /**
- * Certificate authorities
- */
- final X500Principal[] certificate_authorities;
-
- /**
- * Requested certificate types as Strings
- * ("RSA", "DSA", "DH_RSA" or "DH_DSA")
- */
- private String[] types;
-
- /**
- * Encoded form of certificate authorities
- */
- private byte[][] encoded_principals;
-
- /**
- * Creates outbound message
- *
- * @param certificate_types
- * @param accepted - array of certificate authority certificates
- */
- public CertificateRequest(byte[] certificate_types,
- X509Certificate[] accepted) {
-
- if (accepted == null) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "CertificateRequest: array of certificate authority certificates is null");
- }
- this.certificate_types = certificate_types;
-
- int totalPrincipalsLength = 0;
- certificate_authorities = new X500Principal[accepted.length];
- encoded_principals = new byte[accepted.length][];
- for (int i = 0; i < accepted.length; i++) {
- certificate_authorities[i] = accepted[i].getIssuerX500Principal();
- encoded_principals[i] = certificate_authorities[i].getEncoded();
- totalPrincipalsLength += encoded_principals[i].length + 2;
- }
-
- length = 3 + certificate_types.length + totalPrincipalsLength;
- }
-
- /**
- * Creates inbound message
- *
- * @param in
- * @param length
- * @throws IOException
- */
- public CertificateRequest(HandshakeIODataStream in, int length) throws IOException {
- int size = in.readUint8();
- certificate_types = new byte[size];
- Streams.readFully(in, certificate_types);
- size = in.readUint16();
- int totalPrincipalsLength = 0;
- int principalLength = 0;
- ArrayList<X500Principal> principals = new ArrayList<X500Principal>();
- while (totalPrincipalsLength < size) {
- principalLength = in.readUint16(); // encoded X500Principal size
- principals.add(new X500Principal(in));
- totalPrincipalsLength += 2;
- totalPrincipalsLength += principalLength;
- }
- certificate_authorities = principals.toArray(new X500Principal[principals.size()]);
- this.length = 3 + certificate_types.length + totalPrincipalsLength;
- if (this.length != length) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect CertificateRequest");
- }
- }
-
- /**
- * Sends message
- *
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
-
- out.writeUint8(certificate_types.length);
- for (int i = 0; i < certificate_types.length; i++) {
- out.write(certificate_types[i]);
- }
- int authoritiesLength = 0;
- for (int i = 0; i < certificate_authorities.length; i++) {
- authoritiesLength += encoded_principals[i].length +2;
- }
- out.writeUint16(authoritiesLength);
- for (int i = 0; i < certificate_authorities.length; i++) {
- out.writeUint16(encoded_principals[i].length);
- out.write(encoded_principals[i]);
- }
- }
-
- /**
- * Returns message type
- */
- @Override
- public int getType() {
- return Handshake.CERTIFICATE_REQUEST;
- }
-
- /**
- * Returns requested certificate types as array of strings
- */
- public String[] getTypesAsString() {
- if (types == null) {
- types = new String[certificate_types.length];
- for (int i = 0; i < types.length; i++) {
- String type = CipherSuite.getClientKeyType(certificate_types[i]);
- if (type == null) {
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect CertificateRequest");
- }
- types[i] = type;
- }
- }
- return types;
- }
-
-}
diff --git a/src/main/java/org/conscrypt/CertificateVerify.java b/src/main/java/org/conscrypt/CertificateVerify.java
deleted file mode 100644
index 8ba394a..0000000
--- a/src/main/java/org/conscrypt/CertificateVerify.java
+++ /dev/null
@@ -1,90 +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;
-
-/**
- * Represents certificate verify message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.8.
- * Certificate verify</a>
- */
-public class CertificateVerify extends Message {
-
- /**
- * Signature
- */
- byte[] signedHash;
-
- /**
- * Creates outbound message
- *
- * @param hash
- */
- public CertificateVerify(byte[] hash) {
- if (hash == null || hash.length == 0) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "INTERNAL ERROR: incorrect certificate verify hash");
- }
- this.signedHash = hash;
- length = hash.length + 2;
- }
-
- /**
- * Creates inbound message
- *
- * @param in
- * @param length
- * @throws IOException
- */
- public CertificateVerify(HandshakeIODataStream in, int length)
- throws IOException {
- if (length == 0) {
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect CertificateVerify");
- } else {
- if (in.readUint16() != length - 2) {
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect CertificateVerify");
- }
- signedHash = in.read(length -2);
- }
- this.length = length;
- }
-
- /**
- * Sends message
- *
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- if (signedHash.length != 0) {
- out.writeUint16(signedHash.length);
- out.write(signedHash);
- }
- }
-
- /**
- * Returns message type
- */
- @Override
- public int getType() {
- return Handshake.CERTIFICATE_VERIFY;
- }
-}
diff --git a/src/main/java/org/conscrypt/CipherSuite.java b/src/main/java/org/conscrypt/CipherSuite.java
deleted file mode 100644
index bd1a671..0000000
--- a/src/main/java/org/conscrypt/CipherSuite.java
+++ /dev/null
@@ -1,1190 +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.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-import javax.crypto.Cipher;
-
-/**
- * Represents Cipher Suite as defined in TLS 1.0 spec.,
- * A.5. The CipherSuite;
- * C. CipherSuite definitions.
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec.</a>
- *
- */
-public class CipherSuite {
-
- /**
- * true if this cipher suite is supported
- */
- boolean supported = true;
-
- /**
- * cipher suite key exchange
- */
- final int keyExchange;
-
- /**
- * algorithm used for authentication ("RSA", "DSA", "DH", null for anonymous)
- */
- final String authType;
-
- /**
- * cipher
- */
- final String cipherName;
-
- /**
- * Cipher information
- */
- final int keyMaterial;
- final int expandedKeyMaterial;
- final int effectiveKeyBytes;
- final int ivSize;
- final private int blockSize;
-
- // cipher suite code
- private final byte[] cipherSuiteCode;
-
- // cipher suite name
- private final String name;
-
- // true if cipher suite is exportable
- private final boolean isExportable;
-
- // Hash algorithm
- final private String hashName;
-
- // MAC algorithm
- final private String hmacName;
-
- // Hash size
- final private int hashSize;
-
- /**
- * Whether this cipher needs BEAST mitigation or not. This enabled for CBC
- * mode ciphers.
- */
- final private boolean needInitialRecordSplit;
-
- /**
- * key exchange values
- */
- static final int KEY_EXCHANGE_RSA = 1;
- static final int KEY_EXCHANGE_RSA_EXPORT = 2;
- static final int KEY_EXCHANGE_DHE_DSS = 3;
- static final int KEY_EXCHANGE_DHE_DSS_EXPORT = 4;
- static final int KEY_EXCHANGE_DHE_RSA = 5;
- static final int KEY_EXCHANGE_DHE_RSA_EXPORT = 6;
- // BEGIN android-removed
- // static final int KEY_EXCHANGE_DH_DSS = 7;
- // static final int KEY_EXCHANGE_DH_RSA = 8;
- // END android-removed
- static final int KEY_EXCHANGE_DH_anon = 9;
- static final int KEY_EXCHANGE_DH_anon_EXPORT = 10;
- // BEGIN android-removed
- // static final int KEY_EXCHANGE_DH_DSS_EXPORT = 11;
- // static final int KEY_EXCHANGE_DH_RSA_EXPORT = 12;
- // END android-removed
- static final int KEY_EXCHANGE_ECDH_ECDSA = 13;
- static final int KEY_EXCHANGE_ECDHE_ECDSA = 14;
- static final int KEY_EXCHANGE_ECDH_RSA = 15;
- static final int KEY_EXCHANGE_ECDHE_RSA = 16;
- static final int KEY_EXCHANGE_ECDH_anon = 17;
-
- /**
- * TLS cipher suite codes
- */
- static final byte[] CODE_SSL_NULL_WITH_NULL_NULL = { 0x00, 0x00 };
- static final byte[] CODE_SSL_RSA_WITH_NULL_MD5 = { 0x00, 0x01 };
- static final byte[] CODE_SSL_RSA_WITH_NULL_SHA = { 0x00, 0x02 };
- static final byte[] CODE_SSL_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x03 };
- static final byte[] CODE_SSL_RSA_WITH_RC4_128_MD5 = { 0x00, 0x04 };
- static final byte[] CODE_SSL_RSA_WITH_RC4_128_SHA = { 0x00, 0x05 };
- static final byte[] CODE_SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00, 0x06 };
- // BEGIN android-removed
- // static final byte[] CODE_TLS_RSA_WITH_IDEA_CBC_SHA = { 0x00, 0x07 };
- // END android-removed
- static final byte[] CODE_SSL_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x08 };
- static final byte[] CODE_SSL_RSA_WITH_DES_CBC_SHA = { 0x00, 0x09 };
- static final byte[] CODE_SSL_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x0A };
- // BEGIN android-removed
- // static final byte[] CODE_SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0B };
- // static final byte[] CODE_SSL_DH_DSS_WITH_DES_CBC_SHA = { 0x00, 0x0C };
- // static final byte[] CODE_SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x0D };
- // static final byte[] CODE_SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0E };
- // static final byte[] CODE_SSL_DH_RSA_WITH_DES_CBC_SHA = { 0x00, 0x0F };
- // static final byte[] CODE_SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x10 };
- // END android-removed
- static final byte[] CODE_SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x11 };
- static final byte[] CODE_SSL_DHE_DSS_WITH_DES_CBC_SHA = { 0x00, 0x12 };
- static final byte[] CODE_SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x13 };
- static final byte[] CODE_SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x14 };
- static final byte[] CODE_SSL_DHE_RSA_WITH_DES_CBC_SHA = { 0x00, 0x15 };
- static final byte[] CODE_SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x16 };
- static final byte[] CODE_SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x17 };
- static final byte[] CODE_SSL_DH_anon_WITH_RC4_128_MD5 = { 0x00, 0x18 };
- static final byte[] CODE_SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x19 };
- static final byte[] CODE_SSL_DH_anon_WITH_DES_CBC_SHA = { 0x00, 0x1A };
- static final byte[] CODE_SSL_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x1B };
-
- // AES Cipher Suites from RFC 3268 - http://www.ietf.org/rfc/rfc3268.txt
- static final byte[] CODE_TLS_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x2F };
- //static final byte[] CODE_TLS_DH_DSS_WITH_AES_128_CBC_SHA = { 0x00, 0x30 };
- //static final byte[] CODE_TLS_DH_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x31 };
- static final byte[] CODE_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = { 0x00, 0x32 };
- static final byte[] CODE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x33 };
- static final byte[] CODE_TLS_DH_anon_WITH_AES_128_CBC_SHA = { 0x00, 0x34 };
- static final byte[] CODE_TLS_RSA_WITH_AES_256_CBC_SHA = { 0x00, 0x35 };
- //static final byte[] CODE_TLS_DH_DSS_WITH_AES_256_CBC_SHA = { 0x00, 0x36 };
- //static final byte[] CODE_TLS_DH_RSA_WITH_AES_256_CBC_SHA = { 0x00, 0x37 };
- static final byte[] CODE_TLS_DHE_DSS_WITH_AES_256_CBC_SHA = { 0x00, 0x38 };
- static final byte[] CODE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = { 0x00, 0x39 };
- static final byte[] CODE_TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A };
-
- // EC Cipher Suites from RFC 4492 - http://www.ietf.org/rfc/rfc4492.txt
- static final byte[] CODE_TLS_ECDH_ECDSA_WITH_NULL_SHA = { (byte) 0xc0, 0x01};
- static final byte[] CODE_TLS_ECDH_ECDSA_WITH_RC4_128_SHA = { (byte) 0xc0, 0x02};
- static final byte[] CODE_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = { (byte) 0xc0, 0x03};
- static final byte[] CODE_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = { (byte) 0xc0, 0x04};
- static final byte[] CODE_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = { (byte) 0xc0, 0x05};
- static final byte[] CODE_TLS_ECDHE_ECDSA_WITH_NULL_SHA = { (byte) 0xc0, 0x06};
- static final byte[] CODE_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = { (byte) 0xc0, 0x07};
- static final byte[] CODE_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = { (byte) 0xc0, 0x08};
- static final byte[] CODE_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = { (byte) 0xc0, 0x09};
- static final byte[] CODE_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = { (byte) 0xc0, 0x0A};
- static final byte[] CODE_TLS_ECDH_RSA_WITH_NULL_SHA = { (byte) 0xc0, 0x0B};
- static final byte[] CODE_TLS_ECDH_RSA_WITH_RC4_128_SHA = { (byte) 0xc0, 0x0C};
- static final byte[] CODE_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = { (byte) 0xc0, 0x0D};
- static final byte[] CODE_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = { (byte) 0xc0, 0x0E};
- static final byte[] CODE_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = { (byte) 0xc0, 0x0F};
- static final byte[] CODE_TLS_ECDHE_RSA_WITH_NULL_SHA = { (byte) 0xc0, 0x10};
- static final byte[] CODE_TLS_ECDHE_RSA_WITH_RC4_128_SHA = { (byte) 0xc0, 0x11};
- static final byte[] CODE_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = { (byte) 0xc0, 0x12};
- static final byte[] CODE_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = { (byte) 0xc0, 0x13};
- static final byte[] CODE_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = { (byte) 0xc0, 0x14};
- static final byte[] CODE_TLS_ECDH_anon_WITH_NULL_SHA = { (byte) 0xc0, 0x15};
- static final byte[] CODE_TLS_ECDH_anon_WITH_RC4_128_SHA = { (byte) 0xc0, 0x16};
- static final byte[] CODE_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = { (byte) 0xc0, 0x17};
- static final byte[] CODE_TLS_ECDH_anon_WITH_AES_128_CBC_SHA = { (byte) 0xc0, 0x18};
- static final byte[] CODE_TLS_ECDH_anon_WITH_AES_256_CBC_SHA = { (byte) 0xc0, 0x19};
-
- static final CipherSuite SSL_NULL_WITH_NULL_NULL = new CipherSuite(
- "SSL_NULL_WITH_NULL_NULL", true, 0, null, null, null,
- CODE_SSL_NULL_WITH_NULL_NULL);
-
- static final CipherSuite SSL_RSA_WITH_NULL_MD5 = new CipherSuite(
- "SSL_RSA_WITH_NULL_MD5", true, KEY_EXCHANGE_RSA, "RSA", null, "MD5",
- CODE_SSL_RSA_WITH_NULL_MD5);
-
- static final CipherSuite SSL_RSA_WITH_NULL_SHA = new CipherSuite(
- "SSL_RSA_WITH_NULL_SHA", true, KEY_EXCHANGE_RSA, "RSA", null, "SHA",
- CODE_SSL_RSA_WITH_NULL_SHA);
-
- static final CipherSuite SSL_RSA_EXPORT_WITH_RC4_40_MD5 = new CipherSuite(
- "SSL_RSA_EXPORT_WITH_RC4_40_MD5", true, KEY_EXCHANGE_RSA_EXPORT,
- "RSA", "RC4_40", "MD5", CODE_SSL_RSA_EXPORT_WITH_RC4_40_MD5);
-
- static final CipherSuite SSL_RSA_WITH_RC4_128_MD5 = new CipherSuite(
- "SSL_RSA_WITH_RC4_128_MD5", false, KEY_EXCHANGE_RSA, "RSA", "RC4_128",
- "MD5", CODE_SSL_RSA_WITH_RC4_128_MD5);
-
- static final CipherSuite SSL_RSA_WITH_RC4_128_SHA = new CipherSuite(
- "SSL_RSA_WITH_RC4_128_SHA", false, KEY_EXCHANGE_RSA, "RSA", "RC4_128",
- "SHA", CODE_SSL_RSA_WITH_RC4_128_SHA);
-
- static final CipherSuite SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = new CipherSuite(
- "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", true, KEY_EXCHANGE_RSA_EXPORT,
- "RSA", "RC2_CBC_40", "MD5", CODE_SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5);
-
- // BEGIN android-removed
- // static final CipherSuite TLS_RSA_WITH_IDEA_CBC_SHA = new CipherSuite(
- // "TLS_RSA_WITH_IDEA_CBC_SHA", false, KEY_EXCHANGE_RSA, "RSA", "IDEA_CBC",
- // "SHA", CODE_TLS_RSA_WITH_IDEA_CBC_SHA);
- // END android-removed
-
- static final CipherSuite SSL_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
- "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", true, KEY_EXCHANGE_RSA_EXPORT,
- "RSA", "DES40_CBC", "SHA", CODE_SSL_RSA_EXPORT_WITH_DES40_CBC_SHA);
-
- static final CipherSuite SSL_RSA_WITH_DES_CBC_SHA = new CipherSuite(
- "SSL_RSA_WITH_DES_CBC_SHA", false, KEY_EXCHANGE_RSA, "RSA", "DES_CBC",
- "SHA", CODE_SSL_RSA_WITH_DES_CBC_SHA);
-
- static final CipherSuite SSL_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
- "SSL_RSA_WITH_3DES_EDE_CBC_SHA", false, KEY_EXCHANGE_RSA,
- "RSA", "3DES_EDE_CBC", "SHA", CODE_SSL_RSA_WITH_3DES_EDE_CBC_SHA);
-
- // BEGIN android-removed
- // static final CipherSuite SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
- // "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", true,
- // KEY_EXCHANGE_DH_DSS_EXPORT, "DH", "DES40_CBC", "SHA",
- // CODE_SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);
- //
- // static final CipherSuite SSL_DH_DSS_WITH_DES_CBC_SHA = new CipherSuite(
- // "SSL_DH_DSS_WITH_DES_CBC_SHA", false, KEY_EXCHANGE_DH_DSS,
- // "DH", "DES_CBC", "SHA", CODE_SSL_DH_DSS_WITH_DES_CBC_SHA);
- //
- // static final CipherSuite SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
- // "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA", false, KEY_EXCHANGE_DH_DSS,
- // "DH", "3DES_EDE_CBC", "SHA", CODE_SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA);
- //
- // static final CipherSuite SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
- // "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", true,
- // KEY_EXCHANGE_DH_RSA_EXPORT, "DH", "DES40_CBC", "SHA",
- // CODE_SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);
- //
- // static final CipherSuite SSL_DH_RSA_WITH_DES_CBC_SHA = new CipherSuite(
- // "SSL_DH_RSA_WITH_DES_CBC_SHA", false, KEY_EXCHANGE_DH_RSA,
- // "DH", "DES_CBC", "SHA", CODE_SSL_DH_RSA_WITH_DES_CBC_SHA);
- //
- // static final CipherSuite SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
- // "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA", false, KEY_EXCHANGE_DH_RSA,
- // "DH", "3DES_EDE_CBC", "SHA", CODE_SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA);
- // END android-removed
-
- static final CipherSuite SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
- "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", true,
- KEY_EXCHANGE_DHE_DSS_EXPORT, "DSA", "DES40_CBC", "SHA",
- CODE_SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
-
- static final CipherSuite SSL_DHE_DSS_WITH_DES_CBC_SHA = new CipherSuite(
- "SSL_DHE_DSS_WITH_DES_CBC_SHA", false, KEY_EXCHANGE_DHE_DSS,
- "DSA", "DES_CBC", "SHA", CODE_SSL_DHE_DSS_WITH_DES_CBC_SHA);
-
- static final CipherSuite SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
- "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", false, KEY_EXCHANGE_DHE_DSS,
- "DSA", "3DES_EDE_CBC", "SHA", CODE_SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
-
- static final CipherSuite SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
- "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", true,
- KEY_EXCHANGE_DHE_RSA_EXPORT, "RSA", "DES40_CBC", "SHA",
- CODE_SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);
-
- static final CipherSuite SSL_DHE_RSA_WITH_DES_CBC_SHA = new CipherSuite(
- "SSL_DHE_RSA_WITH_DES_CBC_SHA", false, KEY_EXCHANGE_DHE_RSA,
- "RSA", "DES_CBC", "SHA", CODE_SSL_DHE_RSA_WITH_DES_CBC_SHA);
-
- static final CipherSuite SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
- "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", false, KEY_EXCHANGE_DHE_RSA,
- "RSA", "3DES_EDE_CBC", "SHA", CODE_SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
-
- static final CipherSuite SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 = new CipherSuite(
- "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", true,
- KEY_EXCHANGE_DH_anon_EXPORT, "DH", "RC4_40", "MD5",
- CODE_SSL_DH_anon_EXPORT_WITH_RC4_40_MD5);
-
- static final CipherSuite SSL_DH_anon_WITH_RC4_128_MD5 = new CipherSuite(
- "SSL_DH_anon_WITH_RC4_128_MD5", false, KEY_EXCHANGE_DH_anon,
- "DH", "RC4_128", "MD5", CODE_SSL_DH_anon_WITH_RC4_128_MD5);
-
- static final CipherSuite SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
- "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", true,
- KEY_EXCHANGE_DH_anon_EXPORT, "DH", "DES40_CBC", "SHA",
- CODE_SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA);
-
- static final CipherSuite SSL_DH_anon_WITH_DES_CBC_SHA = new CipherSuite(
- "SSL_DH_anon_WITH_DES_CBC_SHA", false, KEY_EXCHANGE_DH_anon,
- "DH", "DES_CBC", "SHA", CODE_SSL_DH_anon_WITH_DES_CBC_SHA);
-
- static final CipherSuite SSL_DH_anon_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
- "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", false, KEY_EXCHANGE_DH_anon,
- "DH", "3DES_EDE_CBC", "SHA", CODE_SSL_DH_anon_WITH_3DES_EDE_CBC_SHA);
-
- static final CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_RSA,
- "RSA",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_RSA_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_DHE_DSS,
- "DSA",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_DHE_DSS_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_DHE_RSA,
- "RSA",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_DH_anon_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_DH_anon_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_DH_anon,
- "DH",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_DH_anon_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_RSA_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_RSA_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_RSA,
- "RSA",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_RSA_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_DHE_DSS_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_DHE_DSS,
- "DSA",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_DHE_DSS_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_DHE_RSA_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_DHE_RSA,
- "RSA",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_DH_anon_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_DH_anon_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_DH_anon,
- "DH",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_DH_anon_WITH_AES_256_CBC_SHA);
-
- static final CipherSuite TLS_ECDH_ECDSA_WITH_NULL_SHA
- = new CipherSuite("TLS_ECDH_ECDSA_WITH_NULL_SHA",
- false,
- KEY_EXCHANGE_ECDH_ECDSA,
- "EC",
- null,
- "SHA",
- CODE_TLS_ECDH_ECDSA_WITH_NULL_SHA);
- static final CipherSuite TLS_ECDH_ECDSA_WITH_RC4_128_SHA
- = new CipherSuite("TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
- false,
- KEY_EXCHANGE_ECDH_ECDSA,
- "EC",
- "RC4_128",
- "SHA",
- CODE_TLS_ECDH_ECDSA_WITH_RC4_128_SHA);
- static final CipherSuite TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
- = new CipherSuite("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_ECDSA,
- "EC",
- "3DES_EDE_CBC",
- "SHA",
- CODE_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA);
- static final CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_ECDSA,
- "EC",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_ECDSA,
- "EC",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_ECDHE_ECDSA_WITH_NULL_SHA
- = new CipherSuite("TLS_ECDHE_ECDSA_WITH_NULL_SHA",
- false,
- KEY_EXCHANGE_ECDHE_ECDSA,
- "EC",
- null,
- "SHA",
- CODE_TLS_ECDHE_ECDSA_WITH_NULL_SHA);
- static final CipherSuite TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
- = new CipherSuite("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
- false,
- KEY_EXCHANGE_ECDHE_ECDSA,
- "EC",
- "RC4_128",
- "SHA",
- CODE_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA);
- static final CipherSuite TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
- = new CipherSuite("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDHE_ECDSA,
- "EC",
- "3DES_EDE_CBC",
- "SHA",
- CODE_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
- static final CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDHE_ECDSA,
- "EC",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDHE_ECDSA,
- "EC",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_ECDH_RSA_WITH_NULL_SHA
- = new CipherSuite("TLS_ECDH_RSA_WITH_NULL_SHA",
- false,
- KEY_EXCHANGE_ECDH_RSA,
- "EC",
- null,
- "SHA",
- CODE_TLS_ECDH_RSA_WITH_NULL_SHA);
- static final CipherSuite TLS_ECDH_RSA_WITH_RC4_128_SHA
- = new CipherSuite("TLS_ECDH_RSA_WITH_RC4_128_SHA",
- false,
- KEY_EXCHANGE_ECDH_RSA,
- "EC",
- "RC4_128",
- "SHA",
- CODE_TLS_ECDH_RSA_WITH_RC4_128_SHA);
- static final CipherSuite TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
- = new CipherSuite("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_RSA,
- "EC",
- "3DES_EDE_CBC",
- "SHA",
- CODE_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA);
- static final CipherSuite TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_RSA,
- "EC",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_RSA,
- "EC",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_ECDHE_RSA_WITH_NULL_SHA
- = new CipherSuite("TLS_ECDHE_RSA_WITH_NULL_SHA",
- false,
- KEY_EXCHANGE_ECDHE_RSA,
- "EC",
- null,
- "SHA",
- CODE_TLS_ECDHE_RSA_WITH_NULL_SHA);
- static final CipherSuite TLS_ECDHE_RSA_WITH_RC4_128_SHA
- = new CipherSuite("TLS_ECDHE_RSA_WITH_RC4_128_SHA",
- false,
- KEY_EXCHANGE_ECDHE_RSA,
- "EC",
- "RC4_128",
- "SHA",
- CODE_TLS_ECDHE_RSA_WITH_RC4_128_SHA);
- static final CipherSuite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
- = new CipherSuite("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDHE_RSA,
- "EC",
- "3DES_EDE_CBC",
- "SHA",
- CODE_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
- static final CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDHE_RSA,
- "EC",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDHE_RSA,
- "EC",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
- static final CipherSuite TLS_ECDH_anon_WITH_NULL_SHA
- = new CipherSuite("TLS_ECDH_anon_WITH_NULL_SHA",
- false,
- KEY_EXCHANGE_ECDH_anon,
- "EC",
- null,
- "SHA",
- CODE_TLS_ECDH_anon_WITH_NULL_SHA);
- static final CipherSuite TLS_ECDH_anon_WITH_RC4_128_SHA
- = new CipherSuite("TLS_ECDH_anon_WITH_RC4_128_SHA",
- false,
- KEY_EXCHANGE_ECDH_anon,
- "EC",
- "RC4_128",
- "SHA",
- CODE_TLS_ECDH_anon_WITH_RC4_128_SHA);
- static final CipherSuite TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA
- = new CipherSuite("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_anon,
- "EC",
- "3DES_EDE_CBC",
- "SHA",
- CODE_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA);
- static final CipherSuite TLS_ECDH_anon_WITH_AES_128_CBC_SHA
- = new CipherSuite("TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_anon,
- "EC",
- "AES_128_CBC",
- "SHA",
- CODE_TLS_ECDH_anon_WITH_AES_128_CBC_SHA);
- static final CipherSuite TLS_ECDH_anon_WITH_AES_256_CBC_SHA
- = new CipherSuite("TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
- false,
- KEY_EXCHANGE_ECDH_anon,
- "EC",
- "AES_256_CBC",
- "SHA",
- CODE_TLS_ECDH_anon_WITH_AES_256_CBC_SHA);
-
- // arrays for quick access to cipher suite by code
- private static final CipherSuite[] SUITES_BY_CODE_0x00 = {
- // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
- SSL_NULL_WITH_NULL_NULL, // { 0x00, 0x00 };
- SSL_RSA_WITH_NULL_MD5, // { 0x00, 0x01 };
- SSL_RSA_WITH_NULL_SHA, // { 0x00, 0x02 };
- SSL_RSA_EXPORT_WITH_RC4_40_MD5, // { 0x00, 0x03 };
- SSL_RSA_WITH_RC4_128_MD5, // { 0x00, 0x04 };
- SSL_RSA_WITH_RC4_128_SHA, // { 0x00, 0x05 };
- // BEGIN android-changed
- null, // SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, // { 0x00, 0x06 };
- null, // TLS_RSA_WITH_IDEA_CBC_SHA, // { 0x00, 0x07 };
- // END android-changed
- SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, // { 0x00, 0x08 };
- SSL_RSA_WITH_DES_CBC_SHA, // { 0x00, 0x09 };
- SSL_RSA_WITH_3DES_EDE_CBC_SHA, // { 0x00, 0x0a };
- // BEGIN android-changed
- null, // SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA // { 0x00, 0x0b };
- null, // SSL_DH_DSS_WITH_DES_CBC_SHA, // { 0x00, 0x0c };
- null, // SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA, // { 0x00, 0x0d };
- null, // SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, // { 0x00, 0x0e };
- null, // SSL_DH_RSA_WITH_DES_CBC_SHA, // { 0x00, 0x0f };
- null, // SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA, // { 0x00, 0x10 };
- // END android-changed
- SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, // { 0x00, 0x11 };
- SSL_DHE_DSS_WITH_DES_CBC_SHA, // { 0x00, 0x12 };
- SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, // { 0x00, 0x13 };
- SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, // { 0x00, 0x14 };
- SSL_DHE_RSA_WITH_DES_CBC_SHA, // { 0x00, 0x15 };
- SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, // { 0x00, 0x16 };
- SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, // { 0x00, 0x17 };
- SSL_DH_anon_WITH_RC4_128_MD5, // { 0x00, 0x18 };
- SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, // { 0x00, 0x19 };
- SSL_DH_anon_WITH_DES_CBC_SHA, // { 0x00, 0x1A };
- SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, // { 0x00, 0x1B };
- // BEGIN android-added
- null, // SSL_FORTEZZA_KEA_WITH_NULL_SHA // { 0x00, 0x1C };
- null, // SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA // { 0x00, 0x1D };
- null, // TLS_KRB5_WITH_DES_CBC_SHA // { 0x00, 0x1E };
- null, // TLS_KRB5_WITH_3DES_EDE_CBC_SHA // { 0x00, 0x1F };
- null, // TLS_KRB5_WITH_RC4_128_SHA // { 0x00, 0x20 };
- null, // TLS_KRB5_WITH_IDEA_CBC_SHA // { 0x00, 0x21 };
- null, // TLS_KRB5_WITH_DES_CBC_MD5 // { 0x00, 0x22 };
- null, // TLS_KRB5_WITH_3DES_EDE_CBC_MD5 // { 0x00, 0x23 };
- null, // TLS_KRB5_WITH_RC4_128_MD5 // { 0x00, 0x24 };
- null, // TLS_KRB5_WITH_IDEA_CBC_MD5 // { 0x00, 0x25 };
- null, // TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA // { 0x00, 0x26 };
- null, // TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA // { 0x00, 0x27 };
- null, // TLS_KRB5_EXPORT_WITH_RC4_40_SHA // { 0x00, 0x28 };
- null, // TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 // { 0x00, 0x29 };
- null, // TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 // { 0x00, 0x2A };
- null, // TLS_KRB5_EXPORT_WITH_RC4_40_MD5 // { 0x00, 0x2B };
- null, // TLS_PSK_WITH_NULL_SHA // { 0x00, 0x2C };
- null, // TLS_DHE_PSK_WITH_NULL_SHA // { 0x00, 0x2D };
- null, // TLS_RSA_PSK_WITH_NULL_SHA // { 0x00, 0x2E };
- TLS_RSA_WITH_AES_128_CBC_SHA, // { 0x00, 0x2F };
- null, // TLS_DH_DSS_WITH_AES_128_CBC_SHA // { 0x00, 0x30 };
- null, // TLS_DH_RSA_WITH_AES_128_CBC_SHA // { 0x00, 0x31 };
- TLS_DHE_DSS_WITH_AES_128_CBC_SHA, // { 0x00, 0x32 };
- TLS_DHE_RSA_WITH_AES_128_CBC_SHA, // { 0x00, 0x33 };
- TLS_DH_anon_WITH_AES_128_CBC_SHA, // { 0x00, 0x34 };
- TLS_RSA_WITH_AES_256_CBC_SHA, // { 0x00, 0x35 };
- null, // TLS_DH_DSS_WITH_AES_256_CBC_SHA, // { 0x00, 0x36 };
- null, // TLS_DH_RSA_WITH_AES_256_CBC_SHA, // { 0x00, 0x37 };
- TLS_DHE_DSS_WITH_AES_256_CBC_SHA, // { 0x00, 0x38 };
- TLS_DHE_RSA_WITH_AES_256_CBC_SHA, // { 0x00, 0x39 };
- TLS_DH_anon_WITH_AES_256_CBC_SHA, // { 0x00, 0x3A };
- // END android-added
- };
- private static final CipherSuite[] SUITES_BY_CODE_0xc0 = {
- null, // { 0xc0, 0x00};
- TLS_ECDH_ECDSA_WITH_NULL_SHA, // { 0xc0, 0x01};
- TLS_ECDH_ECDSA_WITH_RC4_128_SHA, // { 0xc0, 0x02};
- TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x03};
- TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, // { 0xc0, 0x04};
- TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, // { 0xc0, 0x05};
- TLS_ECDHE_ECDSA_WITH_NULL_SHA, // { 0xc0, 0x06};
- TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, // { 0xc0, 0x07};
- TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x08};
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // { 0xc0, 0x09};
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // { 0xc0, 0x0A};
- TLS_ECDH_RSA_WITH_NULL_SHA, // { 0xc0, 0x0B};
- TLS_ECDH_RSA_WITH_RC4_128_SHA, // { 0xc0, 0x0C};
- TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x0D};
- TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, // { 0xc0, 0x0E};
- TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, // { 0xc0, 0x0F};
- TLS_ECDHE_RSA_WITH_NULL_SHA, // { 0xc0, 0x10};
- TLS_ECDHE_RSA_WITH_RC4_128_SHA, // { 0xc0, 0x11};
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x12};
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // { 0xc0, 0x13};
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // { 0xc0, 0x14};
- TLS_ECDH_anon_WITH_NULL_SHA, // { 0xc0, 0x15};
- TLS_ECDH_anon_WITH_RC4_128_SHA, // { 0xc0, 0x16};
- TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x17};
- TLS_ECDH_anon_WITH_AES_128_CBC_SHA, // { 0xc0, 0x18};
- TLS_ECDH_anon_WITH_AES_256_CBC_SHA, // { 0xc0, 0x19};
- // TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x1A};
- // TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x1B};
- // TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x1C};
- // TLS_SRP_SHA_WITH_AES_128_CBC_SHA, // { 0xc0, 0x1D};
- // TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, // { 0xc0, 0x1E};
- // TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, // { 0xc0, 0x1F};
- // TLS_SRP_SHA_WITH_AES_256_CBC_SHA, // { 0xc0, 0x20};
- // TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, // { 0xc0, 0x21};
- // TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, // { 0xc0, 0x22};
- // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // { 0xc0, 0x23};
- // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, // { 0xc0, 0x24};
- // TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, // { 0xc0, 0x25};
- // TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, // { 0xc0, 0x26};
- // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, // { 0xc0, 0x27};
- // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, // { 0xc0, 0x28};
- // TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, // { 0xc0, 0x29};
- // TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, // { 0xc0, 0x2A};
- // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // { 0xc0, 0x2B};
- // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, // { 0xc0, 0x2C};
- // TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, // { 0xc0, 0x2D};
- // TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, // { 0xc0, 0x2E};
- // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // { 0xc0, 0x2F};
- // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // { 0xc0, 0x30};
- // TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, // { 0xc0, 0x31};
- // TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, // { 0xc0, 0x32};
- // TLS_ECDHE_PSK_WITH_RC4_128_SHA, // { 0xc0, 0x33};
- // TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, // { 0xc0, 0x34};
- // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, // { 0xc0, 0x35};
- // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, // { 0xc0, 0x36};
- // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, // { 0xc0, 0x37};
- // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, // { 0xc0, 0x38};
- // TLS_ECDHE_PSK_WITH_NULL_SHA, // { 0xc0, 0x39};
- // TLS_ECDHE_PSK_WITH_NULL_SHA256, // { 0xc0, 0x3A};
- // TLS_ECDHE_PSK_WITH_NULL_SHA384, // { 0xc0, 0x3B};
- };
-
- // hash for quick access to cipher suite by name
- private static final Hashtable<String, CipherSuite> SUITES_BY_NAME;
-
- /**
- * array of supported cipher suites.
- * Set of supported suites is defined at the moment provider's start
- */
- // TODO Dynamically supported suites: new providers may be dynamically
- // added/removed and the set of supported suites may be changed
- static final CipherSuite[] SUPPORTED_CIPHER_SUITES;
-
- /**
- * array of supported cipher suites names
- */
- static final String[] SUPPORTED_CIPHER_SUITE_NAMES;
-
- /**
- * default cipher suites
- */
- static final CipherSuite[] DEFAULT_CIPHER_SUITES;
-
- static {
- SUITES_BY_NAME = new Hashtable<String, CipherSuite>();
- int count_0x00 = registerCipherSuitesByCode(SUITES_BY_CODE_0x00);
- int count_0xc0 = registerCipherSuitesByCode(SUITES_BY_CODE_0xc0);
- int count = count_0x00 + count_0xc0;
- SUPPORTED_CIPHER_SUITES = new CipherSuite[count];
- SUPPORTED_CIPHER_SUITE_NAMES = new String[count];
- registerSupportedCipherSuites(0, SUITES_BY_CODE_0x00);
- registerSupportedCipherSuites(count_0x00, SUITES_BY_CODE_0xc0);
-
- // Make DEFAULT_CIPHER_SUITES the sublist of NativeCrypto's default cipher suites supported
- // by this SSLEngine implementation.
- List<CipherSuite> defaultCipherSuitesList = new ArrayList<CipherSuite>();
- for (String cipherSuiteName : NativeCrypto.getDefaultCipherSuites()) {
- CipherSuite cipherSuite = CipherSuite.getByName(cipherSuiteName);
- if ((cipherSuite != null) && (cipherSuite.supported)) {
- defaultCipherSuitesList.add(cipherSuite);
- }
- }
- DEFAULT_CIPHER_SUITES = defaultCipherSuitesList.toArray(
- new CipherSuite[defaultCipherSuitesList.size()]);
- }
- private static int registerCipherSuitesByCode(CipherSuite[] cipherSuites) {
- int count = 0;
- for (int i = 0; i < cipherSuites.length; i++) {
- if (cipherSuites[i] == SSL_NULL_WITH_NULL_NULL) {
- continue;
- }
- if (cipherSuites[i] == null) {
- continue;
- }
- SUITES_BY_NAME.put(cipherSuites[i].getName(), cipherSuites[i]);
- if (cipherSuites[i].supported) {
- count++;
- }
- }
- return count;
- }
- private static void registerSupportedCipherSuites(int offset, CipherSuite[] cipherSuites) {
- int count = offset;
- for (int i = 0; i < cipherSuites.length; i++) {
- if (cipherSuites[i] == SSL_NULL_WITH_NULL_NULL) {
- continue;
- }
- if (cipherSuites[i] == null) {
- continue;
- }
- if (cipherSuites[i].supported) {
- SUPPORTED_CIPHER_SUITES[count] = cipherSuites[i];
- SUPPORTED_CIPHER_SUITE_NAMES[count] = SUPPORTED_CIPHER_SUITES[count].getName();
- count++;
- }
- }
- }
-
- /**
- * Returns CipherSuite by name
- */
- public static CipherSuite getByName(String name) {
- return SUITES_BY_NAME.get(name);
- }
-
- /**
- * Returns CipherSuite based on TLS CipherSuite code
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., A.5. The CipherSuite</a>
- */
- public static CipherSuite getByCode(byte b1, byte b2) {
- int i1 = b1 & 0xff;
- int i2 = b2 & 0xff;
- CipherSuite cs = getCipherSuiteByCode(0, i1, i2);
- if (cs != null) {
- return cs;
- }
- return new CipherSuite("UNKNOWN_" + i1 + "_" + i2, false, 0, null,
- null, null, new byte[] { b1, b2 });
- }
-
- /**
- * Returns CipherSuite based on V2CipherSpec code
- * as described in TLS 1.0 spec., E. Backward Compatibility With SSL
- */
- public static CipherSuite getByCode(byte b1, byte b2, byte b3) {
- int i1 = b1 & 0xff;
- int i2 = b2 & 0xff;
- int i3 = b3 & 0xff;
- CipherSuite cs = getCipherSuiteByCode(i1, i2, i3);
- if (cs != null) {
- return cs;
- }
- return new CipherSuite("UNKNOWN_" + i1 + "_" + i2 + "_" + i3, false, 0,
- null, null, null, new byte[] { b1, b2, b3 });
- }
-
- private static CipherSuite getCipherSuiteByCode(int i1, int i2, int i3) {
- CipherSuite[] cipherSuites;
- if (i1 == 0x00 && i2 == 0x00) {
- cipherSuites = SUITES_BY_CODE_0x00;
- } else if (i1 == 0x00 && i2 == 0xc0) {
- cipherSuites = SUITES_BY_CODE_0xc0;
- } else {
- return null;
- }
- if (i3 >= cipherSuites.length) {
- return null;
- }
- return cipherSuites[i3];
- }
-
- /**
- * Creates CipherSuite
- */
- private CipherSuite(String name, boolean isExportable, int keyExchange,
- String authType, String cipherName, String hash, byte[] code) {
- this.name = name;
- this.keyExchange = keyExchange;
- this.authType = authType;
- this.isExportable = isExportable;
- if (cipherName == null) {
- this.cipherName = null;
- keyMaterial = 0;
- expandedKeyMaterial = 0;
- effectiveKeyBytes = 0;
- ivSize = 0;
- blockSize = 0;
- needInitialRecordSplit = false;
- // BEGIN android-removed
- // } else if ("IDEA_CBC".equals(cipherName)) {
- // this.cipherName = "IDEA/CBC/NoPadding";
- // keyMaterial = 16;
- // expandedKeyMaterial = 16;
- // effectiveKeyBytes = 16;
- // ivSize = 8;
- // blockSize = 8;
- // } else if ("RC2_CBC_40".equals(cipherName)) {
- // this.cipherName = "RC2/CBC/NoPadding";
- // keyMaterial = 5;
- // expandedKeyMaterial = 16;
- // effectiveKeyBytes = 5;
- // ivSize = 8;
- // blockSize = 8;
- // END android-removed
- } else if ("RC4_40".equals(cipherName)) {
- this.cipherName = "RC4";
- keyMaterial = 5;
- expandedKeyMaterial = 16;
- effectiveKeyBytes = 5;
- ivSize = 0;
- blockSize = 0;
- needInitialRecordSplit = false;
- } else if ("RC4_128".equals(cipherName)) {
- this.cipherName = "RC4";
- keyMaterial = 16;
- expandedKeyMaterial = 16;
- effectiveKeyBytes = 16;
- ivSize = 0;
- blockSize = 0;
- needInitialRecordSplit = false;
- } else if ("DES40_CBC".equals(cipherName)) {
- this.cipherName = "DES/CBC/NoPadding";
- keyMaterial = 5;
- expandedKeyMaterial = 8;
- effectiveKeyBytes = 5;
- ivSize = 8;
- blockSize = 8;
- needInitialRecordSplit = true;
- } else if ("DES_CBC".equals(cipherName)) {
- this.cipherName = "DES/CBC/NoPadding";
- keyMaterial = 8;
- expandedKeyMaterial = 8;
- effectiveKeyBytes = 7;
- ivSize = 8;
- blockSize = 8;
- needInitialRecordSplit = true;
- } else if ("3DES_EDE_CBC".equals(cipherName)) {
- this.cipherName = "DESede/CBC/NoPadding";
- keyMaterial = 24;
- expandedKeyMaterial = 24;
- effectiveKeyBytes = 24;
- ivSize = 8;
- blockSize = 8;
- needInitialRecordSplit = true;
- } else if ("AES_128_CBC".equals(cipherName)) {
- this.cipherName = "AES/CBC/NoPadding";
- keyMaterial = 16;
- expandedKeyMaterial = 16;
- effectiveKeyBytes = 16;
- ivSize = 16;
- blockSize = 16;
- needInitialRecordSplit = true;
- } else if ("AES_256_CBC".equals(cipherName)) {
- this.cipherName = "AES/CBC/NoPadding";
- keyMaterial = 32;
- expandedKeyMaterial = 32;
- effectiveKeyBytes = 32;
- ivSize = 16;
- blockSize = 16;
- needInitialRecordSplit = true;
- } else {
- this.cipherName = cipherName;
- keyMaterial = 0;
- expandedKeyMaterial = 0;
- effectiveKeyBytes = 0;
- ivSize = 0;
- blockSize = 0;
- needInitialRecordSplit = false;
- }
-
- if ("MD5".equals(hash)) {
- this.hmacName = "HmacMD5";
- this.hashName = "MD5";
- hashSize = 16;
- } else if ("SHA".equals(hash)) {
- this.hmacName = "HmacSHA1";
- this.hashName = "SHA-1";
- hashSize = 20;
- } else {
- this.hmacName = null;
- this.hashName = null;
- hashSize = 0;
- }
-
- cipherSuiteCode = code;
-
- if (this.cipherName != null) {
- try {
- Cipher.getInstance(this.cipherName);
- } catch (GeneralSecurityException e) {
- supported = false;
- }
- }
-
- // We define the Elliptic Curve cipher suites for use with
- // code shared by OpenSSL, but they are not supported by
- // SSLEngine or SSLSocket's built with SSLEngine.
- if (this.name.startsWith("TLS_EC")) {
- supported = false;
- }
- }
-
- /**
- * Returns true if cipher suite is anonymous
- */
- public boolean isAnonymous() {
- if (keyExchange == KEY_EXCHANGE_DH_anon
- || keyExchange == KEY_EXCHANGE_DH_anon_EXPORT
- || keyExchange == KEY_EXCHANGE_ECDH_anon) {
- return true;
- }
- return false;
- }
-
- /**
- * Returns array of supported CipherSuites
- */
- public static CipherSuite[] getSupported() {
- return SUPPORTED_CIPHER_SUITES;
- }
-
- /**
- * Returns array of supported cipher suites names
- */
- public static String[] getSupportedCipherSuiteNames() {
- return SUPPORTED_CIPHER_SUITE_NAMES.clone();
- }
-
- /**
- * Returns cipher suite name
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns cipher suite code as byte array
- */
- public byte[] toBytes() {
- return cipherSuiteCode;
- }
-
- /**
- * Returns cipher suite description
- */
- @Override
- public String toString() {
- return name + ": " + cipherSuiteCode[0] + " " + cipherSuiteCode[1];
- }
-
- /**
- * Returns cipher algorithm name
- */
- public String getBulkEncryptionAlgorithm() {
- return cipherName;
- }
-
- /**
- * Returns cipher block size
- */
- public int getBlockSize() {
- return blockSize;
- }
-
- /**
- * Returns MAC algorithm name
- */
- public String getHmacName() {
- return hmacName;
- }
-
- /**
- * Returns hash algorithm name
- */
- public String getHashName() {
- return hashName;
- }
-
- /**
- * Returns hash size
- */
- public int getMACLength() {
- return hashSize;
- }
-
- /**
- * Indicates whether this cipher suite is exportable
- */
- public boolean isExportable() {
- return isExportable;
- }
-
- /**
- * Indicates whether this cipher suite needs the initial record split to
- * mitigate the BEAST attack.
- */
- public boolean isInitialRecordSplit() {
- return needInitialRecordSplit;
- }
-
- static final String KEY_TYPE_RSA = "RSA";
- static final String KEY_TYPE_DSA = "DSA";
- static final String KEY_TYPE_DH_RSA = "DH_RSA";
- static final String KEY_TYPE_DH_DSA = "DH_DSA";
- static final String KEY_TYPE_EC = "EC";
- static final String KEY_TYPE_EC_EC = "EC_EC";
- static final String KEY_TYPE_EC_RSA = "EC_RSA";
-
- /**
- * Returns key type constant suitable for calling
- * X509KeyManager.chooseServerAlias or
- * X509ExtendedKeyManager.chooseEngineServerAlias.
- */
- public String getServerKeyType() {
- switch (keyExchange) {
- case KEY_EXCHANGE_DHE_RSA:
- case KEY_EXCHANGE_DHE_RSA_EXPORT:
- case KEY_EXCHANGE_ECDHE_RSA:
- case KEY_EXCHANGE_RSA:
- case KEY_EXCHANGE_RSA_EXPORT:
- return KEY_TYPE_RSA;
- case KEY_EXCHANGE_DHE_DSS:
- case KEY_EXCHANGE_DHE_DSS_EXPORT:
- return KEY_TYPE_DSA;
- case KEY_EXCHANGE_ECDH_ECDSA:
- case KEY_EXCHANGE_ECDHE_ECDSA:
- return KEY_TYPE_EC_EC;
- case KEY_EXCHANGE_ECDH_RSA:
- return KEY_TYPE_EC_RSA;
- case KEY_EXCHANGE_DH_anon:
- case KEY_EXCHANGE_DH_anon_EXPORT:
- case KEY_EXCHANGE_ECDH_anon:
- return null;
- default:
- throw new IllegalStateException("Unknown key type for key exchange " + keyExchange);
- }
- }
-
- /**
- * Client certificate types as defined in
- * TLS 1.0 spec., 7.4.4. Certificate request.
- * EC constants from RFC 4492.
- * Names match openssl constants.
- */
- static final byte TLS_CT_RSA_SIGN = 1;
- static final byte TLS_CT_DSS_SIGN = 2;
- static final byte TLS_CT_RSA_FIXED_DH = 3;
- static final byte TLS_CT_DSS_FIXED_DH = 4;
- static final byte TLS_CT_ECDSA_SIGN = 64;
- static final byte TLS_CT_RSA_FIXED_ECDH = 65;
- static final byte TLS_CT_ECDSA_FIXED_ECDH = 66;
-
- /**
- * Similar to getServerKeyType, but returns value given TLS
- * ClientCertificateType byte values from a CertificateRequest
- * message for use with X509KeyManager.chooseClientAlias or
- * X509ExtendedKeyManager.chooseEngineClientAlias.
- */
- public static String getClientKeyType(byte keyType) {
- // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
- switch (keyType) {
- case TLS_CT_RSA_SIGN:
- return KEY_TYPE_RSA; // RFC rsa_sign
- case TLS_CT_DSS_SIGN:
- return KEY_TYPE_DSA; // RFC dss_sign
- case TLS_CT_RSA_FIXED_DH:
- return KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
- case TLS_CT_DSS_FIXED_DH:
- return KEY_TYPE_DH_DSA; // RFC dss_fixed_dh
- case TLS_CT_ECDSA_SIGN:
- return KEY_TYPE_EC; // RFC ecdsa_sign
- case TLS_CT_RSA_FIXED_ECDH:
- return KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
- case TLS_CT_ECDSA_FIXED_ECDH:
- return KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh
- default:
- return null;
- }
- }
-
- private static final String AUTH_TYPE_RSA = "RSA";
- private static final String AUTH_TYPE_RSA_EXPORT = "RSA_EXPORT";
- private static final String AUTH_TYPE_DHE_DSS = "DHE_DSS";
- private static final String AUTH_TYPE_DHE_RSA = "DHE_RSA";
- private static final String AUTH_TYPE_DH_DSS = "DH_DSS";
- private static final String AUTH_TYPE_DH_RSA = "DH_RSA";
- private static final String AUTH_TYPE_ECDH_ECDSA = "ECDH_ECDSA";
- private static final String AUTH_TYPE_ECDH_RSA = "ECDH_RSA";
- private static final String AUTH_TYPE_ECDHE_ECDSA = "ECDHE_ECDSA";
- private static final String AUTH_TYPE_ECDHE_RSA = "ECDHE_RSA";
-
- /**
- * Returns auth type constant suitable for calling X509TrustManager.checkServerTrusted.
- */
- public String getAuthType(boolean emphemeral) {
- switch (keyExchange) {
- case KEY_EXCHANGE_RSA:
- return AUTH_TYPE_RSA;
- case KEY_EXCHANGE_RSA_EXPORT:
- return emphemeral ? AUTH_TYPE_RSA_EXPORT : AUTH_TYPE_RSA;
- case KEY_EXCHANGE_DHE_DSS:
- case KEY_EXCHANGE_DHE_DSS_EXPORT:
- return AUTH_TYPE_DHE_DSS;
- case KEY_EXCHANGE_DHE_RSA:
- case KEY_EXCHANGE_DHE_RSA_EXPORT:
- return AUTH_TYPE_DHE_RSA;
- case KEY_EXCHANGE_ECDH_ECDSA:
- return AUTH_TYPE_ECDH_ECDSA;
- case KEY_EXCHANGE_ECDHE_ECDSA:
- return AUTH_TYPE_ECDHE_ECDSA;
- case KEY_EXCHANGE_ECDH_RSA:
- return AUTH_TYPE_ECDH_RSA;
- case KEY_EXCHANGE_ECDHE_RSA:
- return AUTH_TYPE_ECDHE_RSA;
- case KEY_EXCHANGE_DH_anon:
- case KEY_EXCHANGE_DH_anon_EXPORT:
- case KEY_EXCHANGE_ECDH_anon:
- return null;
- default:
- throw new IllegalStateException("Unknown auth type for key exchange " + keyExchange);
- }
- }
-}
diff --git a/src/main/java/org/conscrypt/ClientHandshakeImpl.java b/src/main/java/org/conscrypt/ClientHandshakeImpl.java
deleted file mode 100644
index 17815ea..0000000
--- a/src/main/java/org/conscrypt/ClientHandshakeImpl.java
+++ /dev/null
@@ -1,579 +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.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import javax.crypto.Cipher;
-import javax.crypto.KeyAgreement;
-import javax.crypto.interfaces.DHKey;
-import javax.crypto.interfaces.DHPublicKey;
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.DHPublicKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509KeyManager;
-import javax.net.ssl.X509TrustManager;
-import javax.security.auth.x500.X500Principal;
-
-/**
- * Client side handshake protocol implementation.
- * Handshake protocol operates on top of the Record Protocol.
- * It is responsible for session negotiating.
- *
- * The implementation processes inbound server handshake messages,
- * creates and sends respond messages. Outbound messages are supplied
- * to Record Protocol. Detected errors are reported to the Alert protocol.
- *
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7. The
- * TLS Handshake Protocol</a>
- *
- */
-public class ClientHandshakeImpl extends HandshakeProtocol {
-
- /**
- * Creates Client Handshake Implementation
- *
- * @param owner
- */
- ClientHandshakeImpl(SSLEngineImpl owner) {
- super(owner);
- }
-
- /**
- * Starts handshake
- *
- */
- @Override
- public void start() {
- if (session == null) { // initial handshake
- session = findSessionToResume();
- } else { // start session renegotiation
- if (clientHello != null && this.status != FINISHED) {
- // current negotiation has not completed
- return; // ignore
- }
- if (!session.isValid()) {
- session = null;
- }
- }
- if (session != null) {
- isResuming = true;
- } else if (parameters.getEnableSessionCreation()){
- isResuming = false;
- session = new SSLSessionImpl(parameters.getSecureRandom());
- session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
- session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols());
- recordProtocol.setVersion(session.protocol.version);
- } else {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created ");
- }
- startSession();
- }
-
- /**
- * Starts renegotiation on a new session
- *
- */
- private void renegotiateNewSession() {
- if (parameters.getEnableSessionCreation()){
- isResuming = false;
- session = new SSLSessionImpl(parameters.getSecureRandom());
- session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
- session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols());
- recordProtocol.setVersion(session.protocol.version);
- startSession();
- } else {
- status = NOT_HANDSHAKING;
- sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
- }
- }
-
- /*
- * Starts/resumes session
- */
- private void startSession() {
- CipherSuite[] cipher_suites;
- if (isResuming) {
- cipher_suites = new CipherSuite[] { session.cipherSuite };
- } else {
- cipher_suites = parameters.getEnabledCipherSuitesMember();
- }
- clientHello = new ClientHello(parameters.getSecureRandom(),
- session.protocol.version, session.id, cipher_suites);
- session.clientRandom = clientHello.random;
- send(clientHello);
- status = NEED_UNWRAP;
- }
-
- /**
- * Processes inbound handshake messages
- * @param bytes
- */
- @Override
- public void unwrap(byte[] bytes) {
- if (this.delegatedTaskErr != null) {
- Exception e = this.delegatedTaskErr;
- this.delegatedTaskErr = null;
- this.fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "Error in delegated task", e);
- }
- int handshakeType;
- io_stream.append(bytes);
- while (io_stream.available() > 0) {
- io_stream.mark();
- int length;
- try {
- handshakeType = io_stream.read();
- length = io_stream.readUint24();
- if (io_stream.available() < length) {
- io_stream.reset();
- return;
- }
- switch (handshakeType) {
- case 0: // HELLO_REQUEST
- // we don't need to take this message into account
- // during FINISH message verification, so remove it
- io_stream.removeFromMarkedPosition();
- if (clientHello != null
- && (clientFinished == null || serverFinished == null)) {
- //currently negotiating - ignore
- break;
- }
- // renegotiate
- if (session.isValid()) {
- session = (SSLSessionImpl) session.clone();
- isResuming = true;
- startSession();
- } else {
- // if SSLSession is invalidated (e.g. timeout limit is
- // exceeded) connection can't resume the session.
- renegotiateNewSession();
- }
- break;
- case 2: // SERVER_HELLO
- if (clientHello == null || serverHello != null) {
- unexpectedMessage();
- return;
- }
- serverHello = new ServerHello(io_stream, length);
-
- //check protocol version
- ProtocolVersion servProt = ProtocolVersion.getByVersion(serverHello.server_version);
- String[] enabled = parameters.getEnabledProtocols();
- find: {
- for (int i = 0; i < enabled.length; i++) {
- if (servProt.equals(ProtocolVersion.getByName(enabled[i]))) {
- break find;
- }
- }
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "Bad server hello protocol version");
- }
-
- // check compression method
- if (serverHello.compression_method != 0) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "Bad server hello compression method");
- }
-
- //check cipher_suite
- CipherSuite[] enabledSuites = parameters.getEnabledCipherSuitesMember();
- find: {
- for (int i = 0; i < enabledSuites.length; i++) {
- if (serverHello.cipher_suite.equals(enabledSuites[i])) {
- break find;
- }
- }
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "Bad server hello cipher suite");
- }
-
- if (isResuming) {
- if (serverHello.session_id.length == 0) {
- // server is not willing to establish the new connection
- // using specified session
- isResuming = false;
- } else if (!Arrays.equals(serverHello.session_id, clientHello.session_id)) {
- isResuming = false;
- } else if (!session.protocol.equals(servProt)) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "Bad server hello protocol version");
- } else if (!session.cipherSuite.equals(serverHello.cipher_suite)) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "Bad server hello cipher suite");
- }
- if (serverHello.server_version[1] == 1) {
- computerReferenceVerifyDataTLS("server finished");
- } else {
- computerReferenceVerifyDataSSLv3(SSLv3Constants.server);
- }
- }
- session.protocol = servProt;
- recordProtocol.setVersion(session.protocol.version);
- session.cipherSuite = serverHello.cipher_suite;
- session.id = serverHello.session_id.clone();
- session.serverRandom = serverHello.random;
- break;
- case 11: // CERTIFICATE
- if (serverHello == null || serverKeyExchange != null
- || serverCert != null || isResuming) {
- unexpectedMessage();
- return;
- }
- serverCert = new CertificateMessage(io_stream, length);
- break;
- case 12: // SERVER_KEY_EXCHANGE
- if (serverHello == null || serverKeyExchange != null
- || isResuming) {
- unexpectedMessage();
- return;
- }
- serverKeyExchange = new ServerKeyExchange(io_stream,
- length, session.cipherSuite.keyExchange);
- break;
- case 13: // CERTIFICATE_REQUEST
- if (serverCert == null || certificateRequest != null
- || session.cipherSuite.isAnonymous() || isResuming) {
- unexpectedMessage();
- return;
- }
- certificateRequest = new CertificateRequest(io_stream, length);
- break;
- case 14: // SERVER_HELLO_DONE
- if (serverHello == null || serverHelloDone != null || isResuming) {
- unexpectedMessage();
- return;
- }
- serverHelloDone = new ServerHelloDone(io_stream, length);
- if (this.nonBlocking) {
- delegatedTasks.add(new DelegatedTask(new Runnable() {
- @Override
- public void run() {
- processServerHelloDone();
- }
- }, this));
- return;
- }
- processServerHelloDone();
- break;
- case 20: // FINISHED
- if (!changeCipherSpecReceived) {
- unexpectedMessage();
- return;
- }
- serverFinished = new Finished(io_stream, length);
- verifyFinished(serverFinished.getData());
- session.lastAccessedTime = System.currentTimeMillis();
- session.context = parameters.getClientSessionContext();
- parameters.getClientSessionContext().putSession(session);
- if (isResuming) {
- sendChangeCipherSpec();
- } else {
- session.lastAccessedTime = System.currentTimeMillis();
- status = FINISHED;
- }
- // XXX there is no cleanup work
- break;
- default:
- unexpectedMessage();
- return;
- }
- } catch (IOException e) {
- // io stream dosn't contain complete handshake message
- io_stream.reset();
- return;
- }
- }
-
- }
-
- /**
- * Processes SSLv2 Hello message.
- * SSLv2 client hello message message is an unexpected message
- * for client side of handshake protocol.
- * See TLS 1.0 spec., E.1. Version 2 client hello
- * @param bytes
- */
- @Override
- public void unwrapSSLv2(byte[] bytes) {
- unexpectedMessage();
- }
-
- /**
- * Creates and sends Finished message
- */
- @Override
- protected void makeFinished() {
- byte[] verify_data;
- if (serverHello.server_version[1] == 1) {
- verify_data = new byte[12];
- computerVerifyDataTLS("client finished", verify_data);
- } else {
- verify_data = new byte[36];
- computerVerifyDataSSLv3(SSLv3Constants.client, verify_data);
- }
- clientFinished = new Finished(verify_data);
- send(clientFinished);
- if (isResuming) {
- session.lastAccessedTime = System.currentTimeMillis();
- status = FINISHED;
- } else {
- if (serverHello.server_version[1] == 1) {
- computerReferenceVerifyDataTLS("server finished");
- } else {
- computerReferenceVerifyDataSSLv3(SSLv3Constants.server);
- }
- status = NEED_UNWRAP;
- }
- }
-
- /**
- * Processes ServerHelloDone: makes verification of the server messages; sends
- * client messages, computers masterSecret, sends ChangeCipherSpec
- */
- void processServerHelloDone() {
- PrivateKey clientKey = null;
-
- if (serverCert != null) {
- if (session.cipherSuite.isAnonymous()) {
- unexpectedMessage();
- return;
- }
- verifyServerCert();
- } else {
- if (!session.cipherSuite.isAnonymous()) {
- unexpectedMessage();
- return;
- }
- }
-
- // Client certificate
- if (certificateRequest != null) {
- X509Certificate[] certs = null;
- // obtain certificates from key manager
- String alias = null;
- String[] certTypes = certificateRequest.getTypesAsString();
- X500Principal[] issuers = certificateRequest.certificate_authorities;
- X509KeyManager km = parameters.getKeyManager();
- if (km instanceof X509ExtendedKeyManager) {
- X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
- alias = ekm.chooseEngineClientAlias(certTypes, issuers, this.engineOwner);
- if (alias != null) {
- certs = ekm.getCertificateChain(alias);
- }
- } else {
- alias = km.chooseClientAlias(certTypes, issuers, null);
- if (alias != null) {
- certs = km.getCertificateChain(alias);
- }
- }
-
- session.localCertificates = certs;
- clientCert = new CertificateMessage(certs);
- clientKey = km.getPrivateKey(alias);
- send(clientCert);
- }
- // Client key exchange
- if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- // RSA encrypted premaster secret message
- Cipher c;
- try {
- c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
- if (serverKeyExchange != null) {
- if (!session.cipherSuite.isAnonymous()) {
- DigitalSignature ds = new DigitalSignature(serverCert.getAuthType());
- ds.init(serverCert.certs[0]);
- ds.update(clientHello.getRandom());
- ds.update(serverHello.getRandom());
- if (!serverKeyExchange.verifySignature(ds)) {
- fatalAlert(AlertProtocol.DECRYPT_ERROR, "Cannot verify RSA params");
- return;
- }
- }
- c.init(Cipher.WRAP_MODE, serverKeyExchange
- .getRSAPublicKey());
- } else {
- c.init(Cipher.WRAP_MODE, serverCert.certs[0]);
- }
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "Unexpected exception", e);
- return;
- }
- preMasterSecret = new byte[48];
- parameters.getSecureRandom().nextBytes(preMasterSecret);
- System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0, 2);
- try {
- clientKeyExchange = new ClientKeyExchange(c
- .wrap(new SecretKeySpec(preMasterSecret, "preMasterSecret")),
- serverHello.server_version[1] == 1);
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "Unexpected exception", e);
- return;
- }
- } else if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS_EXPORT
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA_EXPORT
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon_EXPORT) {
- /*
- * All other key exchanges should have had a DH key communicated via
- * ServerKeyExchange beforehand.
- */
- if (serverKeyExchange == null) {
- fatalAlert(AlertProtocol.UNEXPECTED_MESSAGE, "Expected ServerKeyExchange");
- return;
- }
- if (session.cipherSuite.isAnonymous() != serverKeyExchange.isAnonymous()) {
- fatalAlert(AlertProtocol.DECRYPT_ERROR, "Wrong type in ServerKeyExchange");
- return;
- }
- try {
- if (!session.cipherSuite.isAnonymous()) {
- DigitalSignature ds = new DigitalSignature(serverCert.getAuthType());
- ds.init(serverCert.certs[0]);
- ds.update(clientHello.getRandom());
- ds.update(serverHello.getRandom());
- if (!serverKeyExchange.verifySignature(ds)) {
- fatalAlert(AlertProtocol.DECRYPT_ERROR, "Cannot verify DH params");
- return;
- }
- }
- KeyFactory kf = KeyFactory.getInstance("DH");
- KeyAgreement agreement = KeyAgreement.getInstance("DH");
- KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
- PublicKey serverDhPublic = kf.generatePublic(new DHPublicKeySpec(
- serverKeyExchange.par3, serverKeyExchange.par1,
- serverKeyExchange.par2));
- DHParameterSpec spec = new DHParameterSpec(serverKeyExchange.par1,
- serverKeyExchange.par2);
- kpg.initialize(spec);
- KeyPair kp = kpg.generateKeyPair();
- DHPublicKey pubDhKey = (DHPublicKey) kp.getPublic();
- clientKeyExchange = new ClientKeyExchange(pubDhKey.getY());
- PrivateKey privDhKey = kp.getPrivate();
- agreement.init(privDhKey);
- agreement.doPhase(serverDhPublic, true);
- preMasterSecret = agreement.generateSecret();
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "Unexpected exception", e);
- return;
- }
- } else {
- fatalAlert(AlertProtocol.DECRYPT_ERROR, "Unsupported handshake type");
- return;
- }
-
- if (clientKeyExchange != null) {
- send(clientKeyExchange);
- }
-
- computerMasterSecret();
-
- // send certificate verify for all certificates except those containing
- // fixed DH parameters
- if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) {
- // Certificate verify
- String authType = clientKey.getAlgorithm();
- DigitalSignature ds = new DigitalSignature(authType);
- ds.init(clientKey);
-
- if ("RSA".equals(authType)) {
- ds.setMD5(io_stream.getDigestMD5());
- ds.setSHA(io_stream.getDigestSHA());
- } else if ("DSA".equals(authType)) {
- ds.setSHA(io_stream.getDigestSHA());
- // The Signature should be empty in case of anonymous signature algorithm:
- // } else if ("DH".equals(authType)) {
- }
- certificateVerify = new CertificateVerify(ds.sign());
- send(certificateVerify);
- }
-
- sendChangeCipherSpec();
- }
-
- /*
- * Verifies certificate path
- */
- private void verifyServerCert() {
- String authType = session.cipherSuite.getAuthType(serverKeyExchange != null);
- if (authType == null) {
- return;
- }
- String hostname = engineOwner.getPeerHost();
- try {
- X509TrustManager x509tm = parameters.getTrustManager();
- if (x509tm instanceof TrustManagerImpl) {
- TrustManagerImpl tm = (TrustManagerImpl) x509tm;
- tm.checkServerTrusted(serverCert.certs, authType, hostname);
- } else {
- x509tm.checkServerTrusted(serverCert.certs, authType);
- }
- } catch (CertificateException e) {
- fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e);
- return;
- }
- session.peerCertificates = serverCert.certs;
- }
-
- /**
- * Processes ChangeCipherSpec message
- */
- @Override
- public void receiveChangeCipherSpec() {
- if (isResuming) {
- if (serverHello == null) {
- unexpectedMessage();
- }
- } else if (clientFinished == null) {
- unexpectedMessage();
- }
- changeCipherSpecReceived = true;
- }
-
- // Find session to resume in client session context
- private SSLSessionImpl findSessionToResume() {
- String host = engineOwner.getPeerHost();
- int port = engineOwner.getPeerPort();
- if (host == null || port == -1) {
- return null; // starts new session
- }
-
- ClientSessionContext context = parameters.getClientSessionContext();
- SSLSessionImpl session
- = (SSLSessionImpl) context.getSession(host, port);
- if (session != null) {
- session = (SSLSessionImpl) session.clone();
- }
- return session;
- }
-
-}
diff --git a/src/main/java/org/conscrypt/ClientHello.java b/src/main/java/org/conscrypt/ClientHello.java
deleted file mode 100644
index 2bf9f5f..0000000
--- a/src/main/java/org/conscrypt/ClientHello.java
+++ /dev/null
@@ -1,207 +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.security.SecureRandom;
-import java.util.Arrays;
-import libcore.io.Streams;
-import org.conscrypt.util.EmptyArray;
-
-/**
- * Represents Client Hello message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.2.
- * Client hello</a>
- *
- */
-public class ClientHello extends Message {
-
- /**
- * Client version
- */
- final byte[] client_version;
-
- /**
- * Random bytes
- */
- final byte[] random = new byte[32];
-
- /**
- * Session id
- */
- final byte[] session_id;
-
- /**
- * Cipher suites supported by the client
- */
- final CipherSuite[] cipher_suites;
-
- /**
- * Compression methods supported by the client
- */
- final byte[] compression_methods;
-
- /**
- * Creates outbound message
- * @param sr
- * @param version
- * @param ses_id
- * @param cipher_suite
- */
- public ClientHello(SecureRandom sr, byte[] version, byte[] ses_id,
- CipherSuite[] cipher_suite) {
- client_version = version;
- long gmt_unix_time = System.currentTimeMillis()/1000;
- sr.nextBytes(random);
- random[0] = (byte) (gmt_unix_time & 0xFF000000 >>> 24);
- random[1] = (byte) (gmt_unix_time & 0xFF0000 >>> 16);
- random[2] = (byte) (gmt_unix_time & 0xFF00 >>> 8);
- random[3] = (byte) (gmt_unix_time & 0xFF);
- session_id = ses_id;
- this.cipher_suites = cipher_suite;
- compression_methods = new byte[] { 0 }; // CompressionMethod.null
- length = 38 + session_id.length + (this.cipher_suites.length << 1)
- + compression_methods.length;
- }
-
- /**
- * Creates inbound message
- * @param in
- * @param length
- * @throws IOException
- */
- public ClientHello(HandshakeIODataStream in, int length) throws IOException {
- client_version = new byte[2];
- client_version[0] = (byte) in.readUint8();
- client_version[1] = (byte) in.readUint8();
- Streams.readFully(in, random);
- int size = in.read();
- session_id = new byte[size];
- in.read(session_id, 0, size);
- int l = in.readUint16();
- if ((l & 0x01) == 0x01) { // cipher suites length must be an even number
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect ClientHello");
- }
- size = l >> 1;
- cipher_suites = new CipherSuite[size];
- for (int i = 0; i < size; i++) {
- byte b0 = (byte) in.read();
- byte b1 = (byte) in.read();
- cipher_suites[i] = CipherSuite.getByCode(b0, b1);
- }
- size = in.read();
- compression_methods = new byte[size];
- in.read(compression_methods, 0, size);
- this.length = 38 + session_id.length + (cipher_suites.length << 1)
- + compression_methods.length;
- if (this.length > length) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ClientHello");
- }
- // for forward compatibility, extra data is permitted;
- // must be ignored
- if (this.length < length) {
- in.skip(length - this.length);
- this.length = length;
- }
- }
- /**
- * Parse V2ClientHello
- * @param in
- * @throws IOException
- */
- public ClientHello(HandshakeIODataStream in) throws IOException {
- if (in.readUint8() != 1) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello");
- }
- client_version = new byte[2];
- client_version[0] = (byte) in.readUint8();
- client_version[1] = (byte) in.readUint8();
- int cipher_spec_length = in.readUint16();
- if (in.readUint16() != 0) { // session_id_length
- // as client already knows the protocol known to a server it should
- // initiate the connection in that native protocol
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect V2ClientHello, cannot be used for resuming");
- }
- int challenge_length = in.readUint16();
- if (challenge_length < 16) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, short challenge data");
- }
- session_id = EmptyArray.BYTE;
- cipher_suites = new CipherSuite[cipher_spec_length/3];
- for (int i = 0; i < cipher_suites.length; i++) {
- byte b0 = (byte) in.read();
- byte b1 = (byte) in.read();
- byte b2 = (byte) in.read();
- cipher_suites[i] = CipherSuite.getByCode(b0, b1, b2);
- }
- compression_methods = new byte[] { 0 }; // CompressionMethod.null
-
- if (challenge_length < 32) {
- Arrays.fill(random, 0, 32 - challenge_length, (byte)0);
- System.arraycopy(in.read(challenge_length), 0, random, 32 - challenge_length, challenge_length);
- } else if (challenge_length == 32) {
- System.arraycopy(in.read(32), 0, random, 0, 32);
- } else {
- System.arraycopy(in.read(challenge_length), challenge_length - 32, random, 0, 32);
- }
- if (in.available() > 0) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, extra data");
- }
- this.length = 38 + session_id.length + (cipher_suites.length << 1)
- + compression_methods.length;
- }
-
- /**
- * Sends message
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- out.write(client_version);
- out.write(random);
- out.writeUint8(session_id.length);
- out.write(session_id);
- int size = cipher_suites.length << 1;
- out.writeUint16(size);
- for (int i = 0; i < cipher_suites.length; i++) {
- out.write(cipher_suites[i].toBytes());
- }
- out.writeUint8(compression_methods.length);
- for (int i = 0; i < compression_methods.length; i++) {
- out.write(compression_methods[i]);
- }
- }
-
- /**
- * Returns client random
- * @return client random
- */
- public byte[] getRandom() {
- return random;
- }
-
- /**
- * Returns message type
- */
- @Override
- public int getType() {
- return Handshake.CLIENT_HELLO;
- }
-}
diff --git a/src/main/java/org/conscrypt/ClientKeyExchange.java b/src/main/java/org/conscrypt/ClientKeyExchange.java
deleted file mode 100644
index 5cad36b..0000000
--- a/src/main/java/org/conscrypt/ClientKeyExchange.java
+++ /dev/null
@@ -1,148 +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.math.BigInteger;
-import libcore.io.Streams;
-import org.conscrypt.util.EmptyArray;
-
-/**
- * Represents client key exchange message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.7.
- * Client key exchange message</a>
- *
- */
-public class ClientKeyExchange extends Message {
-
- /**
- * Exchange keys
- */
- final byte[] exchange_keys;
-
- /**
- * Equals true if TLS1.0 protocol is used
- */
- boolean isTLS;
-
- /**
- * Equals true if key exchange algorithm is RSA
- */
- final boolean isRSA;
-
- /**
- * Creates outbound message
- * @param encrypted_pre_master_secret
- * @param isTLS
- */
- public ClientKeyExchange(byte[] encrypted_pre_master_secret, boolean isTLS) {
- this.exchange_keys = encrypted_pre_master_secret;
- length = this.exchange_keys.length;
- if (isTLS) {
- length += 2;
- }
- this.isTLS = isTLS;
- isRSA = true;
- }
-
- /**
- * Creates outbound message
- * @param dh_Yc
- */
- public ClientKeyExchange(BigInteger dh_Yc) {
- byte[] bb = dh_Yc.toByteArray();
- if (bb[0] == 0) {
- exchange_keys = new byte[bb.length-1];
- System.arraycopy(bb, 1, exchange_keys, 0, exchange_keys.length);
- } else {
- exchange_keys = bb;
- }
- length = exchange_keys.length +2;
- isRSA = false;
- }
-
- /**
- * Creates empty message
- *
- */
- public ClientKeyExchange() {
- exchange_keys = EmptyArray.BYTE;
- length = 0;
- isRSA = false;
- }
-
- /**
- * Creates inbound message
- * @param length
- * @param isTLS
- * @param isRSA
- * @throws IOException
- */
- public ClientKeyExchange(HandshakeIODataStream in, int length, boolean isTLS, boolean isRSA)
- throws IOException {
- this.isTLS = isTLS;
- this.isRSA = isRSA;
- if (length == 0) {
- this.length = 0;
- exchange_keys = EmptyArray.BYTE;
- } else {
- int size;
- if (isRSA && !isTLS) {// SSL3.0 RSA
- size = length;
- this.length = size;
- } else { // DH or TLSv1 RSA
- size = in.readUint16();
- this.length = 2 + size;
- }
- exchange_keys = new byte[size];
- Streams.readFully(in, exchange_keys);
- if (this.length != length) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ClientKeyExchange");
- }
- }
- }
-
- /**
- * Sends message
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- if (exchange_keys.length != 0) {
- if (!isRSA || isTLS) {// DH or TLSv1 RSA
- out.writeUint16(exchange_keys.length);
- }
- out.write(exchange_keys);
- }
- }
-
- /**
- * Returns message type
- */
- @Override
- public int getType() {
- return Handshake.CLIENT_KEY_EXCHANGE;
- }
-
- /**
- * Returns true if the message is empty (in case of implicit DH Yc)
- */
- public boolean isEmpty() {
- return (exchange_keys.length == 0);
- }
-}
diff --git a/src/main/java/org/conscrypt/ConnectionState.java b/src/main/java/org/conscrypt/ConnectionState.java
deleted file mode 100644
index 82fbf3c..0000000
--- a/src/main/java/org/conscrypt/ConnectionState.java
+++ /dev/null
@@ -1,177 +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 javax.crypto.Cipher;
-
-/**
- * This abstract class is a base for Record Protocol operating environmet
- * of different SSL protocol versions.
- */
-public abstract class ConnectionState {
-
- /**
- * The cipher used for encode operations
- */
- protected Cipher encCipher;
-
- /**
- * The cipher used for decode operations
- */
- protected Cipher decCipher;
-
- /**
- * The block size, or zero if not a block cipher
- */
- protected int block_size;
-
- /**
- * The size of MAC used under this connection state
- */
- protected int hash_size;
-
- /**
- * Write sequence number which is incremented after each
- * encrypt call
- */
- protected final byte[] write_seq_num = {0, 0, 0, 0, 0, 0, 0, 0};
-
- /**
- * Read sequence number which is incremented after each
- * decrypt call
- */
- protected final byte[] read_seq_num = {0, 0, 0, 0, 0, 0, 0, 0};
-
- protected Logger.Stream logger = Logger.getStream("conn_state");
-
- /**
- * Returns the minimal possible size of the
- * Generic[Stream|Block]Cipher structure under this
- * connection state.
- */
- protected int getMinFragmentSize() {
- // block ciphers return value with padding included
- return encCipher.getOutputSize(1+hash_size); // 1 byte for data
- }
-
- /**
- * Returns the size of the Generic[Stream|Block]Cipher structure
- * corresponding to the content data of specified size.
- */
- protected int getFragmentSize(int content_size) {
- return encCipher.getOutputSize(content_size+hash_size);
- }
-
- /**
- * Returns the minimal upper bound of the content size enclosed
- * into the Generic[Stream|Block]Cipher structure of specified size.
- * For stream ciphers the returned value will be exact value.
- */
- protected int getContentSize(int generic_cipher_size) {
- //it does not take the padding of block ciphered structures
- //into account (so returned value can be greater than actual)
- return decCipher.getOutputSize(generic_cipher_size)-hash_size;
- }
-
- /**
- * Returns the number of bytes of padding required to round the
- * content up to the required block size. Assumes power of two
- * block size.
- */
- protected int getPaddingSize(int content_size) {
- int mask = block_size - 1;
- return (block_size - (content_size & mask));
- }
-
- /**
- * Creates the GenericStreamCipher or GenericBlockCipher
- * data structure for specified data of specified type.
- * @param type - the ContentType of the provided data
- * @param fragment - the byte array containing the
- * data to be encrypted under the current connection state.
- */
- protected byte[] encrypt(byte type, byte[] fragment) {
- return encrypt(type, fragment, 0, fragment.length);
- }
-
- /**
- * Creates the GenericStreamCipher or GenericBlockCipher
- * data structure for specified data of specified type.
- * @param type - the ContentType of the provided data
- * @param fragment - the byte array containing the
- * data to be encrypted under the current connection state.
- * @param offset - the offset from which the data begins with.
- * @param len - the length of the data.
- */
- protected abstract byte[] encrypt
- (byte type, byte[] fragment, int offset, int len);
-
- /**
- * Retrieves the fragment of the Plaintext structure of
- * the specified type from the provided data.
- * @param type - the ContentType of the data to be decrypted.
- * @param fragment - the byte array containing the
- * data to be encrypted under the current connection state.
- */
- protected byte[] decrypt(byte type, byte[] fragment) {
- return decrypt(type, fragment, 0, fragment.length);
- }
-
- /**
- * Retrieves the fragment of the Plaintext structure of
- * the specified type from the provided data.
- * @param type - the ContentType of the data to be decrypted.
- * @param fragment - the byte array containing the
- * data to be encrypted under the current connection state.
- * @param offset - the offset from which the data begins with.
- * @param len - the length of the data.
- */
- protected abstract byte[] decrypt
- (byte type, byte[] fragment, int offset, int len);
-
- /**
- * Increments the sequence number.
- */
- protected static void incSequenceNumber(byte[] seq_num) {
- int octet = 7;
- while (octet >= 0) {
- seq_num[octet] ++;
- if (seq_num[octet] == 0) {
- // characteristic overflow, so
- // carrying a number in adding
- octet --;
- } else {
- return;
- }
- }
- }
-
- /**
- * Shutdownes the protocol. It will be impossiblke to use the instance
- * after the calling of this method.
- */
- protected void shutdown() {
- encCipher = null;
- decCipher = null;
- for (int i=0; i<write_seq_num.length; i++) {
- write_seq_num[i] = 0;
- read_seq_num[i] = 0;
- }
- }
-}
-
diff --git a/src/main/java/org/conscrypt/ConnectionStateSSLv3.java b/src/main/java/org/conscrypt/ConnectionStateSSLv3.java
deleted file mode 100644
index e3cebbb..0000000
--- a/src/main/java/org/conscrypt/ConnectionStateSSLv3.java
+++ /dev/null
@@ -1,371 +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.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.util.Arrays;
-import javax.crypto.Cipher;
-import javax.crypto.NullCipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLProtocolException;
-
-
-/**
- * This class encapsulates the operating environment of the SSL v3
- * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides
- * relating encryption/decryption functionality.
- * The work functionality is based on the security
- * parameters negotiated during the handshake.
- */
-public class ConnectionStateSSLv3 extends ConnectionState {
-
- // digest to create and check the message integrity info
- private final MessageDigest messageDigest;
- private final byte[] mac_write_secret;
- private final byte[] mac_read_secret;
-
- // paddings
- private final byte[] pad_1;
- private final byte[] pad_2;
-
- // array will hold the part of the MAC material:
- // length of 6 == 2 X (1(SSLCompressed.type) + 2(SSLCompressed.length))
- // 1 each for encryption and decryption.
- //
- // (more on SSLv3 MAC computation and payload protection see
- // SSL v3 specification, p. 5.2.3)
- private final byte[] mac_material_part = new byte[6];
-
- /**
- * Creates the instance of SSL v3 Connection State. All of the
- * security parameters are provided by session object.
- * @param session the sessin object which incapsulates
- * all of the security parameters established by handshake protocol.
- * The key calculation for the state is done according
- * to the SSL v3 Protocol specification.
- * (http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt)
- */
- protected ConnectionStateSSLv3(SSLSessionImpl session) {
- try {
- CipherSuite cipherSuite = session.cipherSuite;
-
- boolean is_exportabe = cipherSuite.isExportable();
- hash_size = cipherSuite.getMACLength();
- int key_size = (is_exportabe)
- ? cipherSuite.keyMaterial
- : cipherSuite.expandedKeyMaterial;
- int iv_size = cipherSuite.ivSize;
- block_size = cipherSuite.getBlockSize();
-
- String algName = cipherSuite.getBulkEncryptionAlgorithm();
- String hashName = cipherSuite.getHashName();
- if (logger != null) {
- logger.println("ConnectionStateSSLv3.create:");
- logger.println(" cipher suite name: "
- + session.getCipherSuite());
- logger.println(" encryption alg name: " + algName);
- logger.println(" hash alg name: " + hashName);
- logger.println(" hash size: " + hash_size);
- logger.println(" block size: " + block_size);
- logger.println(" IV size:" + iv_size);
- logger.println(" key size: " + key_size);
- }
-
- byte[] clientRandom = session.clientRandom;
- byte[] serverRandom = session.serverRandom;
- // so we need PRF value of size of
- // 2*hash_size + 2*key_size + 2*iv_size
- byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
- byte[] seed = new byte[clientRandom.length + serverRandom.length];
- System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
- System.arraycopy(clientRandom, 0, seed, serverRandom.length,
- clientRandom.length);
-
- PRF.computePRF_SSLv3(key_block, session.master_secret, seed);
-
- byte[] client_mac_secret = new byte[hash_size];
- byte[] server_mac_secret = new byte[hash_size];
- byte[] client_key = new byte[key_size];
- byte[] server_key = new byte[key_size];
-
- boolean is_client = !session.isServer;
-
- System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
- System.arraycopy(key_block, hash_size,
- server_mac_secret, 0, hash_size);
- System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
- System.arraycopy(key_block, 2*hash_size+key_size,
- server_key, 0, key_size);
-
- IvParameterSpec clientIV = null;
- IvParameterSpec serverIV = null;
-
- if (is_exportabe) {
- if (logger != null) {
- logger.println("ConnectionStateSSLv3: is_exportable");
- }
-
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- md5.update(client_key);
- md5.update(clientRandom);
- md5.update(serverRandom);
- client_key = md5.digest();
-
- md5.update(server_key);
- md5.update(serverRandom);
- md5.update(clientRandom);
- server_key = md5.digest();
-
- key_size = cipherSuite.expandedKeyMaterial;
-
- if (block_size != 0) {
- md5.update(clientRandom);
- md5.update(serverRandom);
- clientIV = new IvParameterSpec(md5.digest(), 0, iv_size);
- md5.update(serverRandom);
- md5.update(clientRandom);
- serverIV = new IvParameterSpec(md5.digest(), 0, iv_size);
- }
- } else if (block_size != 0) {
- clientIV = new IvParameterSpec(key_block,
- 2*hash_size+2*key_size, iv_size);
- serverIV = new IvParameterSpec(key_block,
- 2*hash_size+2*key_size+iv_size, iv_size);
- }
-
- if (logger != null) {
- logger.println("is exportable: "+is_exportabe);
- logger.println("master_secret");
- logger.print(session.master_secret);
- logger.println("client_random");
- logger.print(clientRandom);
- logger.println("server_random");
- logger.print(serverRandom);
- //logger.println("key_block");
- //logger.print(key_block);
- logger.println("client_mac_secret");
- logger.print(client_mac_secret);
- logger.println("server_mac_secret");
- logger.print(server_mac_secret);
- logger.println("client_key");
- logger.print(client_key, 0, key_size);
- logger.println("server_key");
- logger.print(server_key, 0, key_size);
- if (clientIV != null) {
- logger.println("client_iv");
- logger.print(clientIV.getIV());
- logger.println("server_iv");
- logger.print(serverIV.getIV());
- } else {
- logger.println("no IV.");
- }
- }
-
- if (algName == null) {
- encCipher = new NullCipher();
- decCipher = new NullCipher();
- } else {
- encCipher = Cipher.getInstance(algName);
- decCipher = Cipher.getInstance(algName);
- if (is_client) { // client side
- encCipher.init(Cipher.ENCRYPT_MODE,
- new SecretKeySpec(client_key, 0, key_size, algName),
- clientIV);
- decCipher.init(Cipher.DECRYPT_MODE,
- new SecretKeySpec(server_key, 0, key_size, algName),
- serverIV);
- } else { // server side
- encCipher.init(Cipher.ENCRYPT_MODE,
- new SecretKeySpec(server_key, 0, key_size, algName),
- serverIV);
- decCipher.init(Cipher.DECRYPT_MODE,
- new SecretKeySpec(client_key, 0, key_size, algName),
- clientIV);
- }
- }
-
- messageDigest = MessageDigest.getInstance(hashName);
- if (is_client) { // client side
- mac_write_secret = client_mac_secret;
- mac_read_secret = server_mac_secret;
- } else { // server side
- mac_write_secret = server_mac_secret;
- mac_read_secret = client_mac_secret;
- }
- if (hashName.equals("MD5")) {
- pad_1 = SSLv3Constants.MD5pad1;
- pad_2 = SSLv3Constants.MD5pad2;
- } else {
- pad_1 = SSLv3Constants.SHApad1;
- pad_2 = SSLv3Constants.SHApad2;
- }
- } catch (Exception e) {
- e.printStackTrace();
- throw new AlertException(AlertProtocol.INTERNAL_ERROR,
- new SSLProtocolException(
- "Error during computation of security parameters"));
- }
- }
-
- /**
- * Creates the GenericStreamCipher or GenericBlockCipher
- * data structure for specified data of specified type.
- * @throws AlertException if alert was occurred.
- */
- @Override
- protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
- try {
- int content_mac_length = len + hash_size;
- int padding_length = (block_size == 0) ? 0 : getPaddingSize(++content_mac_length);
- byte[] res = new byte[content_mac_length + padding_length];
- System.arraycopy(fragment, offset, res, 0, len);
-
- messageDigest.update(mac_write_secret);
- messageDigest.update(pad_1);
- messageDigest.update(write_seq_num);
-
- // Use the first 3 bytes of mac_material_part as a scratch area,
- // the last 3 bytes are used by decrypt.
- mac_material_part[0] = type;
- mac_material_part[1] = (byte) ((0x00FF00 & len) >> 8);
- mac_material_part[2] = (byte) (0x0000FF & len);
- messageDigest.update(mac_material_part, 0, 3);
-
- messageDigest.update(fragment, offset, len);
- byte[] digest = messageDigest.digest();
- messageDigest.update(mac_write_secret);
- messageDigest.update(pad_2);
- messageDigest.update(digest);
- digest = messageDigest.digest();
- System.arraycopy(digest, 0, res, len, hash_size);
-
- //if (logger != null) {
- // logger.println("MAC Material:");
- // logger.print(write_seq_num);
- // logger.print(mac_material_header);
- // logger.print(fragment, offset, len);
- //}
-
- if (block_size != 0) {
- // do padding:
- Arrays.fill(res, content_mac_length-1,
- res.length, (byte) (padding_length));
- }
- if (logger != null) {
- logger.println("SSLRecordProtocol.encrypt: "
- + (block_size != 0
- ? "GenericBlockCipher with padding["
- +padding_length+"]:"
- : "GenericStreamCipher:"));
- logger.print(res);
- }
- byte[] rez = new byte[encCipher.getOutputSize(res.length)];
- encCipher.update(res, 0, res.length, rez);
- incSequenceNumber(write_seq_num);
- return rez;
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- throw new AlertException(AlertProtocol.INTERNAL_ERROR,
- new SSLProtocolException("Error during the encryption"));
- }
- }
-
- /**
- * Retrieves the fragment of the Plaintext structure of
- * the specified type from the provided data.
- * @throws AlertException if alert was occured.
- */
- @Override
- protected byte[] decrypt(byte type, byte[] fragment,
- int offset, int len) {
- // plain data of the Generic[Stream|Block]Cipher structure
- byte[] data = decCipher.update(fragment, offset, len);
- // the 'content' part of the structure
- byte[] content;
- if (block_size != 0) {
- // check padding
- int padding_length = data[data.length-1] & 0xFF;
- for (int i=0; i<padding_length; i++) {
- if ((data[data.length-2-i] & 0xFF) != padding_length) {
- throw new AlertException(
- AlertProtocol.DECRYPTION_FAILED,
- new SSLProtocolException(
- "Received message has bad padding"));
- }
- }
- content = new byte[data.length - hash_size - padding_length - 1];
- } else {
- content = new byte[data.length - hash_size];
- }
-
- byte[] mac_value;
-
- messageDigest.update(mac_read_secret);
- messageDigest.update(pad_1);
- messageDigest.update(read_seq_num);
-
- // The first 3 bytes of mac_material_part are used as a scratch area
- // by encrypt() so we use the last three bytes here.
- mac_material_part[3] = type;
- mac_material_part[4] = (byte) ((0x00FF00 & content.length) >> 8);
- mac_material_part[5] = (byte) (0x0000FF & content.length);
- messageDigest.update(mac_material_part, 3, mac_material_part.length);
-
- messageDigest.update(data, 0, content.length);
- mac_value = messageDigest.digest();
- messageDigest.update(mac_read_secret);
- messageDigest.update(pad_2);
- messageDigest.update(mac_value);
- mac_value = messageDigest.digest();
-
- if (logger != null) {
- logger.println("Decrypted:");
- logger.print(data);
- //logger.println("MAC Material:");
- //logger.print(read_seq_num);
- //logger.print(mac_material_header);
- //logger.print(data, 0, content.length);
- logger.println("Expected mac value:");
- logger.print(mac_value);
- }
- // checking the mac value
- for (int i=0; i<hash_size; i++) {
- if (mac_value[i] != data[i+content.length]) {
- throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
- new SSLProtocolException("Bad record MAC"));
- }
- }
- System.arraycopy(data, 0, content, 0, content.length);
- incSequenceNumber(read_seq_num);
- return content;
- }
-
- /**
- * Shutdown the protocol. It will be impossible to use the instance
- * after the calling of this method.
- */
- @Override
- protected void shutdown() {
- Arrays.fill(mac_write_secret, (byte) 0);
- Arrays.fill(mac_read_secret, (byte) 0);
- super.shutdown();
- }
-}
-
diff --git a/src/main/java/org/conscrypt/ConnectionStateTLS.java b/src/main/java/org/conscrypt/ConnectionStateTLS.java
deleted file mode 100644
index a4d9de8..0000000
--- a/src/main/java/org/conscrypt/ConnectionStateTLS.java
+++ /dev/null
@@ -1,351 +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.security.GeneralSecurityException;
-import java.util.Arrays;
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.NullCipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLProtocolException;
-
-/**
- * This class encapsulates the operating environment of the TLS v1
- * (http://www.ietf.org/rfc/rfc2246.txt) Record Protocol and provides
- * relating encryption/decryption functionality.
- * The work functionality is based on the security
- * parameters negotiated during the handshake.
- */
-public class ConnectionStateTLS extends ConnectionState {
-
- // Pre-calculated prf label values:
- // "key expansion".getBytes()
- private static byte[] KEY_EXPANSION_LABEL = {
- (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x20, (byte) 0x65,
- (byte) 0x78, (byte) 0x70, (byte) 0x61, (byte) 0x6E, (byte) 0x73,
- (byte) 0x69, (byte) 0x6F, (byte) 0x6E };
-
- // "client write key".getBytes()
- private static byte[] CLIENT_WRITE_KEY_LABEL = {
- (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
- (byte) 0x74, (byte) 0x20, (byte) 0x77, (byte) 0x72, (byte) 0x69,
- (byte) 0x74, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
- (byte) 0x79 };
-
- // "server write key".getBytes()
- private static byte[] SERVER_WRITE_KEY_LABEL = {
- (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65,
- (byte) 0x72, (byte) 0x20, (byte) 0x77, (byte) 0x72, (byte) 0x69,
- (byte) 0x74, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
- (byte) 0x79 };
-
- // "IV block".getBytes()
- private static byte[] IV_BLOCK_LABEL = {
- (byte) 0x49, (byte) 0x56, (byte) 0x20, (byte) 0x62, (byte) 0x6C,
- (byte) 0x6F, (byte) 0x63, (byte) 0x6B };
-
- // MACs to create and check the message integrity info
- private final Mac encMac;
- private final Mac decMac;
-
- // Once created permanently used array:
- // is used to create the header of the MAC material value:
- // 5 == 1(TLSCompressed.type) + 2(TLSCompressed.version) +
- // 2(TLSCompressed.length)
- private final byte[] mac_material_header = new byte[] {0, 3, 1, 0, 0};
-
- /**
- * Creates the instance of TLS v1 Connection State. All of the
- * security parameters are provided by session object.
- * @param session the sessin object which incapsulates
- * all of the security parameters established by handshake protocol.
- * The key calculation for the state is done according
- * to the TLS v 1.0 Protocol specification.
- * (http://www.ietf.org/rfc/rfc2246.txt)
- */
- protected ConnectionStateTLS(SSLSessionImpl session) {
- try {
- CipherSuite cipherSuite = session.cipherSuite;
-
- hash_size = cipherSuite.getMACLength();
- boolean is_exportabe = cipherSuite.isExportable();
- int key_size = (is_exportabe)
- ? cipherSuite.keyMaterial
- : cipherSuite.expandedKeyMaterial;
- int iv_size = cipherSuite.ivSize;
- block_size = cipherSuite.getBlockSize();
-
- String algName = cipherSuite.getBulkEncryptionAlgorithm();
- String macName = cipherSuite.getHmacName();
- if (logger != null) {
- logger.println("ConnectionStateTLS.create:");
- logger.println(" cipher suite name: "
- + cipherSuite.getName());
- logger.println(" encryption alg name: " + algName);
- logger.println(" mac alg name: " + macName);
- logger.println(" hash size: " + hash_size);
- logger.println(" block size: " + block_size);
- logger.println(" IV size:" + iv_size);
- logger.println(" key size: " + key_size);
- }
-
- byte[] clientRandom = session.clientRandom;
- byte[] serverRandom = session.serverRandom;
- // so we need PRF value of size of
- // 2*hash_size + 2*key_size + 2*iv_size
- byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
- byte[] seed = new byte[clientRandom.length + serverRandom.length];
- System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
- System.arraycopy(clientRandom, 0, seed, serverRandom.length,
- clientRandom.length);
-
- PRF.computePRF(key_block, session.master_secret,
- KEY_EXPANSION_LABEL, seed);
-
- byte[] client_mac_secret = new byte[hash_size];
- byte[] server_mac_secret = new byte[hash_size];
- byte[] client_key = new byte[key_size];
- byte[] server_key = new byte[key_size];
-
- boolean is_client = !session.isServer;
-
- System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
- System.arraycopy(key_block, hash_size,
- server_mac_secret, 0, hash_size);
- System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
- System.arraycopy(key_block, 2*hash_size+key_size,
- server_key, 0, key_size);
-
- IvParameterSpec clientIV = null;
- IvParameterSpec serverIV = null;
-
- if (is_exportabe) {
- System.arraycopy(clientRandom, 0,
- seed, 0, clientRandom.length);
- System.arraycopy(serverRandom, 0,
- seed, clientRandom.length, serverRandom.length);
- byte[] final_client_key =
- new byte[cipherSuite.expandedKeyMaterial];
- byte[] final_server_key =
- new byte[cipherSuite.expandedKeyMaterial];
- PRF.computePRF(final_client_key, client_key,
- CLIENT_WRITE_KEY_LABEL, seed);
- PRF.computePRF(final_server_key, server_key,
- SERVER_WRITE_KEY_LABEL, seed);
- client_key = final_client_key;
- server_key = final_server_key;
- if (block_size != 0) {
- byte[] iv_block = new byte[2*iv_size];
- PRF.computePRF(iv_block, null, IV_BLOCK_LABEL, seed);
- clientIV = new IvParameterSpec(iv_block, 0, iv_size);
- serverIV = new IvParameterSpec(iv_block, iv_size, iv_size);
- }
- } else if (block_size != 0) {
- clientIV = new IvParameterSpec(key_block,
- 2*(hash_size+key_size), iv_size);
- serverIV = new IvParameterSpec(key_block,
- 2*(hash_size+key_size)+iv_size, iv_size);
- }
-
- if (logger != null) {
- logger.println("is exportable: "+is_exportabe);
- logger.println("master_secret");
- logger.print(session.master_secret);
- logger.println("client_random");
- logger.print(clientRandom);
- logger.println("server_random");
- logger.print(serverRandom);
- //logger.println("key_block");
- //logger.print(key_block);
- logger.println("client_mac_secret");
- logger.print(client_mac_secret);
- logger.println("server_mac_secret");
- logger.print(server_mac_secret);
- logger.println("client_key");
- logger.print(client_key);
- logger.println("server_key");
- logger.print(server_key);
- if (clientIV == null) {
- logger.println("no IV.");
- } else {
- logger.println("client_iv");
- logger.print(clientIV.getIV());
- logger.println("server_iv");
- logger.print(serverIV.getIV());
- }
- }
-
- if (algName == null) {
- encCipher = new NullCipher();
- decCipher = new NullCipher();
- } else {
- encCipher = Cipher.getInstance(algName);
- decCipher = Cipher.getInstance(algName);
- if (is_client) { // client side
- encCipher.init(Cipher.ENCRYPT_MODE,
- new SecretKeySpec(client_key, algName), clientIV);
- decCipher.init(Cipher.DECRYPT_MODE,
- new SecretKeySpec(server_key, algName), serverIV);
- } else { // server side
- encCipher.init(Cipher.ENCRYPT_MODE,
- new SecretKeySpec(server_key, algName), serverIV);
- decCipher.init(Cipher.DECRYPT_MODE,
- new SecretKeySpec(client_key, algName), clientIV);
- }
- }
-
- encMac = Mac.getInstance(macName);
- decMac = Mac.getInstance(macName);
- if (is_client) { // client side
- encMac.init(new SecretKeySpec(client_mac_secret, macName));
- decMac.init(new SecretKeySpec(server_mac_secret, macName));
- } else { // server side
- encMac.init(new SecretKeySpec(server_mac_secret, macName));
- decMac.init(new SecretKeySpec(client_mac_secret, macName));
- }
- } catch (Exception e) {
- e.printStackTrace();
- throw new AlertException(AlertProtocol.INTERNAL_ERROR,
- new SSLProtocolException(
- "Error during computation of security parameters"));
- }
- }
-
- /**
- * Creates the GenericStreamCipher or GenericBlockCipher
- * data structure for specified data of specified type.
- * @throws AlertException if alert was occurred.
- */
- @Override
- protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
- try {
- int content_mac_length = len + hash_size;
- int padding_length = (block_size == 0) ? 0 : getPaddingSize(++content_mac_length);
- byte[] res = new byte[content_mac_length + padding_length];
- System.arraycopy(fragment, offset, res, 0, len);
-
- mac_material_header[0] = type;
- mac_material_header[3] = (byte) ((0x00FF00 & len) >> 8);
- mac_material_header[4] = (byte) (0x0000FF & len);
-
- encMac.update(write_seq_num);
- encMac.update(mac_material_header);
- encMac.update(fragment, offset, len);
- encMac.doFinal(res, len);
-
- //if (logger != null) {
- // logger.println("MAC Material:");
- // logger.print(write_seq_num);
- // logger.print(mac_material_header);
- // logger.print(fragment, offset, len);
- //}
-
- if (block_size != 0) {
- // do padding:
- Arrays.fill(res, content_mac_length-1,
- res.length, (byte) (padding_length));
- }
- if (logger != null) {
- logger.println("SSLRecordProtocol.do_encryption: Generic"
- + (block_size != 0
- ? "BlockCipher with padding["+padding_length+"]:"
- : "StreamCipher:"));
- logger.print(res);
- }
- byte[] rez = new byte[encCipher.getOutputSize(res.length)];
- // We should not call just doFinal because it reinitialize
- // the cipher, but as says rfc 2246:
- // "For stream ciphers that do not use a synchronization
- // vector (such as RC4), the stream cipher state from the end
- // of one record is simply used on the subsequent packet."
- // and for block ciphers:
- // "The IV for subsequent records is the last ciphertext block from
- // the previous record."
- // i.e. we should keep the cipher state.
- encCipher.update(res, 0, res.length, rez);
- incSequenceNumber(write_seq_num);
- return rez;
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- throw new AlertException(AlertProtocol.INTERNAL_ERROR,
- new SSLProtocolException("Error during the encryption"));
- }
- }
-
- /**
- * Retrieves the fragment of the Plaintext structure of
- * the specified type from the provided data representing
- * the Generic[Stream|Block]Cipher structure.
- * @throws AlertException if alert was occurred.
- */
- @Override
- protected byte[] decrypt(byte type, byte[] fragment,
- int offset, int len) {
- // plain data of the Generic[Stream|Block]Cipher structure
- byte[] data = decCipher.update(fragment, offset, len);
- // the 'content' part of the structure
- byte[] content;
- if (block_size != 0) {
- // check padding
- int padding_length = data[data.length - 1] & 0xFF;
- for (int i=0; i<padding_length; i++) {
- if ((data[data.length-2-i] & 0xFF) != padding_length) {
- throw new AlertException(
- AlertProtocol.DECRYPTION_FAILED,
- new SSLProtocolException(
- "Received message has bad padding"));
- }
- }
- content = new byte[data.length - hash_size - padding_length - 1];
- } else {
- content = new byte[data.length - hash_size];
- }
-
- mac_material_header[0] = type;
- mac_material_header[3] = (byte) ((0x00FF00 & content.length) >> 8);
- mac_material_header[4] = (byte) (0x0000FF & content.length);
-
- decMac.update(read_seq_num);
- decMac.update(mac_material_header);
- decMac.update(data, 0, content.length); // mac.update(fragment);
- byte[] mac_value = decMac.doFinal();
- if (logger != null) {
- logger.println("Decrypted:");
- logger.print(data);
- //logger.println("MAC Material:");
- //logger.print(read_seq_num);
- //logger.print(mac_material_header);
- //logger.print(data, 0, content.length);
- logger.println("Expected mac value:");
- logger.print(mac_value);
- }
- // checking the mac value
- for (int i=0; i<hash_size; i++) {
- if (mac_value[i] != data[i+content.length]) {
- throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
- new SSLProtocolException("Bad record MAC"));
- }
- }
- System.arraycopy(data, 0, content, 0, content.length);
- incSequenceNumber(read_seq_num);
- return content;
- }
-}
-
diff --git a/src/main/java/org/conscrypt/ContentType.java b/src/main/java/org/conscrypt/ContentType.java
deleted file mode 100644
index b09f029..0000000
--- a/src/main/java/org/conscrypt/ContentType.java
+++ /dev/null
@@ -1,49 +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;
-
-/**
- * This class incapsulates the constants determining the
- * types of SSL/TLS record's content data.
- * Constant values are taken according to the TLS v1 specification
- * (http://www.ietf.org/rfc/rfc2246.txt).
- */
-public class ContentType {
-
- /**
- * Identifies change cipher spec message
- */
- protected static final byte CHANGE_CIPHER_SPEC = 20;
-
- /**
- * Identifies alert message
- */
- protected static final byte ALERT = 21;
-
- /**
- * Identifies handshake message
- */
- protected static final byte HANDSHAKE = 22;
-
- /**
- * Identifies application data message
- */
- protected static final byte APPLICATION_DATA = 23;
-
-}
-
diff --git a/src/main/java/org/conscrypt/CryptoUpcalls.java b/src/main/java/org/conscrypt/CryptoUpcalls.java
new file mode 100644
index 0000000..9ab4fa1
--- /dev/null
+++ b/src/main/java/org/conscrypt/CryptoUpcalls.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * Provides a place where NativeCrypto can call back up to do Java language
+ * calls to work on delegated key types from native code.
+ */
+public final class CryptoUpcalls {
+ private static final String RSA_CRYPTO_ALGORITHM = "RSA/ECB/PKCS1Padding";
+
+ private CryptoUpcalls() {
+ }
+
+ /**
+ * Finds the first provider which provides {@code algorithm} but is not from
+ * the same ClassLoader as ours.
+ */
+ public static Provider getExternalProvider(String algorithm) {
+ Provider selectedProvider = null;
+ for (Provider p : Security.getProviders(algorithm)) {
+ if (!p.getClass().getClassLoader().equals(CryptoUpcalls.class.getClassLoader())) {
+ selectedProvider = p;
+ break;
+ }
+ }
+ if (selectedProvider == null) {
+ System.err.println("Could not find external provider for algorithm: " + algorithm);
+ }
+ return selectedProvider;
+ }
+
+ public static byte[] rawSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message) {
+ // Get the raw signature algorithm for this key type.
+ String algorithm = null;
+ // Hint: Algorithm names come from:
+ // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
+ if (javaKey instanceof RSAPrivateKey) {
+ // IMPORTANT: Due to a platform bug, this will throw
+ // NoSuchAlgorithmException
+ // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
+ // See https://android-review.googlesource.com/#/c/40352/
+ algorithm = "NONEwithRSA";
+ } else if (javaKey instanceof DSAPrivateKey) {
+ algorithm = "NONEwithDSA";
+ } else if (javaKey instanceof ECPrivateKey) {
+ algorithm = "NONEwithECDSA";
+ } else {
+ throw new RuntimeException("Unexpected key type: " + javaKey.toString());
+ }
+
+ Provider p = getExternalProvider("Signature." + algorithm);
+ if (p == null) {
+ return null;
+ }
+
+ // Get the Signature for this key.
+ Signature signature = null;
+ try {
+ signature = Signature.getInstance(algorithm, p);
+ } catch (NoSuchAlgorithmException e) {
+ ;
+ }
+
+ if (signature == null) {
+ System.err.println("Unsupported private key algorithm: " + javaKey.getAlgorithm());
+ return null;
+ }
+
+ // Sign the message.
+ try {
+ signature.initSign(javaKey);
+ signature.update(message);
+ return signature.sign();
+ } catch (Exception e) {
+ System.err.println("Exception while signing message with " + javaKey.getAlgorithm()
+ + " private key:");
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static byte[] rawCipherWithPrivateKey(PrivateKey javaKey, boolean encrypt,
+ byte[] input) {
+ if (!(javaKey instanceof RSAPrivateKey)) {
+ System.err.println("Unexpected key type: " + javaKey.toString());
+ return null;
+ }
+
+ Provider p = getExternalProvider("Cipher." + RSA_CRYPTO_ALGORITHM);
+ if (p == null) {
+ return null;
+ }
+
+ Cipher c = null;
+ try {
+ c = Cipher.getInstance(RSA_CRYPTO_ALGORITHM, p);
+ } catch (NoSuchAlgorithmException e) {
+ ;
+ } catch (NoSuchPaddingException e) {
+ ;
+ }
+
+ if (c == null) {
+ System.err.println("Unsupported private key algorithm: " + javaKey.getAlgorithm());
+ }
+
+ try {
+ c.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, javaKey);
+ return c.doFinal(input);
+ } catch (Exception e) {
+ System.err.println("Exception while ciphering message with " + javaKey.getAlgorithm()
+ + " private key:");
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/DHParameters.java b/src/main/java/org/conscrypt/DHParameters.java
deleted file mode 100644
index 7a742b2..0000000
--- a/src/main/java/org/conscrypt/DHParameters.java
+++ /dev/null
@@ -1,108 +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;
-
-/**
- * This class contains well-known primes
- */
-public class DHParameters {
-
- // Well-known 512 bit prime
- // http://news.hping.org/sci.crypt.archive/2370.html
- private static byte[] prime512 = new byte[] { (byte) 0xF5, (byte) 0x2A, (byte) 0xFF,
- (byte) 0x3C, (byte) 0xE1, (byte) 0xB1, (byte) 0x29, (byte) 0x40,
- (byte) 0x18, (byte) 0x11, (byte) 0x8D, (byte) 0x7C, (byte) 0x84,
- (byte) 0xA7, (byte) 0x0A, (byte) 0x72, (byte) 0xD6, (byte) 0x86,
- (byte) 0xC4, (byte) 0x03, (byte) 0x19, (byte) 0xC8, (byte) 0x07,
- (byte) 0x29, (byte) 0x7A, (byte) 0xCA, (byte) 0x95, (byte) 0x0C,
- (byte) 0xD9, (byte) 0x96, (byte) 0x9F, (byte) 0xAB, (byte) 0xD0,
- (byte) 0x0A, (byte) 0x50, (byte) 0x9B, (byte) 0x02, (byte) 0x46,
- (byte) 0xD3, (byte) 0x08, (byte) 0x3D, (byte) 0x66, (byte) 0xA4,
- (byte) 0x5D, (byte) 0x41, (byte) 0x9F, (byte) 0x9C, (byte) 0x7C,
- (byte) 0xBD, (byte) 0x89, (byte) 0x4B, (byte) 0x22, (byte) 0x19,
- (byte) 0x26, (byte) 0xBA, (byte) 0xAB, (byte) 0xA2, (byte) 0x5E,
- (byte) 0xC3, (byte) 0x55, (byte) 0xE9, (byte) 0x2A, (byte) 0x05,
- (byte) 0x5F };
-
- // Well-Known Group 1: A 768 bit prime rfc 2539
- // (http://www.ietf.org/rfc/rfc2539.txt?number=2539)
- private static byte[] primeGroup1 = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
- (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9,
- (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, (byte) 0x68,
- (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62,
- (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1,
- (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A,
- (byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B,
- (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B,
- (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79,
- (byte) 0x8E, (byte) 0x34, (byte) 0x04, (byte) 0xDD, (byte) 0xEF,
- (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A,
- (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A,
- (byte) 0x6D, (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37,
- (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D,
- (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85,
- (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E,
- (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9,
- (byte) 0xA6, (byte) 0x3A, (byte) 0x36, (byte) 0x20, (byte) 0xFF,
- (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
- (byte) 0xFF, (byte) 0xFF };
-
- // Well-Known Group 2: A 1024 bit prime rfc 2539
- // (http://www.ietf.org/rfc/rfc2539.txt?number=2539)
- private static byte[] primeGroup2 = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
- (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9,
- (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, (byte) 0x68,
- (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62,
- (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1,
- (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A,
- (byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B,
- (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B,
- (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79,
- (byte) 0x8E, (byte) 0x34, (byte) 0x04, (byte) 0xDD, (byte) 0xEF,
- (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A,
- (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A,
- (byte) 0x6D, (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37,
- (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D,
- (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85,
- (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E,
- (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9,
- (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, (byte) 0x0B,
- (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06,
- (byte) 0xB7, (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B,
- (byte) 0xFB, (byte) 0x5A, (byte) 0x89, (byte) 0x9F, (byte) 0xA5,
- (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C,
- (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28,
- (byte) 0x66, (byte) 0x51, (byte) 0xEC, (byte) 0xE6, (byte) 0x53,
- (byte) 0x81, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
- (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
- };
-
- private static byte[] prime;
-
- static {
-//TODO set prime depand on some system or security property
- prime = prime512;
- }
-
- /**
- * Returns prime bytes
- * @return
- */
- public static byte[] getPrime() {
- return prime;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/org/conscrypt/DataStream.java b/src/main/java/org/conscrypt/DataStream.java
deleted file mode 100644
index d65b01a..0000000
--- a/src/main/java/org/conscrypt/DataStream.java
+++ /dev/null
@@ -1,42 +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;
-
-/**
- * This interface represents the ability of the
- * classes to provide the chunks of data.
- */
-public interface DataStream {
-
- /**
- * Checks if there is data to be read.
- * @return true if there is the input data in the stream,
- * false otherwise
- */
- public boolean hasData();
-
- /**
- * Retrieves the data of specified length from the stream.
- * If the data size in the stream is less than specified length,
- * method returns all the data contained in the stream.
- * @return byte array containing the demanded data.
- */
- public byte[] getData(int length);
-
-}
-
diff --git a/src/main/java/org/conscrypt/DelegatedTask.java b/src/main/java/org/conscrypt/DelegatedTask.java
deleted file mode 100644
index 39c0913..0000000
--- a/src/main/java/org/conscrypt/DelegatedTask.java
+++ /dev/null
@@ -1,44 +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;
-
-/**
- * Delegated Runnable task for SSLEngine
- */
-public class DelegatedTask implements Runnable {
-
- private final HandshakeProtocol handshaker;
- private final Runnable action;
-
- public DelegatedTask(Runnable action, HandshakeProtocol handshaker) {
- this.action = action;
- this.handshaker = handshaker;
- }
-
- @Override
- public void run() {
- synchronized (handshaker) {
- try {
- action.run();
- } catch (RuntimeException e) {
- // pass exception to HandshakeProtocol
- handshaker.delegatedTaskErr = e;
- }
- }
- }
-}
diff --git a/src/main/java/org/conscrypt/DigitalSignature.java b/src/main/java/org/conscrypt/DigitalSignature.java
deleted file mode 100644
index e24056e..0000000
--- a/src/main/java/org/conscrypt/DigitalSignature.java
+++ /dev/null
@@ -1,259 +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.security.DigestException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.Certificate;
-import java.util.Arrays;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.net.ssl.SSLException;
-import org.conscrypt.util.EmptyArray;
-
-/**
- * This class represents Signature type, as described in TLS v 1.0 Protocol
- * specification, 7.4.3. It allow to init, update and sign hash. Hash algorithm
- * depends on SignatureAlgorithm.
- *
- * select (SignatureAlgorithm)
- * { case anonymous: struct { };
- * case rsa:
- * digitally-signed struct {
- * opaque md5_hash[16];
- * opaque sha_hash[20];
- * };
- * case dsa:
- * digitally-signed struct {
- * opaque sha_hash[20];
- * };
- * } Signature;
- *
- * Digital signing description see in TLS spec., 4.7.
- * (http://www.ietf.org/rfc/rfc2246.txt)
- *
- */
-public class DigitalSignature {
-
- private final MessageDigest md5;
- private final MessageDigest sha;
- private final Signature signature;
- private final Cipher cipher;
-
- private byte[] md5_hash;
- private byte[] sha_hash;
-
- /**
- * Create Signature type
- * @param algorithm the key algorithm used for the signature
- */
- public DigitalSignature(String algorithm) {
- try {
- sha = MessageDigest.getInstance("SHA-1");
-
- if ("RSA".equals(algorithm)) {
- md5 = MessageDigest.getInstance("MD5");
- cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
- signature = null;
- } else if ("DSA".equals(algorithm)) {
- // SignatureAlgorithm is dsa
- signature = Signature.getInstance("NONEwithDSA");
- cipher = null;
- md5 = null;
- } else {
- cipher = null;
- signature = null;
- md5 = null;
- }
- } catch (NoSuchAlgorithmException e) {
- // this should never happen
- throw new AssertionError(e);
- } catch (NoSuchPaddingException e) {
- // this should never happen
- throw new AssertionError(e);
- }
- }
-
- /**
- * Initiate Signature type by private key
- * @param key
- */
- public void init(PrivateKey key) {
- try {
- if (signature != null) {
- signature.initSign(key);
- } else if (cipher != null) {
- cipher.init(Cipher.ENCRYPT_MODE, key);
- }
- } catch (InvalidKeyException e){
- throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
- new SSLException("init - invalid private key", e));
- }
- }
-
- /**
- * Initiate Signature type by certificate
- * @param cert
- */
- public void init(Certificate cert) {
- try {
- if (signature != null) {
- signature.initVerify(cert);
- } else if (cipher != null) {
- cipher.init(Cipher.DECRYPT_MODE, cert);
- }
- } catch (InvalidKeyException e){
- throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
- new SSLException("init - invalid certificate", e));
- }
- }
-
- /**
- * Update Signature hash
- * @param data
- */
- public void update(byte[] data) {
- if (sha != null) {
- sha.update(data);
- }
- if (md5 != null) {
- md5.update(data);
- }
- }
-
- /**
- * Sets MD5 hash
- * @param data
- */
- public void setMD5(byte[] data) {
- md5_hash = data;
- }
-
- /**
- * Sets SHA hash
- * @param data
- */
- public void setSHA(byte[] data) {
- sha_hash = data;
- }
-
- /**
- * Sign hash
- * @return Signature bytes
- */
- public byte[] sign() {
- try {
- if (md5 != null && md5_hash == null) {
- md5_hash = new byte[16];
- md5.digest(md5_hash, 0, md5_hash.length);
- }
- if (md5_hash != null) {
- if (signature != null) {
- signature.update(md5_hash);
- } else if (cipher != null) {
- cipher.update(md5_hash);
- }
- }
- if (sha != null && sha_hash == null) {
- sha_hash = new byte[20];
- sha.digest(sha_hash, 0, sha_hash.length);
- }
- if (sha_hash != null) {
- if (signature != null) {
- signature.update(sha_hash);
- } else if (cipher != null) {
- cipher.update(sha_hash);
- }
- }
- if (signature != null) {
- return signature.sign();
- } else if (cipher != null) {
- return cipher.doFinal();
- }
- return EmptyArray.BYTE;
- } catch (DigestException e){
- return EmptyArray.BYTE;
- } catch (SignatureException e){
- return EmptyArray.BYTE;
- } catch (BadPaddingException e){
- return EmptyArray.BYTE;
- } catch (IllegalBlockSizeException e){
- return EmptyArray.BYTE;
- }
- }
-
- /**
- * Verifies the signature data.
- * @param data - the signature bytes
- * @return true if verified
- */
- public boolean verifySignature(byte[] data) {
- if (signature != null) {
- try {
- if (sha_hash == null) {
- sha_hash = sha.digest();
- }
- signature.update(sha_hash);
- return signature.verify(data);
- } catch (SignatureException e) {
- return false;
- }
- }
-
- if (cipher != null) {
- final byte[] decrypt;
- try {
- decrypt = cipher.doFinal(data);
- } catch (IllegalBlockSizeException e) {
- return false;
- } catch (BadPaddingException e) {
- return false;
- }
-
- final byte[] md5_sha;
- if (sha != null && sha_hash == null) {
- sha_hash = sha.digest();
- }
- if (md5 != null && md5_hash == null) {
- md5_hash = md5.digest();
- }
- if (md5_hash != null && sha_hash != null) {
- md5_sha = new byte[md5_hash.length + sha_hash.length];
- System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length);
- System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length);
- } else if (md5_hash != null) {
- md5_sha = md5_hash;
- } else {
- md5_sha = sha_hash;
- }
-
- return Arrays.equals(decrypt, md5_sha);
- } else if (data == null || data.length == 0) {
- return true;
- } else {
- return false;
- }
- }
-
-}
diff --git a/src/main/java/org/conscrypt/EndOfBufferException.java b/src/main/java/org/conscrypt/EndOfBufferException.java
deleted file mode 100644
index acd3417..0000000
--- a/src/main/java/org/conscrypt/EndOfBufferException.java
+++ /dev/null
@@ -1,33 +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;
-
-/**
- * This exception indicates that data could not be read from the stream because the underlying input
- * stream reached its end.
- */
-public class EndOfBufferException extends IOException {
-
- private static final long serialVersionUID = 1838636631255369519L;
-
- public EndOfBufferException() {
- }
-
-}
diff --git a/src/main/java/org/conscrypt/EndOfSourceException.java b/src/main/java/org/conscrypt/EndOfSourceException.java
deleted file mode 100644
index 4789cd8..0000000
--- a/src/main/java/org/conscrypt/EndOfSourceException.java
+++ /dev/null
@@ -1,33 +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;
-
-/**
- * This exception indicates that data could not be read from the buffered stream because underlying
- * data buffer was exhausted.
- */
-public class EndOfSourceException extends IOException {
-
- private static final long serialVersionUID = -4673611435974054413L;
-
- public EndOfSourceException() {
- }
-
-}
diff --git a/src/main/java/org/conscrypt/FileClientSessionCache.java b/src/main/java/org/conscrypt/FileClientSessionCache.java
index be95622..26e32ac 100644
--- a/src/main/java/org/conscrypt/FileClientSessionCache.java
+++ b/src/main/java/org/conscrypt/FileClientSessionCache.java
@@ -30,14 +30,12 @@
import java.util.Set;
import java.util.TreeSet;
import javax.net.ssl.SSLSession;
-import libcore.io.IoUtils;
/**
* File-based cache implementation. Only one process should access the
* underlying directory at a time.
*/
public class FileClientSessionCache {
-
public static final int MAX_SIZE = 12; // ~72k
private FileClientSessionCache() {}
@@ -171,12 +169,20 @@
logReadError(host, file, e);
return null;
} finally {
- IoUtils.closeQuietly(in);
+ if (in != null) {
+ try {
+ in.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
}
}
static void logReadError(String host, File file, Throwable t) {
- System.logW("Error reading session data for " + host + " from " + file + ".", t);
+ System.err.println("FileClientSessionCache: Error reading session data for " + host + " from " + file + ".");
+ t.printStackTrace();
}
@Override
@@ -290,13 +296,14 @@
@SuppressWarnings("ThrowableInstanceNeverThrown")
private void delete(File file) {
if (!file.delete()) {
- System.logW("Failed to delete " + file + ".", new IOException());
+ new IOException("FileClientSessionCache: Failed to delete " + file + ".").printStackTrace();
}
size--;
}
static void logWriteError(String host, File file, Throwable t) {
- System.logW("Error writing session data for " + host + " to " + file + ".", t);
+ System.err.println("FileClientSessionCache: Error writing session data for " + host + " to " + file + ".");
+ t.printStackTrace();
}
}
diff --git a/src/main/java/org/conscrypt/Finished.java b/src/main/java/org/conscrypt/Finished.java
deleted file mode 100644
index e0b7eaa..0000000
--- a/src/main/java/org/conscrypt/Finished.java
+++ /dev/null
@@ -1,78 +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;
-
-/**
- *
- * Represents Finished message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.9.
- * Finished</a>
- *
- */
-public class Finished extends Message {
-
- // verify data
- private byte[] data;
-
- /**
- * Creates outbound message
- * @param bytes
- */
- public Finished(byte[] bytes) {
- data = bytes;
- length = data.length;
- }
-
- /**
- * Creates inbound message
- * @param in
- * @param length
- * @throws IOException
- */
- public Finished(HandshakeIODataStream in, int length)
- throws IOException {
- if (length == 12 || length == 36) {
- data = in.read(length);
- this.length = data.length;
- } else {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect Finished");
- }
- }
-
- @Override
- public void send(HandshakeIODataStream out) {
- out.write(data);
- }
-
- /**
- * Returns message type
- */
- @Override
- public int getType() {
- return Handshake.FINISHED;
- }
-
- /**
- * Returns verify data
- */
- public byte[] getData() {
- return data;
- }
-}
diff --git a/src/main/java/org/conscrypt/Handshake.java b/src/main/java/org/conscrypt/Handshake.java
deleted file mode 100644
index 7cee71b..0000000
--- a/src/main/java/org/conscrypt/Handshake.java
+++ /dev/null
@@ -1,89 +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;
-
-/**
- *
- * This class incapsulates the constants determining the types of handshake
- * messages as defined in TLS 1.0 spec., 7.4. Handshake protocol.
- * (http://www.ietf.org/rfc/rfc2246.txt)
- *
- */
-public class Handshake {
-
- /**
- *
- * hello_request handshake type
- */
- public static final byte HELLO_REQUEST = 0;
-
- /**
- *
- * client_hello handshake type
- */
- public static final byte CLIENT_HELLO = 1;
-
- /**
- *
- * server_hello handshake type
- */
- public static final byte SERVER_HELLO = 2;
-
- /**
- *
- * certificate handshake type
- */
- public static final byte CERTIFICATE = 11;
-
- /**
- *
- * server_key_exchange handshake type
- */
- public static final byte SERVER_KEY_EXCHANGE = 12;
-
- /**
- *
- * certificate_request handshake type
- */
- public static final byte CERTIFICATE_REQUEST = 13;
-
- /**
- *
- * server_hello_done handshake type
- */
- public static final byte SERVER_HELLO_DONE = 14;
-
- /**
- *
- * certificate_verify handshake type
- */
- public static final byte CERTIFICATE_VERIFY = 15;
-
- /**
- *
- * client_key_exchange handshake type
- */
- public static final byte CLIENT_KEY_EXCHANGE = 16;
-
- /**
- *
- * finished handshake type
- */
- public static final byte FINISHED = 20;
-
-}
\ No newline at end of file
diff --git a/src/main/java/org/conscrypt/HandshakeIODataStream.java b/src/main/java/org/conscrypt/HandshakeIODataStream.java
deleted file mode 100644
index 7091525..0000000
--- a/src/main/java/org/conscrypt/HandshakeIODataStream.java
+++ /dev/null
@@ -1,436 +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.security.MessageDigest;
-import java.util.Arrays;
-import javax.net.ssl.SSLHandshakeException;
-
-/**
- * This class provides Input/Output data functionality
- * for handshake layer. It provides read and write operations
- * and accumulates all sent/received handshake's data.
- * This class can be presented as a combination of 2 data pipes.
- * The first data pipe is a pipe of income data: append method
- * places the data at the beginning of the pipe, and read methods
- * consume the data from the pipe. The second pipe is an outcoming
- * data pipe: write operations plases the data into the pipe,
- * and getData methods consume the data.
- * It is important to note that work with pipe cound not be
- * started if there is unconsumed data in another pipe. It is
- * reasoned by the following: handshake protocol performs read
- * and write operations consecuently. I.e. it first reads all
- * income data and only than produces the responce and places it
- * into the stream.
- * The read operations of the stream presented by the methods
- * of SSLInputStream which in its turn is an extension of InputStream.
- * So this stream can be used as an InputStream parameter for
- * certificate generation.
- * Also input stream functionality supports marks. The marks
- * help to reset the position of the stream in case of incompleate
- * handshake records. Note that in case of exhausting
- * of income data the EndOfBufferException is thown which implies
- * the following:
- * 1. the stream contains scrappy handshake record,
- * 2. the read position should be reseted to marked,
- * 3. and more income data is expected.
- * The throwing of the exception (instead of returning of -1 value
- * or incompleate filling of destination buffer)
- * helps to speed up the process of scrappy data recognition and
- * processing.
- * For more information about TLS handshake process see
- * TLS v 1 specification at http://www.ietf.org/rfc/rfc2246.txt.
- */
-public class HandshakeIODataStream
- extends SSLInputStream implements org.conscrypt.Appendable, DataStream {
-
- // Objects are used to compute digests of data passed
- // during the handshake phase
- private static final MessageDigest md5;
- private static final MessageDigest sha;
-
- static {
- try {
- md5 = MessageDigest.getInstance("MD5");
- sha = MessageDigest.getInstance("SHA-1");
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException(
- "Could not initialize the Digest Algorithms.");
- }
- }
-
- public HandshakeIODataStream() {}
-
- // buffer is used to keep the handshaking data;
- private int buff_size = 1024;
- private int inc_buff_size = 1024;
- private byte[] buffer = new byte[buff_size];
-
-
- // ---------------- Input related functionality -----------------
-
- // position of the next byte to read
- private int read_pos;
- private int marked_pos;
- // position of the last byte to read + 1
- private int read_pos_end;
-
- @Override
- public int available() {
- return read_pos_end - read_pos;
- }
-
- @Override
- public boolean markSupported() {
- return true;
- }
-
- @Override
- public void mark(int limit) {
- marked_pos = read_pos;
- }
-
- public void mark() {
- marked_pos = read_pos;
- }
-
- @Override
- public void reset() {
- read_pos = marked_pos;
- }
-
- /**
- * Removes the data from the marked position to
- * the current read position. The method is usefull when it is needed
- * to delete one message from the internal buffer.
- */
- protected void removeFromMarkedPosition() {
- System.arraycopy(buffer, read_pos,
- buffer, marked_pos, read_pos_end - read_pos);
- read_pos_end -= (read_pos - marked_pos);
- read_pos = marked_pos;
- }
-
- /**
- * read an opaque value;
- * @param byte: byte
- * @return
- */
- @Override
- public int read() throws IOException {
- if (read_pos == read_pos_end) {
- //return -1;
- throw new EndOfBufferException();
- }
- return buffer[read_pos++] & 0xFF;
- }
-
- /**
- * reads vector of opaque values
- * @param new: long
- * @return
- */
- @Override
- public byte[] read(int length) throws IOException {
- if (length > available()) {
- throw new EndOfBufferException();
- }
- byte[] res = new byte[length];
- System.arraycopy(buffer, read_pos, res, 0, length);
- read_pos = read_pos + length;
- return res;
- }
-
- @Override
- public int read(byte[] dst, int offset, int length) throws IOException {
- if (length > available()) {
- throw new EndOfBufferException();
- }
- System.arraycopy(buffer, read_pos, dst, offset, length);
- read_pos = read_pos + length;
- return length;
- }
-
- // ------------------- Extending of the input data ---------------------
-
- /**
- * Appends the income data to be read by handshake protocol.
- * The attempts to overflow the buffer by means of this methods
- * seem to be futile because of:
- * 1. The SSL protocol specifies the maximum size of the record
- * and record protocol does not pass huge messages.
- * (see TLS v1 specification http://www.ietf.org/rfc/rfc2246.txt ,
- * p 6.2)
- * 2. After each call of this method, handshake protocol should
- * start (and starts) the operations on received data and recognize
- * the fake data if such was provided (to check the size of certificate
- * for example).
- */
- @Override
- public void append(byte[] src) {
- append(src, 0, src.length);
- }
-
- private void append(byte[] src, int from, int length) {
- if (read_pos == read_pos_end) {
- // start reading state after writing
- if (write_pos_beg != write_pos) {
- // error: outboud handshake data was not sent,
- // but inbound handshake data has been received.
- throw new AlertException(
- AlertProtocol.UNEXPECTED_MESSAGE,
- new SSLHandshakeException(
- "Handshake message has been received before "
- + "the last oubound message had been sent."));
- }
- if (read_pos < write_pos) {
- read_pos = write_pos;
- read_pos_end = read_pos;
- }
- }
- if (read_pos_end + length > buff_size) {
- enlargeBuffer(read_pos_end+length-buff_size);
- }
- System.arraycopy(src, from, buffer, read_pos_end, length);
- read_pos_end += length;
- }
-
- private void enlargeBuffer(int size) {
- buff_size = (size < inc_buff_size)
- ? buff_size + inc_buff_size
- : buff_size + size;
- byte[] new_buff = new byte[buff_size];
- System.arraycopy(buffer, 0, new_buff, 0, buffer.length);
- buffer = new_buff;
- }
-
- protected void clearBuffer() {
- read_pos = 0;
- marked_pos = 0;
- read_pos_end = 0;
- write_pos = 0;
- write_pos_beg = 0;
- Arrays.fill(buffer, (byte) 0);
- }
-
- // ------------------- Output related functionality --------------------
-
- // position in the buffer available for write
- private int write_pos;
- // position in the buffer where the last write session has begun
- private int write_pos_beg;
-
- // checks if the data can be written in the buffer
- private void check(int length) {
- // (write_pos == write_pos_beg) iff:
- // 1. there were not write operations yet
- // 2. all written data was demanded by getData methods
- if (write_pos == write_pos_beg) {
- // just started to write after the reading
- if (read_pos != read_pos_end) {
- // error: attempt to write outbound data into the stream before
- // all the inbound handshake data had been read
- throw new AlertException(
- AlertProtocol.INTERNAL_ERROR,
- new SSLHandshakeException("Data was not fully read: "
- + read_pos + " " + read_pos_end));
- }
- // set up the write positions
- if (write_pos_beg < read_pos_end) {
- write_pos_beg = read_pos_end;
- write_pos = write_pos_beg;
- }
- }
- // if there is not enought free space in the buffer - enlarge it:
- if (write_pos + length >= buff_size) {
- enlargeBuffer(length);
- }
- }
-
- /**
- * Writes an opaque value
- * @param byte: byte
- */
- public void write(byte b) {
- check(1);
- buffer[write_pos++] = b;
- }
-
- /**
- * Writes Uint8 value
- * @param long: the value to be written (last byte)
- */
- public void writeUint8(long n) {
- check(1);
- buffer[write_pos++] = (byte) (n & 0x00ff);
- }
-
- /**
- * Writes Uint16 value
- * @param long: the value to be written (last 2 bytes)
- */
- public void writeUint16(long n) {
- check(2);
- buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
- buffer[write_pos++] = (byte) (n & 0x00ff);
- }
-
- /**
- * Writes Uint24 value
- * @param long: the value to be written (last 3 bytes)
- */
- public void writeUint24(long n) {
- check(3);
- buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
- buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
- buffer[write_pos++] = (byte) (n & 0x00ff);
- }
-
- /**
- * Writes Uint32 value
- * @param long: the value to be written (last 4 bytes)
- */
- public void writeUint32(long n) {
- check(4);
- buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24);
- buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
- buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
- buffer[write_pos++] = (byte) (n & 0x00ff);
- }
-
- /**
- * Writes Uint64 value
- * @param long: the value to be written
- */
- public void writeUint64(long n) {
- check(8);
- buffer[write_pos++] = (byte) ((n & 0x00ff00000000000000L) >> 56);
- buffer[write_pos++] = (byte) ((n & 0x00ff000000000000L) >> 48);
- buffer[write_pos++] = (byte) ((n & 0x00ff0000000000L) >> 40);
- buffer[write_pos++] = (byte) ((n & 0x00ff00000000L) >> 32);
- buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24);
- buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
- buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
- buffer[write_pos++] = (byte) (n & 0x00ff);
- }
-
- /**
- * writes vector of opaque values
- * @param vector the vector to be written
- */
- public void write(byte[] vector) {
- check(vector.length);
- System.arraycopy(vector, 0, buffer, write_pos, vector.length);
- write_pos += vector.length;
- }
-
- // ------------------- Retrieve the written bytes ----------------------
-
- @Override
- public boolean hasData() {
- return (write_pos > write_pos_beg);
- }
-
- /**
- * returns the chunk of stored data with the length no more than specified.
- * @param length: int
- * @return
- */
- @Override
- public byte[] getData(int length) {
- byte[] res;
- if (write_pos - write_pos_beg < length) {
- res = new byte[write_pos - write_pos_beg];
- System.arraycopy(buffer, write_pos_beg,
- res, 0, write_pos-write_pos_beg);
- write_pos_beg = write_pos;
- } else {
- res = new byte[length];
- System.arraycopy(buffer, write_pos_beg, res, 0, length);
- write_pos_beg += length;
- }
- return res;
- }
-
- // ---------------------- Message Digest Functionality ----------------
-
- /**
- * Returns the MD5 digest of the data passed throught the stream
- * @return MD5 digest
- */
- protected byte[] getDigestMD5() {
- synchronized (md5) {
- int len = (read_pos_end > write_pos)
- ? read_pos_end
- : write_pos;
- md5.update(buffer, 0, len);
- return md5.digest();
- }
- }
-
- /**
- * Returns the SHA-1 digest of the data passed throught the stream
- * @return SHA-1 digest
- */
- protected byte[] getDigestSHA() {
- synchronized (sha) {
- int len = (read_pos_end > write_pos)
- ? read_pos_end
- : write_pos;
- sha.update(buffer, 0, len);
- return sha.digest();
- }
- }
-
- /**
- * Returns the MD5 digest of the data passed throught the stream
- * except last message
- * @return MD5 digest
- */
- protected byte[] getDigestMD5withoutLast() {
- synchronized (md5) {
- md5.update(buffer, 0, marked_pos);
- return md5.digest();
- }
- }
-
- /**
- * Returns the SHA-1 digest of the data passed throught the stream
- * except last message
- * @return SHA-1 digest
- */
- protected byte[] getDigestSHAwithoutLast() {
- synchronized (sha) {
- sha.update(buffer, 0, marked_pos);
- return sha.digest();
- }
- }
-
- /**
- * Returns all the data passed throught the stream
- * @return all the data passed throught the stream at the moment
- */
- protected byte[] getMessages() {
- int len = (read_pos_end > write_pos) ? read_pos_end : write_pos;
- byte[] res = new byte[len];
- System.arraycopy(buffer, 0, res, 0, len);
- return res;
- }
-}
diff --git a/src/main/java/org/conscrypt/HandshakeProtocol.java b/src/main/java/org/conscrypt/HandshakeProtocol.java
deleted file mode 100644
index 76bbafe..0000000
--- a/src/main/java/org/conscrypt/HandshakeProtocol.java
+++ /dev/null
@@ -1,518 +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.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.interfaces.RSAKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Arrays;
-import java.util.Vector;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-
-/**
- * Base class for ClientHandshakeImpl and ServerHandshakeImpl classes.
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.
- * Handshake protocol</a>
- *
- */
-public abstract class HandshakeProtocol {
-
- /**
- * Handshake status NEED_UNWRAP - HandshakeProtocol needs to receive data
- */
- public static final int NEED_UNWRAP = 1;
-
- /**
- * Handshake status NOT_HANDSHAKING - is not currently handshaking
- */
- public static final int NOT_HANDSHAKING = 2;
-
- /**
- * Handshake status FINISHED - HandshakeProtocol has just finished
- */
- public static final int FINISHED = 3;
-
- /**
- * Handshake status NEED_TASK - HandshakeProtocol needs the results of delegated task
- */
- public static final int NEED_TASK = 4;
-
- /**
- * Current handshake status
- */
- protected int status = NOT_HANDSHAKING;
-
- /**
- * IO stream for income/outcome handshake data
- */
- protected HandshakeIODataStream io_stream = new HandshakeIODataStream();
-
- /**
- * SSL Record Protocol implementation.
- */
- protected SSLRecordProtocol recordProtocol;
-
- /**
- * SSLParametersImpl suplied by SSLSocket or SSLEngine
- */
- protected SSLParametersImpl parameters;
-
- /**
- * Delegated tasks for this handshake implementation
- */
- protected Vector<DelegatedTask> delegatedTasks = new Vector<DelegatedTask>();
-
- /**
- * Indicates non-blocking handshake
- */
- protected boolean nonBlocking;
-
- /**
- * Pending session
- */
- protected SSLSessionImpl session;
-
- /**
- * Sent and received handshake messages
- */
- protected ClientHello clientHello;
- protected ServerHello serverHello;
- protected CertificateMessage serverCert;
- protected ServerKeyExchange serverKeyExchange;
- protected CertificateRequest certificateRequest;
- protected ServerHelloDone serverHelloDone;
- protected CertificateMessage clientCert;
- protected ClientKeyExchange clientKeyExchange;
- protected CertificateVerify certificateVerify;
- protected Finished clientFinished;
- protected Finished serverFinished;
-
- /**
- * Indicates that change cipher spec message has been received
- */
- protected boolean changeCipherSpecReceived = false;
-
- /**
- * Indicates previous session resuming
- */
- protected boolean isResuming = false;
-
- /**
- * Premaster secret
- */
- protected byte[] preMasterSecret;
-
- /**
- * Exception occured in delegated task
- */
- protected Exception delegatedTaskErr;
-
- // reference verify_data used to verify finished message
- private byte[] verify_data = new byte[12];
-
- // Encoding of "master secret" string: "master secret".getBytes()
- private byte[] master_secret_bytes =
- {109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 };
-
- // indicates whether protocol needs to send change cipher spec message
- private boolean needSendCCSpec = false;
-
- // indicates whether protocol needs to send change cipher spec message
- protected boolean needSendHelloRequest = false;
-
- /**
- * SSLEngine owning this HandshakeProtocol
- */
- protected final SSLEngineImpl engineOwner;
-
- /**
- * Creates HandshakeProtocol instance
- * @param owner
- */
- protected HandshakeProtocol(SSLEngineImpl owner) {
- if (owner == null) {
- throw new NullPointerException();
- }
- engineOwner = owner;
- nonBlocking = true;
- this.parameters = engineOwner.sslParameters;
- }
-
- /**
- * Sets SSL Record Protocol
- * @param recordProtocol
- */
- public void setRecordProtocol(SSLRecordProtocol recordProtocol) {
- this.recordProtocol = recordProtocol;
- }
-
- /**
- * Start session negotiation
- */
- public abstract void start();
-
- /**
- * Stops the current session renegotiation process.
- * Such functionality is needed when it is session renegotiation
- * process and no_renegotiation alert message is received
- * from another peer.
- * @param session
- */
- protected void stop() {
- clearMessages();
- status = NOT_HANDSHAKING;
- }
-
- /**
- * Returns handshake status
- */
- public SSLEngineResult.HandshakeStatus getStatus() {
- if (io_stream.hasData() || needSendCCSpec ||
- needSendHelloRequest || delegatedTaskErr != null) {
- return SSLEngineResult.HandshakeStatus.NEED_WRAP;
- }
- if (!delegatedTasks.isEmpty()) {
- return SSLEngineResult.HandshakeStatus.NEED_TASK;
- }
-
- switch (status) {
- case HandshakeProtocol.NEED_UNWRAP:
- return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
- case HandshakeProtocol.FINISHED:
- status = NOT_HANDSHAKING;
- clearMessages();
- return SSLEngineResult.HandshakeStatus.FINISHED;
- default: // HandshakeProtocol.NOT_HANDSHAKING:
- return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
- }
- }
-
- /**
- * Returns pending session
- * @return session
- */
- public SSLSessionImpl getSession() {
- return session;
- }
-
- protected void sendChangeCipherSpec() {
- needSendCCSpec = true;
- }
-
- protected void sendHelloRequest() {
- needSendHelloRequest = true;
- }
-
- /**
- * Proceses inbound ChangeCipherSpec message
- */
- abstract void receiveChangeCipherSpec();
-
- /**
- * Creates and sends finished message
- */
- abstract void makeFinished();
-
- /**
- * Proceses inbound handshake messages
- * @param bytes
- */
- public abstract void unwrap(byte[] bytes);
-
- /**
- * Processes SSLv2 Hello message
- * @param bytes
- */
- public abstract void unwrapSSLv2(byte[] bytes);
-
- /**
- * Processes outbound handshake messages
- */
- public byte[] wrap() {
- if (delegatedTaskErr != null) {
- // process error occured in delegated task
- Exception e = delegatedTaskErr;
- delegatedTaskErr = null;
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "Error occured in delegated task:" + e.getMessage(), e);
- }
- if (io_stream.hasData()) {
- return recordProtocol.wrap(ContentType.HANDSHAKE, io_stream);
- } else if (needSendCCSpec) {
- makeFinished();
- needSendCCSpec = false;
- return recordProtocol.getChangeCipherSpecMesage(getSession());
- } else if (needSendHelloRequest) {
- needSendHelloRequest = false;
- return recordProtocol.wrap(ContentType.HANDSHAKE,
- // hello request message
- // (see TLS v 1 specification:
- // http://www.ietf.org/rfc/rfc2246.txt)
- new byte[] {0, 0, 0, 0}, 0, 4);
- } else {
- return null; // nothing to send;
- }
- }
-
- /**
- * Sends fatal alert, breaks execution
- *
- * @param description
- */
- protected void sendWarningAlert(byte description) {
- recordProtocol.alert(AlertProtocol.WARNING, description);
- }
-
- /**
- * Sends fatal alert, breaks execution
- *
- * @param description
- * @param reason
- */
- protected void fatalAlert(byte description, String reason) {
- throw new AlertException(description, new SSLHandshakeException(reason));
- }
-
- /**
- * Sends fatal alert, breaks execution
- *
- * @param description
- * @param reason
- * @param cause
- */
- protected void fatalAlert(byte description, String reason, Exception cause) {
- throw new AlertException(description, new SSLException(reason, cause));
- }
-
- /**
- * Sends fatal alert, breaks execution
- *
- * @param description
- * @param cause
- */
- protected void fatalAlert(byte description, SSLException cause) {
- throw new AlertException(description, cause);
- }
-
- /**
- * Computers reference TLS verify_data that is used to verify finished message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS spec. 7.4.9. Finished</a>
- * @param label
- */
- protected void computerReferenceVerifyDataTLS(String label) {
- computerVerifyDataTLS(label, verify_data);
- }
-
- /**
- * Computer TLS verify_data
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS spec. 7.4.9. Finished</a>
- * @param label
- * @param buf
- */
- protected void computerVerifyDataTLS(String label, byte[] buf) {
- byte[] md5_digest = io_stream.getDigestMD5();
- byte[] sha_digest = io_stream.getDigestSHA();
-
- byte[] digest = new byte[md5_digest.length + sha_digest.length];
- System.arraycopy(md5_digest, 0, digest, 0, md5_digest.length);
- System.arraycopy(sha_digest, 0, digest, md5_digest.length,
- sha_digest.length);
- try {
- PRF.computePRF(buf, session.master_secret,
- label.getBytes(), digest);
- } catch (GeneralSecurityException e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "PRF error", e);
- }
- }
-
- /**
- * Computer reference SSLv3 verify_data that is used to verify finished message
- * @see "SSLv3 spec. 7.6.9. Finished"
- * @param label
- */
- protected void computerReferenceVerifyDataSSLv3(byte[] sender) {
- verify_data = new byte[36];
- computerVerifyDataSSLv3(sender, verify_data);
- }
-
- /**
- * Computer SSLv3 verify_data
- * @see "SSLv3 spec. 7.6.9. Finished"
- * @param label
- * @param buf
- */
- protected void computerVerifyDataSSLv3(byte[] sender, byte[] buf) {
- MessageDigest md5;
- MessageDigest sha;
- try {
- md5 = MessageDigest.getInstance("MD5");
- sha = MessageDigest.getInstance("SHA-1");
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "Could not initialize the Digest Algorithms.",
- e);
- return;
- }
- try {
- byte[] handshake_messages = io_stream.getMessages();
- md5.update(handshake_messages);
- md5.update(sender);
- md5.update(session.master_secret);
- byte[] b = md5.digest(SSLv3Constants.MD5pad1);
- md5.update(session.master_secret);
- md5.update(SSLv3Constants.MD5pad2);
- System.arraycopy(md5.digest(b), 0, buf, 0, 16);
-
- sha.update(handshake_messages);
- sha.update(sender);
- sha.update(session.master_secret);
- b = sha.digest(SSLv3Constants.SHApad1);
- sha.update(session.master_secret);
- sha.update(SSLv3Constants.SHApad2);
- System.arraycopy(sha.digest(b), 0, buf, 16, 20);
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
-
- }
- }
-
- /**
- * Verifies finished data
- *
- * @param data
- * @param isServer
- */
- protected void verifyFinished(byte[] data) {
- if (!Arrays.equals(verify_data, data)) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "Incorrect FINISED");
- }
- }
-
- /**
- * Sends fatal alert "UNEXPECTED MESSAGE"
- *
- */
- protected void unexpectedMessage() {
- fatalAlert(AlertProtocol.UNEXPECTED_MESSAGE, "UNEXPECTED MESSAGE");
- }
-
- /**
- * Writes message to HandshakeIODataStream
- *
- * @param message
- */
- public void send(Message message) {
- io_stream.writeUint8(message.getType());
- io_stream.writeUint24(message.length());
- message.send(io_stream);
- }
-
- /**
- * Computers master secret
- *
- */
- public void computerMasterSecret() {
- byte[] seed = new byte[64];
- System.arraycopy(clientHello.getRandom(), 0, seed, 0, 32);
- System.arraycopy(serverHello.getRandom(), 0, seed, 32, 32);
- session.master_secret = new byte[48];
- if (serverHello.server_version[1] == 1) { // TLSv1
- try {
- PRF.computePRF(session.master_secret, preMasterSecret,
- master_secret_bytes, seed);
- } catch (GeneralSecurityException e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "PRF error", e);
- }
- } else { // SSL3.0
- PRF.computePRF_SSLv3(session.master_secret, preMasterSecret, seed);
- }
-
- //delete preMasterSecret from memory
- Arrays.fill(preMasterSecret, (byte)0);
- preMasterSecret = null;
- }
-
- /**
- * Returns a delegated task.
- * @return Delegated task or null
- */
- public Runnable getTask() {
- if (delegatedTasks.isEmpty()) {
- return null;
- }
- return delegatedTasks.remove(0);
- }
-
- /**
- * Clears previously sent and received handshake messages
- */
- protected void clearMessages() {
- io_stream.clearBuffer();
- clientHello = null;
- serverHello = null;
- serverCert = null;
- serverKeyExchange = null;
- certificateRequest = null;
- serverHelloDone = null;
- clientCert = null;
- clientKeyExchange = null;
- certificateVerify = null;
- clientFinished = null;
- serverFinished = null;
- }
-
- /**
- * Returns RSA key length
- * @param pk
- * @return
- * @throws NoSuchAlgorithmException
- * @throws InvalidKeySpecException
- */
- protected static int getRSAKeyLength(PublicKey pk)
- throws NoSuchAlgorithmException, InvalidKeySpecException {
-
- BigInteger mod;
- if (pk instanceof RSAKey) {
- mod = ((RSAKey) pk).getModulus();
- } else {
- KeyFactory kf = KeyFactory.getInstance("RSA");
- mod = kf.getKeySpec(pk, RSAPublicKeySpec.class)
- .getModulus();
- }
- return mod.bitLength();
- }
-
- /**
- * Shuts down the protocol. It will be impossible to use the instance
- * after calling this method.
- */
- protected void shutdown() {
- clearMessages();
- session = null;
- preMasterSecret = null;
- delegatedTasks.clear();
- }
-}
diff --git a/src/main/java/org/conscrypt/HelloRequest.java b/src/main/java/org/conscrypt/HelloRequest.java
deleted file mode 100644
index 20efbfd..0000000
--- a/src/main/java/org/conscrypt/HelloRequest.java
+++ /dev/null
@@ -1,73 +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;
-
-/**
- *
- * Represents Hello Request message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.1.
- * Hello request</a>
- *
- */
-public class HelloRequest extends Message {
-
- /**
- * Creates outbound message
- *
- */
- public HelloRequest() {
- }
-
- /**
- * Creates inbound message
- * @param in
- * @param length
- * @throws IOException
- */
- public HelloRequest(HandshakeIODataStream in, int length)
- throws IOException {
- if (length != 0) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect HelloRequest");
- }
- }
-
- /**
- * Sends message
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- }
-
- @Override
- public int length() {
- return 0;
- }
-
- /**
- * Returns message type
- * @return
- */
- @Override
- public int getType() {
- return Handshake.HELLO_REQUEST;
- }
-
-}
diff --git a/src/main/java/org/conscrypt/Logger.java b/src/main/java/org/conscrypt/Logger.java
deleted file mode 100644
index a908557..0000000
--- a/src/main/java/org/conscrypt/Logger.java
+++ /dev/null
@@ -1,107 +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.PrintStream;
-import org.conscrypt.util.EmptyArray;
-
-/**
- * This class provides debug logging for JSSE provider implementation
- * TODO: Use java.util.logging
- */
-public class Logger {
-
- public static class Stream extends PrintStream {
- private final String prefix;
- private static int indent = 0;
-
- public Stream(String name) {
- super(System.err);
- prefix = name + "["+Thread.currentThread().getName()+"] ";
- }
-
- @Override
- public void print(String msg) {
- for (int i=0; i<indent; i++) {
- super.print(" ");
- }
- super.print(msg);
- }
-
- public void newIndent() {
- indent ++;
- }
-
- public void endIndent() {
- indent --;
- }
-
- @Override
- public void println(String msg) {
- print(prefix);
- super.println(msg);
- }
-
- public void print(byte[] data) {
- printAsHex(16, " ", "", data, 0, data.length);
- }
-
- public void print(byte[] data, int offset, int len) {
- printAsHex(16, " ", "", data, offset, len);
- }
-
- public void printAsHex(int perLine, String prefix, String delimiter, byte[] data) {
- printAsHex(perLine, prefix, delimiter, data, 0, data.length);
- }
-
- public void printAsHex(int perLine, String prefix, String delimiter,
- byte[] data, int offset, int len) {
- StringBuilder line = new StringBuilder();
- for (int i = 0; i < len; i++) {
- line.append(prefix);
- line.append(Byte.toHexString(data[i+offset], false));
- line.append(delimiter);
-
- if (((i+1)%perLine) == 0) {
- super.println(line.toString());
- line = new StringBuilder();
- }
- }
- super.println(line.toString());
- }
- }
-
- private static String[] names;
-
- static {
- try {
- names = System.getProperty("jsse", "").split(",");
- } catch (Exception e) {
- names = EmptyArray.STRING;
- }
- }
-
- public static Stream getStream(String name) {
- for (int i=0; i<names.length; i++) {
- if (names[i].equals(name)) {
- return new Stream(name);
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/org/conscrypt/Message.java b/src/main/java/org/conscrypt/Message.java
deleted file mode 100644
index 3b932d0..0000000
--- a/src/main/java/org/conscrypt/Message.java
+++ /dev/null
@@ -1,70 +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 javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-
-/**
- *
- * Base class for handshake messages
- */
-public abstract class Message {
-
- /*
- * Message length
- */
- protected int length;
-
- /**
- * Returns message type
- */
- abstract int getType();
-
- /**
- * Returns message length
- */
- public int length() {
- return length;
- }
-
- /**
- * Sends message
- * @param out
- */
- abstract void send(HandshakeIODataStream out);
-
- /**
- * Sends fatal alert
- * @param description
- * @param reason
- */
- protected void fatalAlert(byte description, String reason) {
- throw new AlertException(description, new SSLHandshakeException(reason));
- }
-
- /**
- * Sends fatal alert
- * @param description
- * @param reason
- * @param cause
- */
- protected void fatalAlert(byte description, String reason, Throwable cause) {
- throw new AlertException(description, new SSLException(reason, cause));
- }
-}
diff --git a/src/main/java/org/conscrypt/NativeCrypto.java b/src/main/java/org/conscrypt/NativeCrypto.java
index 127d47d..7f85b34 100644
--- a/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/src/main/java/org/conscrypt/NativeCrypto.java
@@ -26,6 +26,9 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
@@ -50,6 +53,9 @@
*/
if ("com.android.org.conscrypt".equals(NativeCrypto.class.getPackage().getName())) {
System.loadLibrary("javacrypto");
+ } else if ("com.google.android.gms.org.conscrypt".equals(NativeCrypto.class.getPackage().getName())) {
+ System.loadLibrary("gmscore");
+ System.loadLibrary("conscrypt_gmscore_jni");
} else {
System.loadLibrary("conscrypt_jni");
}
@@ -109,6 +115,12 @@
public static native long d2i_PUBKEY(byte[] data);
+ public static native long getRSAPrivateKeyWrapper(RSAPrivateKey key, byte[] modulus);
+
+ public static native long getDSAPrivateKeyWrapper(DSAPrivateKey key);
+
+ public static native long getECPrivateKeyWrapper(ECPrivateKey key, long ecGroupRef);
+
public static native long RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
public static native int RSA_size(long pkey);
@@ -143,6 +155,8 @@
*/
public static native byte[][] get_DSA_params(long dsa);
+ public static native void set_DSA_flag_nonce_from_hash(long dsa);
+
public static native byte[] i2d_RSAPublicKey(long rsa);
public static native byte[] i2d_RSAPrivateKey(long rsa);
@@ -151,6 +165,19 @@
public static native byte[] i2d_DSAPrivateKey(long dsa);
+ // --- DH public/private key handling functions ----------------------------
+
+ public static native long EVP_PKEY_new_DH(byte[] p, byte[] g, byte[] pub_key, byte[] priv_key);
+
+ public static native long DH_generate_parameters_ex(int primeBits, long generator);
+
+ public static native void DH_generate_key(long pkeyRef);
+
+ /**
+ * @return array of {p, g, y(pub), x(priv)}
+ */
+ public static native byte[][] get_DH_params(long dh);
+
// --- EC functions --------------------------
/**
@@ -234,6 +261,8 @@
public static native long EC_KEY_get_public_key(long keyRef);
+ public static native void EC_KEY_set_nonce_from_hash(long keyRef, boolean enabled);
+
public static native int ECDH_compute_key(
byte[] out, int outOffset, long publicKeyRef, long privateKeyRef);
@@ -249,43 +278,47 @@
public static native long EVP_MD_CTX_create();
- public static native void EVP_MD_CTX_init(long ctx);
+ public static native void EVP_MD_CTX_init(OpenSSLDigestContext ctx);
public static native void EVP_MD_CTX_destroy(long ctx);
- public static native long EVP_MD_CTX_copy(long ctx);
+ public static native int EVP_MD_CTX_copy(OpenSSLDigestContext dst_ctx,
+ OpenSSLDigestContext src_ctx);
// --- Digest handling functions -------------------------------------------
- public static native long EVP_DigestInit(long evp_md);
+ public static native int EVP_DigestInit(OpenSSLDigestContext ctx, long evp_md);
- public static native void EVP_DigestUpdate(long ctx, byte[] buffer, int offset, int length);
+ public static native void EVP_DigestUpdate(OpenSSLDigestContext ctx, byte[] buffer,
+ int offset, int length);
- public static native int EVP_DigestFinal(long ctx, byte[] hash, int offset);
+ public static native int EVP_DigestFinal(OpenSSLDigestContext ctx, byte[] hash, int offset);
// --- MAC handling functions ----------------------------------------------
- public static native void EVP_DigestSignInit(long evp_md_ctx, long evp_md, long evp_pkey);
+ public static native void EVP_DigestSignInit(OpenSSLDigestContext evp_md_ctx, long evp_md,
+ long evp_pkey);
- public static native void EVP_DigestSignUpdate(long evp_md_ctx, byte[] in);
+ public static native void EVP_DigestSignUpdate(OpenSSLDigestContext evp_md_ctx, byte[] in);
- public static native byte[] EVP_DigestSignFinal(long evp_md_ctx);
+ public static native byte[] EVP_DigestSignFinal(OpenSSLDigestContext evp_md_ctx);
// --- Signature handling functions ----------------------------------------
- public static native long EVP_SignInit(String algorithm);
+ public static native int EVP_SignInit(OpenSSLDigestContext ctx, long evpRef);
- public static native void EVP_SignUpdate(long ctx, byte[] buffer,
+ public static native void EVP_SignUpdate(OpenSSLDigestContext ctx, byte[] buffer,
int offset, int length);
- public static native int EVP_SignFinal(long ctx, byte[] signature, int offset, long key);
+ public static native int EVP_SignFinal(OpenSSLDigestContext ctx, byte[] signature, int offset,
+ long key);
- public static native long EVP_VerifyInit(String algorithm);
+ public static native int EVP_VerifyInit(OpenSSLDigestContext ctx, long evpRef);
- public static native void EVP_VerifyUpdate(long ctx, byte[] buffer,
+ public static native void EVP_VerifyUpdate(OpenSSLDigestContext ctx, byte[] buffer,
int offset, int length);
- public static native int EVP_VerifyFinal(long ctx, byte[] signature,
+ public static native int EVP_VerifyFinal(OpenSSLDigestContext ctx, byte[] signature,
int offset, int length, long key);
@@ -554,7 +587,7 @@
public static native void BIO_write(long bioRef, byte[] buffer, int offset, int length)
throws IOException;
- public static native void BIO_free(long bioRef);
+ public static native void BIO_free_all(long bioRef);
// --- SSL handling --------------------------------------------------------
@@ -718,11 +751,13 @@
// add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", "EXP-KRB5-RC2-CBC-SHA");
// add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", "EXP-KRB5-RC2-CBC-MD5");
- // PSK is Private Shared Key - didn't exist in Froyo's openssl - no JSSE equivalent
- // add(null, "PSK-3DES-EDE-CBC-SHA");
- // add(null, "PSK-AES128-CBC-SHA");
- // add(null, "PSK-AES256-CBC-SHA");
- // add(null, "PSK-RC4-SHA");
+ // Pre-Shared Key (PSK) cipher suites
+ add("TLS_PSK_WITH_3DES_EDE_CBC_SHA", "PSK-3DES-EDE-CBC-SHA");
+ add("TLS_PSK_WITH_AES_128_CBC_SHA", "PSK-AES128-CBC-SHA");
+ add("TLS_PSK_WITH_AES_256_CBC_SHA", "PSK-AES256-CBC-SHA");
+ add("TLS_PSK_WITH_RC4_128_SHA", "PSK-RC4-SHA");
+ add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", "ECDHE-PSK-AES128-CBC-SHA");
+ add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", "ECDHE-PSK-AES256-CBC-SHA");
// Signaling Cipher Suite Value for secure renegotiation handled as special case.
// add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", null);
@@ -758,6 +793,7 @@
public static final long SSL_MODE_SEND_FALLBACK_SCSV = 0x00000200L;
// SSL options from ssl.h
+ public static final long SSL_OP_TLSEXT_PADDING = 0x00000010L;
public static final long SSL_OP_NO_TICKET = 0x00004000L;
public static final long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L;
public static final long SSL_OP_NO_SSLv3 = 0x02000000L;
@@ -779,52 +815,65 @@
public static final byte TLS_CT_RSA_FIXED_ECDH = 65;
public static final byte TLS_CT_ECDSA_FIXED_ECDH = 66;
+ /*
+ * Used in the SSL_get_shutdown and SSL_set_shutdown functions.
+ */
+ public static final int SSL_SENT_SHUTDOWN = 1;
+ public static final int SSL_RECEIVED_SHUTDOWN = 2;
+
public static native long SSL_CTX_new();
- public static String[] getDefaultCipherSuites() {
- // The default list of cipher suites is a trade-off between what we'd like to use and what
- // servers currently support. We strive to be secure enough by default. We thus avoid
- // unacceptably weak suites (e.g., those with bulk cipher secret key shorter than 128 bits),
- // while maintaining the capability to connect to the majority of servers.
- //
- // Cipher suites are listed in preference order (favorite choice first) of the client.
- // However, servers are not required to honor the order. The key rules governing the
- // preference order are:
- // * Prefer Forward Secrecy (i.e., cipher suites that use ECDHE and DHE for key agreement).
- // * Prefer AES-GCM to AES-CBC whose MAC-pad-then-encrypt approach leads to weaknesses
- // (e.g., Lucky 13).
- // * Prefer AES to RC4 whose foundations are a bit shaky. See
- // http://www.isg.rhul.ac.uk/tls/. BEAST and Lucky13 mitigations are enabled.
- // * Prefer 128-bit bulk encryption to 256-bit one, because 128-bit is safe enough while
- // consuming less CPU/time/energy.
- //
- // NOTE: Removing cipher suites from this list needs to be done with caution, because this
- // may prevent apps from connecting to servers they were previously able to connect to.
- return new String[] {
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
- "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
- "TLS_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_RSA_WITH_AES_128_CBC_SHA",
- "TLS_RSA_WITH_AES_256_CBC_SHA",
- "SSL_RSA_WITH_RC4_128_SHA",
- TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- };
- }
+ // IMPLEMENTATION NOTE: The default list of cipher suites is a trade-off between what we'd like
+ // to use and what servers currently support. We strive to be secure enough by default. We thus
+ // avoid unacceptably weak suites (e.g., those with bulk cipher secret key shorter than 128
+ // bits), while maintaining the capability to connect to the majority of servers.
+ //
+ // Cipher suites are listed in preference order (favorite choice first) of the client. However,
+ // servers are not required to honor the order. The key rules governing the preference order
+ // are:
+ // * Prefer Forward Secrecy (i.e., cipher suites that use ECDHE and DHE for key agreement).
+ // * Prefer AES-GCM to AES-CBC whose MAC-pad-then-encrypt approach leads to weaknesses (e.g.,
+ // Lucky 13).
+ // * Prefer AES to RC4 whose foundations are a bit shaky. See http://www.isg.rhul.ac.uk/tls/.
+ // BEAST and Lucky13 mitigations are enabled.
+ // * Prefer 128-bit bulk encryption to 256-bit one, because 128-bit is safe enough while
+ // consuming less CPU/time/energy.
+ //
+ // NOTE: Removing cipher suites from this list needs to be done with caution, because this may
+ // prevent apps from connecting to servers they were previously able to connect to.
+
+ /** X.509 based cipher suites enabled by default (if requested), in preference order. */
+ static final String[] DEFAULT_X509_CIPHER_SUITES = new String[] {
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ };
+
+ /** TLS-PSK cipher suites enabled by default (if requested), in preference order. */
+ static final String[] DEFAULT_PSK_CIPHER_SUITES = new String[] {
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+ "TLS_PSK_WITH_AES_128_CBC_SHA",
+ "TLS_PSK_WITH_AES_256_CBC_SHA",
+ };
public static String[] getSupportedCipherSuites() {
return SUPPORTED_CIPHER_SUITES.clone();
@@ -862,13 +911,19 @@
public static native long SSL_clear_options(long ssl, long options);
- public static String[] getDefaultProtocols() {
- return new String[] { SUPPORTED_PROTOCOL_SSLV3,
- SUPPORTED_PROTOCOL_TLSV1,
- SUPPORTED_PROTOCOL_TLSV1_1,
- SUPPORTED_PROTOCOL_TLSV1_2,
- };
- }
+ public static native void SSL_use_psk_identity_hint(long ssl, String identityHint)
+ throws SSLException;
+
+ public static native void set_SSL_psk_client_callback_enabled(long ssl, boolean enabled);
+
+ public static native void set_SSL_psk_server_callback_enabled(long ssl, boolean enabled);
+
+ public static final String[] DEFAULT_PROTOCOLS = new String[] {
+ SUPPORTED_PROTOCOL_SSLV3,
+ SUPPORTED_PROTOCOL_TLSV1,
+ SUPPORTED_PROTOCOL_TLSV1_1,
+ SUPPORTED_PROTOCOL_TLSV1_2,
+ };
public static String[] getSupportedProtocols() {
return new String[] { SUPPORTED_PROTOCOL_SSLV3,
@@ -1045,6 +1100,10 @@
public static final int SSL_VERIFY_PEER = 0x01;
public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
+ public static native void SSL_set_accept_state(long sslNativePointer);
+
+ public static native void SSL_set_connect_state(long sslNativePointer);
+
public static native void SSL_set_verify(long sslNativePointer, int mode);
public static native void SSL_set_session(long sslNativePointer, long sslSessionNativePointer)
@@ -1080,9 +1139,9 @@
/**
* For clients, sets the list of supported ALPN protocols in wire-format
- * (length-prefixed 8-bit strings) on an SSL context.
+ * (length-prefixed 8-bit strings).
*/
- public static native int SSL_CTX_set_alpn_protos(long sslCtxPointer, byte[] protos);
+ public static native int SSL_set_alpn_protos(long sslPointer, byte[] protos);
/**
* Returns the selected ALPN protocol. If the server did not select a
@@ -1104,6 +1163,20 @@
byte[] alpnProtocols)
throws SSLException, SocketTimeoutException, CertificateException;
+ /**
+ * Returns the sslSessionNativePointer of the negotiated session. If this is
+ * a server negotiation, supplying the {@code alpnProtocols} will enable
+ * ALPN negotiation.
+ */
+ public static native long SSL_do_handshake_bio(long sslNativePointer,
+ long sourceBioRef,
+ long sinkBioRef,
+ SSLHandshakeCallbacks shc,
+ boolean client_mode,
+ byte[] npnProtocols,
+ byte[] alpnProtocols)
+ throws SSLException, SocketTimeoutException, CertificateException;
+
public static native byte[] SSL_get_npn_negotiated_protocol(long sslNativePointer);
/**
@@ -1132,6 +1205,15 @@
byte[] b, int off, int len, int readTimeoutMillis)
throws IOException;
+ public static native int SSL_read_BIO(long sslNativePointer,
+ byte[] dest,
+ int destOffset,
+ int destLength,
+ long sourceBioRef,
+ long sinkBioRef,
+ SSLHandshakeCallbacks shc)
+ throws IOException;
+
/**
* Writes with the native SSL_write function to the encrypted data stream.
*/
@@ -1141,11 +1223,24 @@
byte[] b, int off, int len, int writeTimeoutMillis)
throws IOException;
+ public static native int SSL_write_BIO(long sslNativePointer,
+ byte[] source,
+ int length,
+ long sinkBioRef,
+ SSLHandshakeCallbacks shc)
+ throws IOException;
+
public static native void SSL_interrupt(long sslNativePointer);
public static native void SSL_shutdown(long sslNativePointer,
FileDescriptor fd,
SSLHandshakeCallbacks shc) throws IOException;
+ public static native void SSL_shutdown_BIO(long sslNativePointer,
+ long sourceBioRef, long sinkBioRef,
+ SSLHandshakeCallbacks shc) throws IOException;
+
+ public static native int SSL_get_shutdown(long sslNativePointer);
+
public static native void SSL_free(long sslNativePointer);
public static native byte[] SSL_SESSION_session_id(long sslSessionNativePointer);
@@ -1170,13 +1265,14 @@
/**
* Verify that we trust the certificate chain is trusted.
*
+ * @param sslSessionNativePtr pointer to a reference of the SSL_SESSION
* @param certificateChainRefs chain of X.509 certificate references
* @param authMethod auth algorithm name
*
* @throws CertificateException if the certificate is untrusted
*/
- public void verifyCertificateChain(long[] certificateChainRefs, String authMethod)
- throws CertificateException;
+ public void verifyCertificateChain(long sslSessionNativePtr, long[] certificateChainRefs,
+ String authMethod) throws CertificateException;
/**
* Called on an SSL client when the server requests (or
@@ -1194,12 +1290,78 @@
throws CertificateEncodingException, SSLException;
/**
- * Called when SSL handshake is completed. Note that this can
- * be after SSL_do_handshake returns when handshake cutthrough
- * is enabled.
+ * Gets the key to be used in client mode for this connection in Pre-Shared Key (PSK) key
+ * exchange.
+ *
+ * @param identityHint PSK identity hint provided by the server or {@code null} if no hint
+ * provided.
+ * @param identity buffer to be populated with PSK identity (NULL-terminated modified UTF-8)
+ * by this method. This identity will be provided to the server.
+ * @param key buffer to be populated with key material by this method.
+ *
+ * @return number of bytes this method stored in the {@code key} buffer or {@code 0} if an
+ * error occurred in which case the handshake will be aborted.
*/
- public void handshakeCompleted();
+ public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key);
+
+ /**
+ * Gets the key to be used in server mode for this connection in Pre-Shared Key (PSK) key
+ * exchange.
+ *
+ * @param identityHint PSK identity hint provided by this server to the client or
+ * {@code null} if no hint was provided.
+ * @param identity PSK identity provided by the client.
+ * @param key buffer to be populated with key material by this method.
+ *
+ * @return number of bytes this method stored in the {@code key} buffer or {@code 0} if an
+ * error occurred in which case the handshake will be aborted.
+ */
+ public int serverPSKKeyRequested(String identityHint, String identity, byte[] key);
+
+ /**
+ * Called when SSL state changes. This could be handshake completion.
+ */
+ public void onSSLStateChange(long sslSessionNativePtr, int type, int val);
}
+ // Values used in the SSLHandshakeCallbacks#onSSLStateChange as the {@code type}.
+ public static final int SSL_ST_CONNECT = 0x1000;
+ public static final int SSL_ST_ACCEPT = 0x2000;
+ public static final int SSL_ST_MASK = 0x0FFF;
+ public static final int SSL_ST_INIT = (SSL_ST_CONNECT | SSL_ST_ACCEPT);
+ public static final int SSL_ST_BEFORE = 0x4000;
+ public static final int SSL_ST_OK = 0x03;
+ public static final int SSL_ST_RENEGOTIATE = (0x04 | SSL_ST_INIT);
+
+ public static final int SSL_CB_LOOP = 0x01;
+ public static final int SSL_CB_EXIT = 0x02;
+ public static final int SSL_CB_READ = 0x04;
+ public static final int SSL_CB_WRITE = 0x08;
+ public static final int SSL_CB_ALERT = 0x4000;
+ public static final int SSL_CB_READ_ALERT = (SSL_CB_ALERT | SSL_CB_READ);
+ public static final int SSL_CB_WRITE_ALERT = (SSL_CB_ALERT | SSL_CB_WRITE);
+ public static final int SSL_CB_ACCEPT_LOOP = (SSL_ST_ACCEPT | SSL_CB_LOOP);
+ public static final int SSL_CB_ACCEPT_EXIT = (SSL_ST_ACCEPT | SSL_CB_EXIT);
+ public static final int SSL_CB_CONNECT_LOOP = (SSL_ST_CONNECT | SSL_CB_LOOP);
+ public static final int SSL_CB_CONNECT_EXIT = (SSL_ST_CONNECT | SSL_CB_EXIT);
+ public static final int SSL_CB_HANDSHAKE_START = 0x10;
+ public static final int SSL_CB_HANDSHAKE_DONE = 0x20;
+
+ /*
+ * From ssl/ssl3.h
+ */
+ public static final int SSL3_RT_HEADER_LENGTH = 5;
+ public static final int SSL_RT_MAX_CIPHER_BLOCK_SIZE = 16;
+ public static final int SSL3_RT_MAX_MD_SIZE = 64;
+ public static final int SSL3_RT_MAX_PLAIN_LENGTH = 16384;
+ public static final int SSL3_RT_MAX_ENCRYPTED_OVERHEAD = 256 + SSL3_RT_MAX_MD_SIZE;
+ public static final int SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD = SSL_RT_MAX_CIPHER_BLOCK_SIZE
+ + SSL3_RT_MAX_MD_SIZE;
+ public static final int SSL3_RT_MAX_COMPRESSED_LENGTH = SSL3_RT_MAX_PLAIN_LENGTH;
+ public static final int SSL3_RT_MAX_ENCRYPTED_LENGTH = SSL3_RT_MAX_ENCRYPTED_OVERHEAD
+ + SSL3_RT_MAX_COMPRESSED_LENGTH;
+ public static final int SSL3_RT_MAX_PACKET_SIZE = SSL3_RT_MAX_ENCRYPTED_LENGTH
+ + SSL3_RT_HEADER_LENGTH;
+
public static native long ERR_peek_last_error();
}
diff --git a/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java b/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
index 26971d5..cb5d0ca 100644
--- a/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
+++ b/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
@@ -38,6 +38,10 @@
return ctx;
}
+ public void release() {
+ NativeCrypto.BIO_free_all(ctx);
+ }
+
/**
* Similar to a {@code readLine} method, but matches what OpenSSL expects
* from a {@code BIO_gets} method.
diff --git a/src/main/java/org/conscrypt/OpenSSLBIOSink.java b/src/main/java/org/conscrypt/OpenSSLBIOSink.java
new file mode 100644
index 0000000..e599da9
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLBIOSink.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+
+public final class OpenSSLBIOSink {
+ private final long ctx;
+ private final ByteArrayOutputStream buffer;
+ private int position;
+
+ public static OpenSSLBIOSink create() {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ return new OpenSSLBIOSink(buffer);
+ }
+
+ private OpenSSLBIOSink(ByteArrayOutputStream buffer) {
+ ctx = NativeCrypto.create_BIO_OutputStream(buffer);
+ this.buffer = buffer;
+ }
+
+ public int available() {
+ return buffer.size() - position;
+ }
+
+ public void reset() {
+ buffer.reset();
+ position = 0;
+ }
+
+ public long skip(long byteCount) {
+ int maxLength = Math.min(available(), (int) byteCount);
+ position += maxLength;
+ if (position == buffer.size()) {
+ reset();
+ }
+ return maxLength;
+ }
+
+ public long getContext() {
+ return ctx;
+ }
+
+ public byte[] toByteArray() {
+ return buffer.toByteArray();
+ }
+
+ public int position() {
+ return position;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ NativeCrypto.BIO_free_all(ctx);
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLBIOSource.java b/src/main/java/org/conscrypt/OpenSSLBIOSource.java
new file mode 100644
index 0000000..61e966b
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLBIOSource.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public final class OpenSSLBIOSource {
+ private OpenSSLBIOInputStream source;
+
+ public static OpenSSLBIOSource wrap(ByteBuffer buffer) {
+ return new OpenSSLBIOSource(new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer)));
+ }
+
+ public OpenSSLBIOSource(OpenSSLBIOInputStream source) {
+ this.source = source;
+ }
+
+ public long getContext() {
+ return source.getBioContext();
+ }
+
+ public synchronized void release() {
+ if (source != null) {
+ NativeCrypto.BIO_free_all(source.getBioContext());
+ source = null;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ release();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private static class ByteBufferInputStream extends InputStream {
+ private final ByteBuffer source;
+
+ public ByteBufferInputStream(ByteBuffer source) {
+ this.source = source;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (source.remaining() > 0) {
+ return source.get();
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int available() throws IOException {
+ return source.limit() - source.position();
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ int originalPosition = source.position();
+ source.get(buffer);
+ return source.position() - originalPosition;
+ }
+
+ @Override
+ public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ int toRead = Math.min(source.remaining(), byteCount);
+ int originalPosition = source.position();
+ source.get(buffer, byteOffset, toRead);
+ return source.position() - originalPosition;
+ }
+
+ @Override
+ public void reset() throws IOException {
+ source.reset();
+ }
+
+ @Override
+ public long skip(long byteCount) throws IOException {
+ int originalPosition = source.position();
+ source.position((int) (originalPosition + byteCount));
+ return source.position() - originalPosition;
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLCipher.java b/src/main/java/org/conscrypt/OpenSSLCipher.java
index e2ae8ab..29e2d4d 100644
--- a/src/main/java/org/conscrypt/OpenSSLCipher.java
+++ b/src/main/java/org/conscrypt/OpenSSLCipher.java
@@ -84,6 +84,12 @@
private Padding padding = Padding.PKCS5PADDING;
/**
+ * May be used when reseting the cipher instance after calling
+ * {@code doFinal}.
+ */
+ private byte[] encodedKey;
+
+ /**
* The Initial Vector (IV) used for the current cipher.
*/
private byte[] iv;
@@ -252,8 +258,8 @@
if (encodedKey == null) {
throw new InvalidKeyException("key.getEncoded() == null");
}
-
checkSupportedKeySize(encodedKey.length);
+ this.encodedKey = encodedKey;
final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(encodedKey.length,
mode));
@@ -392,7 +398,7 @@
* Reset this Cipher instance state to process a new chunk of data.
*/
private void reset() {
- NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, null, null, encrypting);
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, encodedKey, iv, encrypting);
calledUpdate = false;
}
diff --git a/src/main/java/org/conscrypt/OpenSSLContextImpl.java b/src/main/java/org/conscrypt/OpenSSLContextImpl.java
index 7a2beab..6cbd19e 100644
--- a/src/main/java/org/conscrypt/OpenSSLContextImpl.java
+++ b/src/main/java/org/conscrypt/OpenSSLContextImpl.java
@@ -115,7 +115,7 @@
}
SSLParametersImpl p = (SSLParametersImpl) sslParameters.clone();
p.setUseClientMode(false);
- return new SSLEngineImpl(host, port, p);
+ return new OpenSSLEngineImpl(host, port, p);
}
@Override
@@ -125,7 +125,7 @@
}
SSLParametersImpl p = (SSLParametersImpl) sslParameters.clone();
p.setUseClientMode(false);
- return new SSLEngineImpl(p);
+ return new OpenSSLEngineImpl(p);
}
@Override
diff --git a/src/main/java/org/conscrypt/OpenSSLDHKeyFactory.java b/src/main/java/org/conscrypt/OpenSSLDHKeyFactory.java
new file mode 100644
index 0000000..40402e6
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLDHKeyFactory.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+public class OpenSSLDHKeyFactory extends KeyFactorySpi {
+
+ @Override
+ protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+ if (keySpec == null) {
+ throw new InvalidKeySpecException("keySpec == null");
+ }
+
+ if (keySpec instanceof DHPublicKeySpec) {
+ return new OpenSSLDHPublicKey((DHPublicKeySpec) keySpec);
+ } else if (keySpec instanceof X509EncodedKeySpec) {
+ return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeCrypto.EVP_PKEY_DH);
+ }
+ throw new InvalidKeySpecException("Must use DHPublicKeySpec or X509EncodedKeySpec; was "
+ + keySpec.getClass().getName());
+ }
+
+ @Override
+ protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+ if (keySpec == null) {
+ throw new InvalidKeySpecException("keySpec == null");
+ }
+
+ if (keySpec instanceof DHPrivateKeySpec) {
+ return new OpenSSLDHPrivateKey((DHPrivateKeySpec) keySpec);
+ } else if (keySpec instanceof PKCS8EncodedKeySpec) {
+ return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
+ NativeCrypto.EVP_PKEY_DH);
+ }
+ throw new InvalidKeySpecException("Must use DHPrivateKeySpec or PKCS8EncodedKeySpec; was "
+ + keySpec.getClass().getName());
+ }
+
+ @Override
+ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+ throws InvalidKeySpecException {
+ if (key == null) {
+ throw new InvalidKeySpecException("key == null");
+ }
+
+ if (keySpec == null) {
+ throw new InvalidKeySpecException("keySpec == null");
+ }
+
+ if (!"DH".equals(key.getAlgorithm())) {
+ throw new InvalidKeySpecException("Key must be a DH key");
+ }
+
+ if (key instanceof DHPublicKey && DHPublicKeySpec.class.isAssignableFrom(keySpec)) {
+ DHPublicKey dhKey = (DHPublicKey) key;
+ DHParameterSpec params = dhKey.getParams();
+ return (T) new DHPublicKeySpec(dhKey.getY(), params.getP(), params.getG());
+ } else if (key instanceof PublicKey && DHPublicKeySpec.class.isAssignableFrom(keySpec)) {
+ final byte[] encoded = key.getEncoded();
+ if (!"X.509".equals(key.getFormat()) || encoded == null) {
+ throw new InvalidKeySpecException("Not a valid X.509 encoding");
+ }
+ DHPublicKey dhKey = (DHPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
+ DHParameterSpec params = dhKey.getParams();
+ return (T) new DHPublicKeySpec(dhKey.getY(), params.getP(), params.getG());
+ } else if (key instanceof DHPrivateKey && DHPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+ DHPrivateKey dhKey = (DHPrivateKey) key;
+ DHParameterSpec params = dhKey.getParams();
+ return (T) new DHPrivateKeySpec(dhKey.getX(), params.getP(), params.getG());
+ } else if (key instanceof PrivateKey && DHPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+ final byte[] encoded = key.getEncoded();
+ if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+ throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
+ }
+ DHPrivateKey dhKey = (DHPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(
+ encoded));
+ DHParameterSpec params = dhKey.getParams();
+ return (T) new DHPrivateKeySpec(dhKey.getX(), params.getP(), params.getG());
+ } else if (key instanceof PrivateKey
+ && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+ final byte[] encoded = key.getEncoded();
+ if (!"PKCS#8".equals(key.getFormat())) {
+ throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
+ + key.getFormat());
+ } else if (encoded == null) {
+ throw new InvalidKeySpecException("Key is not encodable");
+ }
+ return (T) new PKCS8EncodedKeySpec(encoded);
+ } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+ final byte[] encoded = key.getEncoded();
+ if (!"X.509".equals(key.getFormat())) {
+ throw new InvalidKeySpecException("Encoding type must be X.509; was "
+ + key.getFormat());
+ } else if (encoded == null) {
+ throw new InvalidKeySpecException("Key is not encodable");
+ }
+ return (T) new X509EncodedKeySpec(encoded);
+ } else {
+ throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
+ + key.getClass().getName() + ", keySpec=" + keySpec.getName());
+ }
+ }
+
+ @Override
+ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ }
+ if ((key instanceof OpenSSLDHPublicKey) || (key instanceof OpenSSLDHPrivateKey)) {
+ return key;
+ } else if (key instanceof DHPublicKey) {
+ DHPublicKey dhKey = (DHPublicKey) key;
+
+ BigInteger y = dhKey.getY();
+
+ DHParameterSpec params = dhKey.getParams();
+ BigInteger p = params.getP();
+ BigInteger g = params.getG();
+
+ try {
+ return engineGeneratePublic(new DHPublicKeySpec(y, p, g));
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ } else if (key instanceof DHPrivateKey) {
+ DHPrivateKey dhKey = (DHPrivateKey) key;
+
+ BigInteger x = dhKey.getX();
+
+ DHParameterSpec params = dhKey.getParams();
+ BigInteger p = params.getP();
+ BigInteger g = params.getG();
+
+ try {
+ return engineGeneratePrivate(new DHPrivateKeySpec(x, p, g));
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ } else if ((key instanceof PrivateKey) && ("PKCS#8".equals(key.getFormat()))) {
+ byte[] encoded = key.getEncoded();
+ if (encoded == null) {
+ throw new InvalidKeyException("Key does not support encoding");
+ }
+ try {
+ return engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ } else if ((key instanceof PublicKey) && ("X.509".equals(key.getFormat()))) {
+ byte[] encoded = key.getEncoded();
+ if (encoded == null) {
+ throw new InvalidKeyException("Key does not support encoding");
+ }
+ try {
+ return engineGeneratePublic(new X509EncodedKeySpec(encoded));
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ } else {
+ throw new InvalidKeyException("Key must be DH public or private key; was "
+ + key.getClass().getName());
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLDHKeyPairGenerator.java b/src/main/java/org/conscrypt/OpenSSLDHKeyPairGenerator.java
new file mode 100644
index 0000000..142ef0b
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLDHKeyPairGenerator.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 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.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.spec.DHParameterSpec;
+
+public class OpenSSLDHKeyPairGenerator extends KeyPairGeneratorSpi {
+
+ /** The safe prime to use for the generated DH key pair. */
+ private BigInteger prime;
+
+ /** If {@code prime} is unspecified, this is the size of the generated prime. */
+ private int primeBits = 1024;
+
+ private static final BigInteger DEFAULT_GENERATOR = BigInteger.valueOf(2);
+
+ private BigInteger generator = DEFAULT_GENERATOR;
+
+ @Override
+ public KeyPair generateKeyPair() {
+ final OpenSSLKey key;
+ if (prime != null) {
+ key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(prime.toByteArray(),
+ generator.toByteArray(), null, null));
+ } else {
+ key = new OpenSSLKey(NativeCrypto.DH_generate_parameters_ex(primeBits,
+ generator.longValue()));
+ }
+
+ NativeCrypto.DH_generate_key(key.getPkeyContext());
+
+ final OpenSSLDHPrivateKey privKey = new OpenSSLDHPrivateKey(key);
+ final OpenSSLDHPublicKey pubKey = new OpenSSLDHPublicKey(key);
+
+ return new KeyPair(pubKey, privKey);
+ }
+
+ @Override
+ public void initialize(int keysize, SecureRandom random) {
+ prime = null;
+ primeBits = keysize;
+ generator = DEFAULT_GENERATOR;
+ }
+
+ @Override
+ public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ prime = null;
+ primeBits = 1024;
+ generator = DEFAULT_GENERATOR;
+
+ if (params instanceof DHParameterSpec) {
+ DHParameterSpec dhParams = (DHParameterSpec) params;
+
+ prime = dhParams.getP();
+ BigInteger gen = dhParams.getG();
+ if (gen != null) {
+ generator = gen;
+ }
+ } else if (params != null) {
+ throw new InvalidAlgorithmParameterException("Params must be DHParameterSpec");
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLDHPrivateKey.java b/src/main/java/org/conscrypt/OpenSSLDHPrivateKey.java
new file mode 100644
index 0000000..b138f77
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLDHPrivateKey.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPrivateKeySpec;
+
+public class OpenSSLDHPrivateKey implements DHPrivateKey, OpenSSLKeyHolder {
+ private static final long serialVersionUID = -7321023036951606638L;
+
+ private transient OpenSSLKey key;
+
+ /** base prime */
+ private transient byte[] p;
+
+ /** generator */
+ private transient byte[] g;
+
+ /** private key */
+ private transient byte[] x;
+
+ private transient Object mParamsLock = new Object();
+
+ private transient boolean readParams;
+
+ OpenSSLDHPrivateKey(OpenSSLKey key) {
+ this.key = key;
+ }
+
+ @Override
+ public OpenSSLKey getOpenSSLKey() {
+ return key;
+ }
+
+ OpenSSLDHPrivateKey(DHPrivateKeySpec dhKeySpec) throws InvalidKeySpecException {
+ try {
+ key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
+ dhKeySpec.getP().toByteArray(),
+ dhKeySpec.getG().toByteArray(),
+ null,
+ dhKeySpec.getX().toByteArray()));
+ } catch (Exception e) {
+ throw new InvalidKeySpecException(e);
+ }
+ }
+
+ private void ensureReadParams() {
+ synchronized (mParamsLock) {
+ if (readParams) {
+ return;
+ }
+
+ byte[][] params = NativeCrypto.get_DH_params(key.getPkeyContext());
+
+ p = params[0];
+ g = params[1];
+ x = params[3];
+
+ readParams = true;
+ }
+ }
+
+ static OpenSSLKey getInstance(DHPrivateKey dhPrivateKey) throws InvalidKeyException {
+ try {
+ DHParameterSpec dhParams = dhPrivateKey.getParams();
+ return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
+ dhParams.getP().toByteArray(),
+ dhParams.getG().toByteArray(),
+ null,
+ dhPrivateKey.getX().toByteArray()));
+ } catch (Exception e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return "DH";
+ }
+
+ @Override
+ public String getFormat() {
+ /*
+ * If we're using an OpenSSL ENGINE, there's no guarantee we can export
+ * the key. Returning {@code null} tells the caller that there's no
+ * encoded format.
+ */
+ if (key.isEngineBased()) {
+ return null;
+ }
+
+ return "PKCS#8";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ /*
+ * If we're using an OpenSSL ENGINE, there's no guarantee we can export
+ * the key. Returning {@code null} tells the caller that there's no
+ * encoded format.
+ */
+ if (key.isEngineBased()) {
+ return null;
+ }
+
+ return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
+ }
+
+ @Override
+ public DHParameterSpec getParams() {
+ ensureReadParams();
+ return new DHParameterSpec(new BigInteger(p), new BigInteger(g));
+ }
+
+ @Override
+ public BigInteger getX() {
+ if (key.isEngineBased()) {
+ throw new UnsupportedOperationException("private key value X cannot be extracted");
+ }
+
+ ensureReadParams();
+ return new BigInteger(x);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (o instanceof OpenSSLDHPrivateKey) {
+ OpenSSLDHPrivateKey other = (OpenSSLDHPrivateKey) o;
+
+ /*
+ * We can shortcut the true case, but it still may be equivalent but
+ * different copies.
+ */
+ if (key.equals(other.getOpenSSLKey())) {
+ return true;
+ }
+ }
+
+ if (!(o instanceof DHPrivateKey)) {
+ return false;
+ }
+
+ ensureReadParams();
+
+ final DHPrivateKey other = (DHPrivateKey) o;
+ if (!x.equals(other.getX())) {
+ return false;
+ }
+
+ DHParameterSpec spec = other.getParams();
+ return g.equals(spec.getG()) && p.equals(spec.getP());
+ }
+
+ @Override
+ public int hashCode() {
+ ensureReadParams();
+ int hash = 1;
+ if (!key.isEngineBased()) {
+ hash = hash * 3 + x.hashCode();
+ }
+ hash = hash * 7 + p.hashCode();
+ hash = hash * 13 + g.hashCode();
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("OpenSSLDHPrivateKey{");
+
+ if (key.isEngineBased()) {
+ sb.append("key=");
+ sb.append(key);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ ensureReadParams();
+ sb.append("X=");
+ sb.append(new BigInteger(x).toString(16));
+ sb.append(',');
+ sb.append("P=");
+ sb.append(new BigInteger(p).toString(16));
+ sb.append(',');
+ sb.append("G=");
+ sb.append(new BigInteger(g).toString(16));
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+
+ final BigInteger g = (BigInteger) stream.readObject();
+ final BigInteger p = (BigInteger) stream.readObject();
+ final BigInteger x = (BigInteger) stream.readObject();
+
+ key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
+ p.toByteArray(),
+ g.toByteArray(),
+ null,
+ x.toByteArray()));
+ mParamsLock = new Object();
+ }
+
+ private void writeObject(ObjectOutputStream stream) throws IOException {
+ if (getOpenSSLKey().isEngineBased()) {
+ throw new NotSerializableException("engine-based keys can not be serialized");
+ }
+
+ stream.defaultWriteObject();
+
+ ensureReadParams();
+ stream.writeObject(new BigInteger(g));
+ stream.writeObject(new BigInteger(p));
+ stream.writeObject(new BigInteger(x));
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLDHPublicKey.java b/src/main/java/org/conscrypt/OpenSSLDHPublicKey.java
new file mode 100644
index 0000000..7d0a6bc
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLDHPublicKey.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+public class OpenSSLDHPublicKey implements DHPublicKey, OpenSSLKeyHolder {
+ private static final long serialVersionUID = 6123717708079837723L;
+
+ private transient OpenSSLKey key;
+
+ /** base prime */
+ private transient byte[] p;
+
+ /** generator */
+ private transient byte[] g;
+
+ /** public key */
+ private transient byte[] y;
+
+ private transient final Object mParamsLock = new Object();
+
+ private transient boolean readParams;
+
+ OpenSSLDHPublicKey(OpenSSLKey key) {
+ this.key = key;
+ }
+
+ @Override
+ public OpenSSLKey getOpenSSLKey() {
+ return key;
+ }
+
+ OpenSSLDHPublicKey(DHPublicKeySpec dsaKeySpec) throws InvalidKeySpecException {
+ try {
+ key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
+ dsaKeySpec.getP().toByteArray(),
+ dsaKeySpec.getG().toByteArray(),
+ dsaKeySpec.getY().toByteArray(),
+ null));
+ } catch (Exception e) {
+ throw new InvalidKeySpecException(e);
+ }
+ }
+
+ private void ensureReadParams() {
+ synchronized (mParamsLock) {
+ if (readParams) {
+ return;
+ }
+
+ byte[][] params = NativeCrypto.get_DH_params(key.getPkeyContext());
+
+ p = params[0];
+ g = params[1];
+ y = params[2];
+
+ readParams = true;
+ }
+ }
+
+ static OpenSSLKey getInstance(DHPublicKey DHPublicKey) throws InvalidKeyException {
+ try {
+ final DHParameterSpec dhParams = DHPublicKey.getParams();
+ return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
+ dhParams.getP().toByteArray(),
+ dhParams.getG().toByteArray(),
+ DHPublicKey.getY().toByteArray(),
+ null));
+ } catch (Exception e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ @Override
+ public DHParameterSpec getParams() {
+ ensureReadParams();
+ return new DHParameterSpec(new BigInteger(p), new BigInteger(g));
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return "DH";
+ }
+
+ @Override
+ public String getFormat() {
+ return "X.509";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return NativeCrypto.i2d_PUBKEY(key.getPkeyContext());
+ }
+
+ @Override
+ public BigInteger getY() {
+ ensureReadParams();
+ return new BigInteger(y);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (o instanceof OpenSSLDHPublicKey) {
+ OpenSSLDHPublicKey other = (OpenSSLDHPublicKey) o;
+
+ /*
+ * We can shortcut the true case, but it still may be equivalent but
+ * different copies.
+ */
+ if (key.equals(other.getOpenSSLKey())) {
+ return true;
+ }
+ }
+
+ if (!(o instanceof DHPublicKey)) {
+ return false;
+ }
+
+ ensureReadParams();
+
+ final DHPublicKey other = (DHPublicKey) o;
+ if (!y.equals(other.getY())) {
+ return false;
+ }
+
+ DHParameterSpec spec = other.getParams();
+ return g.equals(spec.getG()) && p.equals(spec.getP());
+ }
+
+ @Override
+ public int hashCode() {
+ ensureReadParams();
+ int hash = 1;
+ hash = hash * 3 + y.hashCode();
+ hash = hash * 7 + p.hashCode();
+ hash = hash * 13 + g.hashCode();
+ return hash;
+ }
+
+
+ @Override
+ public String toString() {
+ ensureReadParams();
+
+ final StringBuilder sb = new StringBuilder("OpenSSLDHPublicKey{");
+ sb.append("Y=");
+ sb.append(new BigInteger(y).toString(16));
+ sb.append(',');
+ sb.append("P=");
+ sb.append(new BigInteger(p).toString(16));
+ sb.append(',');
+ sb.append("G=");
+ sb.append(new BigInteger(g).toString(16));
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+
+ final BigInteger g = (BigInteger) stream.readObject();
+ final BigInteger p = (BigInteger) stream.readObject();
+ final BigInteger y = (BigInteger) stream.readObject();
+
+ key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
+ p.toByteArray(),
+ g.toByteArray(),
+ y.toByteArray(),
+ null));
+ }
+
+ private void writeObject(ObjectOutputStream stream) throws IOException {
+ stream.defaultWriteObject();
+
+ ensureReadParams();
+ stream.writeObject(new BigInteger(g));
+ stream.writeObject(new BigInteger(p));
+ stream.writeObject(new BigInteger(y));
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLDSAPrivateKey.java b/src/main/java/org/conscrypt/OpenSSLDSAPrivateKey.java
index 0fc4db7..dc3df76 100644
--- a/src/main/java/org/conscrypt/OpenSSLDSAPrivateKey.java
+++ b/src/main/java/org/conscrypt/OpenSSLDSAPrivateKey.java
@@ -63,6 +63,14 @@
}
static OpenSSLKey getInstance(DSAPrivateKey dsaPrivateKey) throws InvalidKeyException {
+ /**
+ * If the key is not encodable (PKCS11-like key), then wrap it and use
+ * JNI upcalls to satisfy requests.
+ */
+ if (dsaPrivateKey.getFormat() == null) {
+ return wrapPlatformKey(dsaPrivateKey);
+ }
+
try {
DSAParams dsaParams = dsaPrivateKey.getParams();
return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
@@ -76,6 +84,10 @@
}
}
+ public static OpenSSLKey wrapPlatformKey(DSAPrivateKey dsaPrivateKey) {
+ return new OpenSSLKey(NativeCrypto.getDSAPrivateKeyWrapper(dsaPrivateKey));
+ }
+
@Override
public DSAParams getParams() {
ensureReadParams();
diff --git a/src/main/java/org/conscrypt/OpenSSLDigestContext.java b/src/main/java/org/conscrypt/OpenSSLDigestContext.java
index b11a862..914f9aa 100644
--- a/src/main/java/org/conscrypt/OpenSSLDigestContext.java
+++ b/src/main/java/org/conscrypt/OpenSSLDigestContext.java
@@ -16,15 +16,9 @@
package org.conscrypt;
-public class OpenSSLDigestContext {
- private final long context;
-
+public class OpenSSLDigestContext extends OpenSSLNativeReference {
public OpenSSLDigestContext(long ctx) {
- if (ctx == 0) {
- throw new NullPointerException("ctx == 0");
- }
-
- this.context = ctx;
+ super(ctx);
}
@Override
@@ -35,8 +29,4 @@
super.finalize();
}
}
-
- long getContext() {
- return context;
- }
}
diff --git a/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java b/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java
index 0d8729c..4361fd2 100644
--- a/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java
+++ b/src/main/java/org/conscrypt/OpenSSLECDHKeyAgreement.java
@@ -61,7 +61,7 @@
if (!(key instanceof PublicKey)) {
throw new InvalidKeyException("Not a public key: " + key.getClass());
}
- OpenSSLKey openSslPublicKey = translateKeyToEcOpenSSLKey(key);
+ OpenSSLKey openSslPublicKey = OpenSSLKey.fromPublicKey((PublicKey) key);
byte[] buffer = new byte[mExpectedResultLength];
int actualResultLength = NativeCrypto.ECDH_compute_key(
@@ -124,7 +124,7 @@
throw new InvalidKeyException("Not a private key: " + key.getClass());
}
- OpenSSLKey openSslKey = translateKeyToEcOpenSSLKey(key);
+ OpenSSLKey openSslKey = OpenSSLKey.fromPrivateKey((PrivateKey) key);
int fieldSizeBits = NativeCrypto.EC_GROUP_get_degree(NativeCrypto.EC_KEY_get0_group(
openSslKey.getPkeyContext()));
mExpectedResultLength = (fieldSizeBits + 7) / 8;
@@ -146,13 +146,4 @@
throw new IllegalStateException("Key agreement not completed");
}
}
-
- private static OpenSSLKey translateKeyToEcOpenSSLKey(Key key) throws InvalidKeyException {
- try {
- return ((OpenSSLKeyHolder) KeyFactory.getInstance(
- "EC", OpenSSLProvider.PROVIDER_NAME).translateKey(key)).getOpenSSLKey();
- } catch (Exception e) {
- throw new InvalidKeyException("Failed to translate key to OpenSSL EC key", e);
- }
- }
}
diff --git a/src/main/java/org/conscrypt/OpenSSLECGroupContext.java b/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
index f7f52a6..ce7aad8 100644
--- a/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
+++ b/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
@@ -112,7 +112,7 @@
public static OpenSSLECGroupContext getInstance(ECParameterSpec params)
throws InvalidAlgorithmParameterException {
- final String curveName = params.getCurveName();
+ final String curveName = Platform.getCurveName(params);
if (curveName != null) {
return OpenSSLECGroupContext.getCurveByName(curveName);
}
@@ -166,6 +166,8 @@
final BigInteger order = new BigInteger(NativeCrypto.EC_GROUP_get_order(groupCtx));
final BigInteger cofactor = new BigInteger(NativeCrypto.EC_GROUP_get_cofactor(groupCtx));
- return new ECParameterSpec(curve, generator, order, cofactor.intValue(), curveName);
+ ECParameterSpec spec = new ECParameterSpec(curve, generator, order, cofactor.intValue());
+ Platform.setCurveName(spec, curveName);
+ return spec;
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java b/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
index 4010ec5..e439985 100644
--- a/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
+++ b/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
@@ -21,6 +21,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECParameterSpec;
@@ -59,10 +60,34 @@
}
}
+ public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
+ OpenSSLECGroupContext group;
+ try {
+ group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException("Unknown group parameters", e);
+ }
+ return wrapPlatformKey(ecPrivateKey, group);
+ }
+
+ private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey, OpenSSLECGroupContext group)
+ throws InvalidKeyException {
+ return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey, group.getContext()));
+ }
+
public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
try {
OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey
.getParams());
+
+ /**
+ * If the key is not encodable (PKCS11-like key), then wrap it and
+ * use JNI upcalls to satisfy requests.
+ */
+ if (ecPrivateKey.getFormat() == null) {
+ return wrapPlatformKey(ecPrivateKey, group);
+ }
+
final BigInteger privKey = ecPrivateKey.getS();
return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0,
privKey.toByteArray()));
diff --git a/src/main/java/org/conscrypt/OpenSSLEngineImpl.java b/src/main/java/org/conscrypt/OpenSSLEngineImpl.java
new file mode 100644
index 0000000..34d231f
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLEngineImpl.java
@@ -0,0 +1,754 @@
+/*
+ * Copyright 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.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces.
+ */
+public class OpenSSLEngineImpl extends SSLEngine implements NativeCrypto.SSLHandshakeCallbacks,
+ SSLParametersImpl.AliasChooser, SSLParametersImpl.PSKCallbacks {
+ private final SSLParametersImpl sslParameters;
+
+ /**
+ * Protects handshakeStarted and handshakeCompleted.
+ */
+ private final Object stateLock = new Object();
+
+ private static 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,
+ /**
+ * {@link #beginHandshake()} has been called at least once.
+ */
+ HANDSHAKE_WANTED,
+ /**
+ * 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;
+
+ /**
+ * Protected by synchronizing on stateLock. Starts as 0, set by
+ * startHandshake, reset to 0 on close.
+ */
+ // @GuardedBy("stateLock");
+ private long sslNativePointer;
+
+ /** Used during handshake when {@link #wrap(ByteBuffer, ByteBuffer)} is called. */
+ // TODO: make this use something similar to BIO_s_null() in native code
+ private static OpenSSLBIOSource nullSource = OpenSSLBIOSource.wrap(ByteBuffer.allocate(0));
+
+ /** A BIO sink written to only during handshakes. */
+ private OpenSSLBIOSink handshakeSink;
+
+ /** A BIO sink written to during regular operation. */
+ private final OpenSSLBIOSink localToRemoteSink = OpenSSLBIOSink.create();
+
+ /** Set during startHandshake. */
+ private OpenSSLSessionImpl sslSession;
+
+ /** Used during handshake callbacks. */
+ private OpenSSLSessionImpl handshakeSession;
+
+ /**
+ * Private key for the TLS Channel ID extension. This field is client-side
+ * only. Set during startHandshake.
+ */
+ OpenSSLKey channelIdPrivateKey;
+
+ public OpenSSLEngineImpl(SSLParametersImpl sslParameters) {
+ this.sslParameters = sslParameters;
+ }
+
+ public OpenSSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) {
+ super(host, port);
+ this.sslParameters = sslParameters;
+ }
+
+ @Override
+ public void beginHandshake() throws SSLException {
+ synchronized (stateLock) {
+ if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND
+ || engineState == EngineState.CLOSED_INBOUND) {
+ throw new IllegalStateException("Engine has already been closed");
+ }
+ if (engineState == EngineState.HANDSHAKE_STARTED) {
+ throw new IllegalStateException("Handshake has already been started");
+ }
+ if (engineState != EngineState.MODE_SET) {
+ throw new IllegalStateException("Client/server mode must be set before handshake");
+ }
+ if (getUseClientMode()) {
+ engineState = EngineState.HANDSHAKE_WANTED;
+ } else {
+ engineState = EngineState.HANDSHAKE_STARTED;
+ }
+ }
+
+ boolean releaseResources = true;
+ try {
+ final AbstractSessionContext sessionContext = sslParameters.getSessionContext();
+ final long sslCtxNativePointer = sessionContext.sslCtxNativePointer;
+ sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
+ sslParameters.setSSLParameters(sslCtxNativePointer, sslNativePointer, this, this,
+ getPeerHost());
+ sslParameters.setCertificateValidation(sslNativePointer);
+ sslParameters.setTlsChannelId(sslNativePointer, channelIdPrivateKey);
+ if (getUseClientMode()) {
+ NativeCrypto.SSL_set_connect_state(sslNativePointer);
+ } else {
+ NativeCrypto.SSL_set_accept_state(sslNativePointer);
+ }
+ handshakeSink = OpenSSLBIOSink.create();
+ releaseResources = false;
+ } catch (IOException e) {
+ // Write CCS errors to EventLog
+ 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", getPeerHost());
+ Platform.logEvent(logMessage);
+ }
+ throw new SSLException(e);
+ } finally {
+ if (releaseResources) {
+ synchronized (stateLock) {
+ engineState = EngineState.CLOSED;
+ }
+ shutdownAndFreeSslNative();
+ }
+ }
+ }
+
+ @Override
+ public void closeInbound() throws SSLException {
+ synchronized (stateLock) {
+ if (engineState == EngineState.CLOSED) {
+ return;
+ }
+ if (engineState == EngineState.CLOSED_OUTBOUND) {
+ engineState = EngineState.CLOSED;
+ } else {
+ engineState = EngineState.CLOSED_INBOUND;
+ }
+ }
+ // TODO anything else to notify OpenSSL layer?
+ }
+
+ @Override
+ public void closeOutbound() {
+ synchronized (stateLock) {
+ if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) {
+ return;
+ }
+ if (engineState != EngineState.MODE_SET && engineState != EngineState.NEW) {
+ shutdownAndFreeSslNative();
+ }
+ if (engineState == EngineState.CLOSED_INBOUND) {
+ engineState = EngineState.CLOSED;
+ } else {
+ engineState = EngineState.CLOSED_OUTBOUND;
+ }
+ }
+ shutdown();
+ }
+
+ @Override
+ public Runnable getDelegatedTask() {
+ /* This implementation doesn't use any delegated tasks. */
+ return null;
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ return sslParameters.getEnabledCipherSuites();
+ }
+
+ @Override
+ public String[] getEnabledProtocols() {
+ return sslParameters.getEnabledProtocols();
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ return sslParameters.getEnableSessionCreation();
+ }
+
+ @Override
+ public HandshakeStatus getHandshakeStatus() {
+ synchronized (stateLock) {
+ switch (engineState) {
+ case HANDSHAKE_WANTED:
+ if (getUseClientMode()) {
+ return HandshakeStatus.NEED_WRAP;
+ } else {
+ return HandshakeStatus.NEED_UNWRAP;
+ }
+ case HANDSHAKE_STARTED:
+ if (handshakeSink.available() > 0) {
+ return HandshakeStatus.NEED_WRAP;
+ } else {
+ return HandshakeStatus.NEED_UNWRAP;
+ }
+ case HANDSHAKE_COMPLETED:
+ if (handshakeSink.available() == 0) {
+ handshakeSink = null;
+ engineState = EngineState.READY;
+ return HandshakeStatus.FINISHED;
+ } else {
+ return HandshakeStatus.NEED_WRAP;
+ }
+ case NEW:
+ case MODE_SET:
+ case CLOSED:
+ case CLOSED_INBOUND:
+ case CLOSED_OUTBOUND:
+ case READY:
+ case READY_HANDSHAKE_CUT_THROUGH:
+ return HandshakeStatus.NOT_HANDSHAKING;
+ default:
+ break;
+ }
+ throw new IllegalStateException("Unexpected engine state: " + engineState);
+ }
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ return sslParameters.getNeedClientAuth();
+ }
+
+ @Override
+ public SSLSession getSession() {
+ if (sslSession == null) {
+ return SSLNullSession.getNullSession();
+ }
+ return sslSession;
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return NativeCrypto.getSupportedCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedProtocols() {
+ return NativeCrypto.getSupportedProtocols();
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ return sslParameters.getUseClientMode();
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ return sslParameters.getWantClientAuth();
+ }
+
+ @Override
+ public boolean isInboundDone() {
+ if (sslNativePointer == 0) {
+ synchronized (stateLock) {
+ return engineState == EngineState.CLOSED
+ || engineState == EngineState.CLOSED_INBOUND;
+ }
+ }
+ return (NativeCrypto.SSL_get_shutdown(sslNativePointer)
+ & NativeCrypto.SSL_RECEIVED_SHUTDOWN) != 0;
+ }
+
+ @Override
+ public boolean isOutboundDone() {
+ if (sslNativePointer == 0) {
+ synchronized (stateLock) {
+ return engineState == EngineState.CLOSED
+ || engineState == EngineState.CLOSED_OUTBOUND;
+ }
+ }
+ return (NativeCrypto.SSL_get_shutdown(sslNativePointer)
+ & NativeCrypto.SSL_SENT_SHUTDOWN) != 0;
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] suites) {
+ sslParameters.setEnabledCipherSuites(suites);
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ sslParameters.setEnabledProtocols(protocols);
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean flag) {
+ sslParameters.setEnableSessionCreation(flag);
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean need) {
+ sslParameters.setNeedClientAuth(need);
+ }
+
+ @Override
+ public void setUseClientMode(boolean mode) {
+ synchronized (stateLock) {
+ if (engineState != EngineState.MODE_SET && engineState != EngineState.NEW) {
+ throw new IllegalArgumentException(
+ "Can not change mode after handshake: engineState == " + engineState);
+ }
+ engineState = EngineState.MODE_SET;
+ }
+ sslParameters.setUseClientMode(mode);
+ }
+
+ @Override
+ public void setWantClientAuth(boolean want) {
+ sslParameters.setWantClientAuth(want);
+ }
+
+ private static void checkIndex(int length, int offset, int count) {
+ if (offset < 0) {
+ throw new IndexOutOfBoundsException("offset < 0");
+ } else if (count < 0) {
+ throw new IndexOutOfBoundsException("count < 0");
+ } else if (offset > length) {
+ throw new IndexOutOfBoundsException("offset > length");
+ } else if (offset > length - count) {
+ throw new IndexOutOfBoundsException("offset + count > length");
+ }
+ }
+
+ @Override
+ public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length)
+ throws SSLException {
+ if (src == null) {
+ throw new IllegalArgumentException("src == null");
+ } else if (dsts == null) {
+ throw new IllegalArgumentException("dsts == null");
+ }
+ checkIndex(dsts.length, offset, length);
+ int dstRemaining = 0;
+ for (int i = 0; i < dsts.length; i++) {
+ ByteBuffer dst = dsts[i];
+ if (dst == null) {
+ throw new IllegalArgumentException("one of the dst == null");
+ } else if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+ if (i >= offset && i < offset + length) {
+ dstRemaining += dst.remaining();
+ }
+ }
+
+ synchronized (stateLock) {
+ // If the inbound direction is closed. we can't send anymore.
+ if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_INBOUND) {
+ return new SSLEngineResult(Status.CLOSED, getHandshakeStatus(), 0, 0);
+ }
+ if (engineState == EngineState.NEW || engineState == EngineState.MODE_SET) {
+ beginHandshake();
+ }
+ }
+
+ // If we haven't completed the handshake yet, just let the caller know.
+ HandshakeStatus handshakeStatus = getHandshakeStatus();
+ if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) {
+ OpenSSLBIOSource source = OpenSSLBIOSource.wrap(src);
+ long sslSessionCtx = 0L;
+ try {
+ sslSessionCtx = NativeCrypto.SSL_do_handshake_bio(sslNativePointer,
+ source.getContext(), handshakeSink.getContext(), this, getUseClientMode(),
+ sslParameters.npnProtocols, sslParameters.alpnProtocols);
+ if (sslSessionCtx != 0) {
+ if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) {
+ engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH;
+ }
+ sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession,
+ getPeerHost(), getPeerPort(), true);
+ }
+ int bytesWritten = handshakeSink.position();
+ return new SSLEngineResult(Status.OK, getHandshakeStatus(), 0, bytesWritten);
+ } catch (Exception e) {
+ throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed")
+ .initCause(e);
+ } finally {
+ if (sslSession == null && sslSessionCtx != 0) {
+ NativeCrypto.SSL_SESSION_free(sslSessionCtx);
+ }
+ source.release();
+ }
+ } else if (handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) {
+ return new SSLEngineResult(Status.OK, handshakeStatus, 0, 0);
+ }
+
+ if (dstRemaining == 0) {
+ return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
+ }
+
+ ByteBuffer srcDuplicate = src.duplicate();
+ OpenSSLBIOSource source = OpenSSLBIOSource.wrap(srcDuplicate);
+ try {
+ int positionBeforeRead = srcDuplicate.position();
+ int produced = 0;
+ boolean shouldStop = false;
+
+ while (!shouldStop) {
+ ByteBuffer dst = getNextAvailableByteBuffer(dsts, offset, length);
+ if (dst == null) {
+ shouldStop = true;
+ continue;
+ }
+ ByteBuffer arrayDst = dst;
+ if (dst.isDirect()) {
+ arrayDst = ByteBuffer.allocate(dst.remaining());
+ }
+
+ int dstOffset = arrayDst.arrayOffset() + arrayDst.position();
+
+ int internalProduced = NativeCrypto.SSL_read_BIO(sslNativePointer,
+ arrayDst.array(), dstOffset, dst.remaining(), source.getContext(),
+ localToRemoteSink.getContext(), this);
+ if (internalProduced <= 0) {
+ shouldStop = true;
+ continue;
+ }
+ arrayDst.position(arrayDst.position() + internalProduced);
+ produced += internalProduced;
+ if (dst != arrayDst) {
+ arrayDst.flip();
+ dst.put(arrayDst);
+ }
+ }
+
+ int consumed = srcDuplicate.position() - positionBeforeRead;
+ src.position(srcDuplicate.position());
+ return new SSLEngineResult(Status.OK, getHandshakeStatus(), consumed, produced);
+ } catch (IOException e) {
+ throw new SSLException(e);
+ } finally {
+ source.release();
+ }
+ }
+
+ /** Returns the next non-empty ByteBuffer. */
+ private ByteBuffer getNextAvailableByteBuffer(ByteBuffer[] buffers, int offset, int length) {
+ for (int i = offset; i < length; ++i) {
+ if (buffers[i].remaining() > 0) {
+ return buffers[i];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst)
+ throws SSLException {
+ if (srcs == null) {
+ throw new IllegalArgumentException("srcs == null");
+ } else if (dst == null) {
+ throw new IllegalArgumentException("dst == null");
+ } else if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+ for (ByteBuffer src : srcs) {
+ if (src == null) {
+ throw new IllegalArgumentException("one of the src == null");
+ }
+ }
+ checkIndex(srcs.length, offset, length);
+
+ if (dst.remaining() < NativeCrypto.SSL3_RT_MAX_PACKET_SIZE) {
+ return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
+ }
+
+ synchronized (stateLock) {
+ // If the outbound direction is closed. we can't send anymore.
+ if (engineState == EngineState.CLOSED || engineState == EngineState.CLOSED_OUTBOUND) {
+ return new SSLEngineResult(Status.CLOSED, getHandshakeStatus(), 0, 0);
+ }
+ if (engineState == EngineState.NEW || engineState == EngineState.MODE_SET) {
+ beginHandshake();
+ }
+ }
+
+ // If we haven't completed the handshake yet, just let the caller know.
+ HandshakeStatus handshakeStatus = getHandshakeStatus();
+ if (handshakeStatus == HandshakeStatus.NEED_WRAP) {
+ if (handshakeSink.available() == 0) {
+ long sslSessionCtx = 0L;
+ try {
+ sslSessionCtx = NativeCrypto.SSL_do_handshake_bio(sslNativePointer,
+ nullSource.getContext(), handshakeSink.getContext(), this,
+ getUseClientMode(), sslParameters.npnProtocols,
+ sslParameters.alpnProtocols);
+ if (sslSessionCtx != 0) {
+ if (sslSession != null && engineState == EngineState.HANDSHAKE_STARTED) {
+ engineState = EngineState.READY_HANDSHAKE_CUT_THROUGH;
+ }
+ sslSession = sslParameters.setupSession(sslSessionCtx, sslNativePointer, sslSession,
+ getPeerHost(), getPeerPort(), true);
+ }
+ } catch (Exception e) {
+ throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed")
+ .initCause(e);
+ } finally {
+ if (sslSession == null && sslSessionCtx != 0) {
+ NativeCrypto.SSL_SESSION_free(sslSessionCtx);
+ }
+ }
+ }
+ int bytesWritten = writeSinkToByteBuffer(handshakeSink, dst);
+ return new SSLEngineResult(Status.OK, getHandshakeStatus(), 0, bytesWritten);
+ } else if (handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) {
+ return new SSLEngineResult(Status.OK, handshakeStatus, 0, 0);
+ }
+
+ try {
+ int totalRead = 0;
+ byte[] buffer = null;
+
+ for (ByteBuffer src : srcs) {
+ int toRead = src.remaining();
+ if (buffer == null || toRead > buffer.length) {
+ buffer = new byte[toRead];
+ }
+ /*
+ * We can't just use .mark() here because the caller might be
+ * using it.
+ */
+ src.duplicate().get(buffer, 0, toRead);
+ int numRead = NativeCrypto.SSL_write_BIO(sslNativePointer, buffer, toRead,
+ localToRemoteSink.getContext(), this);
+ if (numRead > 0) {
+ src.position(src.position() + numRead);
+ totalRead += numRead;
+ }
+ }
+
+ return new SSLEngineResult(Status.OK, getHandshakeStatus(), totalRead,
+ writeSinkToByteBuffer(localToRemoteSink, dst));
+ } catch (IOException e) {
+ throw new SSLException(e);
+ }
+ }
+
+ /** Writes data available in a BIO sink to a ByteBuffer. */
+ private static int writeSinkToByteBuffer(OpenSSLBIOSink sink, ByteBuffer dst) {
+ int toWrite = Math.min(sink.available(), dst.remaining());
+ dst.put(sink.toByteArray(), sink.position(), toWrite);
+ sink.skip(toWrite);
+ return toWrite;
+ }
+
+ @Override
+ public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+ return sslParameters.clientPSKKeyRequested(identityHint, identity, key, this);
+ }
+
+ @Override
+ public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+ return sslParameters.serverPSKKeyRequested(identityHint, identity, key, this);
+ }
+
+ @Override
+ public void onSSLStateChange(long sslSessionNativePtr, int type, int val) {
+ synchronized (stateLock) {
+ switch (type) {
+ case NativeCrypto.SSL_CB_HANDSHAKE_DONE:
+ if (engineState != EngineState.HANDSHAKE_STARTED &&
+ engineState != EngineState.READY_HANDSHAKE_CUT_THROUGH) {
+ throw new IllegalStateException("Completed handshake while in mode "
+ + engineState);
+ }
+ engineState = EngineState.HANDSHAKE_COMPLETED;
+ break;
+ case NativeCrypto.SSL_CB_HANDSHAKE_START:
+ // For clients, this will allow the NEED_UNWRAP status to be
+ // returned.
+ engineState = EngineState.HANDSHAKE_STARTED;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void verifyCertificateChain(long sslSessionNativePtr, 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 = new OpenSSLX509Certificate[certRefs.length];
+ for (int i = 0; i < certRefs.length; i++) {
+ peerCertChain[i] = new OpenSSLX509Certificate(certRefs[i]);
+ }
+
+ // Used for verifyCertificateChain callback
+ handshakeSession = new OpenSSLSessionImpl(sslSessionNativePtr, null, peerCertChain,
+ getPeerHost(), getPeerPort(), null);
+
+ boolean client = sslParameters.getUseClientMode();
+ if (client) {
+ Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, getPeerHost());
+ } else {
+ String authType = peerCertChain[0].getPublicKey().getAlgorithm();
+ x509tm.checkClientTrusted(peerCertChain, authType);
+ }
+ } catch (CertificateException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CertificateException(e);
+ } finally {
+ // Clear this before notifying handshake completed listeners
+ handshakeSession = null;
+ }
+ }
+
+ @Override
+ public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+ throws CertificateEncodingException, SSLException {
+ sslParameters.chooseClientCertificate(keyTypeBytes, asn1DerEncodedPrincipals,
+ sslNativePointer, this);
+ }
+
+ private void shutdown() {
+ try {
+ NativeCrypto.SSL_shutdown_BIO(sslNativePointer, nullSource.getContext(),
+ localToRemoteSink.getContext(), this);
+ } catch (IOException ignored) {
+ /*
+ * TODO: The RI ignores close failures in SSLSocket, but need to
+ * investigate whether it does for SSLEngine.
+ */
+ }
+ }
+
+ private void shutdownAndFreeSslNative() {
+ try {
+ shutdown();
+ } finally {
+ free();
+ }
+ }
+
+ private void free() {
+ if (sslNativePointer == 0) {
+ return;
+ }
+ NativeCrypto.SSL_free(sslNativePointer);
+ sslNativePointer = 0;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ free();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
+ if (keyManager instanceof X509ExtendedKeyManager) {
+ X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager;
+ return ekm.chooseEngineServerAlias(keyType, null, this);
+ } else {
+ return keyManager.chooseServerAlias(keyType, null, null);
+ }
+ }
+
+ @Override
+ public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
+ String[] keyTypes) {
+ if (keyManager instanceof X509ExtendedKeyManager) {
+ X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager;
+ return ekm.chooseEngineClientAlias(keyTypes, issuers, this);
+ } else {
+ return keyManager.chooseClientAlias(keyTypes, issuers, null);
+ }
+ }
+
+ @Override
+ public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
+ return keyManager.chooseServerKeyIdentityHint(this);
+ }
+
+ @Override
+ public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
+ return keyManager.chooseClientKeyIdentity(identityHint, this);
+ }
+
+ @Override
+ public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
+ return keyManager.getKey(identityHint, identity, this);
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLKey.java b/src/main/java/org/conscrypt/OpenSSLKey.java
index 8413fd0..9dbd60c 100644
--- a/src/main/java/org/conscrypt/OpenSSLKey.java
+++ b/src/main/java/org/conscrypt/OpenSSLKey.java
@@ -20,6 +20,9 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
@@ -70,8 +73,11 @@
return ((OpenSSLKeyHolder) key).getOpenSSLKey();
}
- if (!"PKCS#8".equals(key.getFormat())) {
- throw new InvalidKeyException("Unknown key format " + key.getFormat());
+ final String keyFormat = key.getFormat();
+ if (keyFormat == null) {
+ return wrapPrivateKey(key);
+ } else if (!"PKCS#8".equals(key.getFormat())) {
+ throw new InvalidKeyException("Unknown key format " + keyFormat);
}
final byte[] encoded = key.getEncoded();
@@ -82,10 +88,41 @@
return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(key.getEncoded()));
}
+ private static OpenSSLKey wrapPrivateKey(PrivateKey key) throws InvalidKeyException {
+ if (key instanceof RSAPrivateKey) {
+ return OpenSSLRSAPrivateKey.wrapPlatformKey((RSAPrivateKey) key);
+ } else if (key instanceof DSAPrivateKey) {
+ return OpenSSLDSAPrivateKey.wrapPlatformKey((DSAPrivateKey) key);
+ } else if (key instanceof ECPrivateKey) {
+ return OpenSSLECPrivateKey.wrapPlatformKey((ECPrivateKey) key);
+ } else {
+ throw new InvalidKeyException("Unknown key type: " + key.toString());
+ }
+ }
+
+ public static OpenSSLKey fromPublicKey(PublicKey key) throws InvalidKeyException {
+ if (key instanceof OpenSSLKeyHolder) {
+ return ((OpenSSLKeyHolder) key).getOpenSSLKey();
+ }
+
+ if (!"X.509".equals(key.getFormat())) {
+ throw new InvalidKeyException("Unknown key format " + key.getFormat());
+ }
+
+ final byte[] encoded = key.getEncoded();
+ if (encoded == null) {
+ throw new InvalidKeyException("Key encoding is null");
+ }
+
+ return new OpenSSLKey(NativeCrypto.d2i_PUBKEY(key.getEncoded()));
+ }
+
public PublicKey getPublicKey() throws NoSuchAlgorithmException {
switch (NativeCrypto.EVP_PKEY_type(ctx)) {
case NativeCrypto.EVP_PKEY_RSA:
return new OpenSSLRSAPublicKey(this);
+ case NativeCrypto.EVP_PKEY_DH:
+ return new OpenSSLDHPublicKey(this);
case NativeCrypto.EVP_PKEY_DSA:
return new OpenSSLDSAPublicKey(this);
case NativeCrypto.EVP_PKEY_EC:
@@ -121,6 +158,8 @@
switch (NativeCrypto.EVP_PKEY_type(ctx)) {
case NativeCrypto.EVP_PKEY_RSA:
return new OpenSSLRSAPrivateKey(this);
+ case NativeCrypto.EVP_PKEY_DH:
+ return new OpenSSLDHPrivateKey(this);
case NativeCrypto.EVP_PKEY_DSA:
return new OpenSSLDSAPrivateKey(this);
case NativeCrypto.EVP_PKEY_EC:
diff --git a/src/main/java/org/conscrypt/OpenSSLMac.java b/src/main/java/org/conscrypt/OpenSSLMac.java
index ed163ec..2cadc3f 100644
--- a/src/main/java/org/conscrypt/OpenSSLMac.java
+++ b/src/main/java/org/conscrypt/OpenSSLMac.java
@@ -26,8 +26,7 @@
import javax.crypto.SecretKey;
public abstract class OpenSSLMac extends MacSpi {
- private final OpenSSLDigestContext ctx = new OpenSSLDigestContext(
- NativeCrypto.EVP_MD_CTX_create());
+ private OpenSSLDigestContext ctx;
/**
* Holds the EVP_MD for the hashing algorithm, e.g.
@@ -88,17 +87,19 @@
macKey = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_mac_key(evp_pkey_type, keyBytes));
}
- NativeCrypto.EVP_MD_CTX_init(ctx.getContext());
-
- reset();
+ resetContext();
}
- private void reset() {
+ private final void resetContext() {
+ OpenSSLDigestContext ctxLocal = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
+ NativeCrypto.EVP_MD_CTX_init(ctxLocal);
+
final OpenSSLKey macKey = this.macKey;
- if (macKey == null) {
- return;
+ if (macKey != null) {
+ NativeCrypto.EVP_DigestSignInit(ctxLocal, evp_md, macKey.getPkeyContext());
}
- NativeCrypto.EVP_DigestSignInit(ctx.getContext(), evp_md, macKey.getPkeyContext());
+
+ this.ctx = ctxLocal;
}
@Override
@@ -109,19 +110,21 @@
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
- NativeCrypto.EVP_DigestUpdate(ctx.getContext(), input, offset, len);
+ final OpenSSLDigestContext ctxLocal = ctx;
+ NativeCrypto.EVP_DigestUpdate(ctxLocal, input, offset, len);
}
@Override
protected byte[] engineDoFinal() {
- final byte[] output = NativeCrypto.EVP_DigestSignFinal(ctx.getContext());
- reset();
+ final OpenSSLDigestContext ctxLocal = ctx;
+ final byte[] output = NativeCrypto.EVP_DigestSignFinal(ctxLocal);
+ resetContext();
return output;
}
@Override
protected void engineReset() {
- reset();
+ resetContext();
}
public static class HmacMD5 extends OpenSSLMac {
diff --git a/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java b/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java
index 7b6c608..f028421 100644
--- a/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java
+++ b/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java
@@ -16,18 +16,14 @@
package org.conscrypt;
-import java.security.MessageDigest;
+import java.security.MessageDigestSpi;
import java.security.NoSuchAlgorithmException;
/**
* Implements the JDK MessageDigest interface using OpenSSL's EVP API.
*/
-public class OpenSSLMessageDigestJDK extends MessageDigest implements Cloneable {
-
- /**
- * Holds a pointer to the native message digest context.
- */
- private long ctx;
+public class OpenSSLMessageDigestJDK extends MessageDigestSpi implements Cloneable {
+ private OpenSSLDigestContext ctx;
/**
* Holds the EVP_MD for the hashing algorithm, e.g. EVP_get_digestbyname("sha1");
@@ -45,19 +41,31 @@
private final byte[] singleByte = new byte[1];
/**
- * Creates a new OpenSSLMessageDigest instance for the given algorithm
- * name.
+ * Creates a new OpenSSLMessageDigest instance for the given algorithm name.
*/
- private OpenSSLMessageDigestJDK(String algorithm, long evp_md, int size)
- throws NoSuchAlgorithmException {
- super(algorithm);
+ private OpenSSLMessageDigestJDK(long evp_md, int size) throws NoSuchAlgorithmException {
this.evp_md = evp_md;
this.size = size;
+
+ resetContext();
+ }
+
+ private OpenSSLMessageDigestJDK(long evp_md, int size, OpenSSLDigestContext ctx) {
+ this.evp_md = evp_md;
+ this.size = size;
+ this.ctx = ctx;
+ }
+
+ private final void resetContext() {
+ OpenSSLDigestContext ctxLocal = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
+ NativeCrypto.EVP_MD_CTX_init(ctxLocal);
+ NativeCrypto.EVP_DigestInit(ctxLocal, evp_md);
+ ctx = ctxLocal;
}
@Override
protected void engineReset() {
- free();
+ resetContext();
}
@Override
@@ -73,52 +81,22 @@
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
- NativeCrypto.EVP_DigestUpdate(getCtx(), input, offset, len);
+ NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
}
@Override
protected byte[] engineDigest() {
- byte[] result = new byte[size];
- NativeCrypto.EVP_DigestFinal(getCtx(), result, 0);
- ctx = 0; // EVP_DigestFinal frees the context as a side effect
+ final byte[] result = new byte[size];
+ NativeCrypto.EVP_DigestFinal(ctx, result, 0);
+ resetContext();
return result;
}
- @Override
- public Object clone() throws CloneNotSupportedException {
- OpenSSLMessageDigestJDK d = (OpenSSLMessageDigestJDK) super.clone();
- d.ctx = NativeCrypto.EVP_MD_CTX_copy(getCtx());
- return d;
- }
-
- private long getCtx() {
- if (ctx == 0) {
- ctx = NativeCrypto.EVP_DigestInit(evp_md);
- }
- return ctx;
- }
-
- private void free() {
- if (ctx != 0) {
- NativeCrypto.EVP_MD_CTX_destroy(ctx);
- ctx = 0;
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- free();
- } finally {
- super.finalize();
- }
- }
-
public static class MD5 extends OpenSSLMessageDigestJDK {
private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("md5");
private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
public MD5() throws NoSuchAlgorithmException {
- super("MD5",EVP_MD, SIZE);
+ super(EVP_MD, SIZE);
}
}
@@ -126,7 +104,7 @@
private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha1");
private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
public SHA1() throws NoSuchAlgorithmException {
- super("SHA-1", EVP_MD, SIZE);
+ super(EVP_MD, SIZE);
}
}
@@ -134,7 +112,7 @@
private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha224");
private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
public SHA224() throws NoSuchAlgorithmException {
- super("SHA-224", EVP_MD, SIZE);
+ super(EVP_MD, SIZE);
}
}
@@ -142,7 +120,7 @@
private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha256");
private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
public SHA256() throws NoSuchAlgorithmException {
- super("SHA-256", EVP_MD, SIZE);
+ super(EVP_MD, SIZE);
}
}
@@ -150,7 +128,7 @@
private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha384");
private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
public SHA384() throws NoSuchAlgorithmException {
- super("SHA-384", EVP_MD, SIZE);
+ super(EVP_MD, SIZE);
}
}
@@ -158,7 +136,15 @@
private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha512");
private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
public SHA512() throws NoSuchAlgorithmException {
- super("SHA-512", EVP_MD, SIZE);
+ super(EVP_MD, SIZE);
}
}
+
+ @Override
+ public Object clone() {
+ OpenSSLDigestContext ctxCopy = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
+ NativeCrypto.EVP_MD_CTX_init(ctxCopy);
+ NativeCrypto.EVP_MD_CTX_copy(ctxCopy, ctx);
+ return new OpenSSLMessageDigestJDK(evp_md, size, ctxCopy);
+ }
}
diff --git a/src/main/java/org/conscrypt/OpenSSLNativeReference.java b/src/main/java/org/conscrypt/OpenSSLNativeReference.java
new file mode 100644
index 0000000..dc2d893
--- /dev/null
+++ b/src/main/java/org/conscrypt/OpenSSLNativeReference.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+/**
+ * Used to hold onto native OpenSSL references and run finalization on those
+ * objects. Individual types must subclass this and implement finalizer.
+ */
+public abstract class OpenSSLNativeReference {
+ final long context;
+
+ public OpenSSLNativeReference(long ctx) {
+ if (ctx == 0) {
+ throw new NullPointerException("ctx == 0");
+ }
+
+ this.context = ctx;
+ }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLProvider.java b/src/main/java/org/conscrypt/OpenSSLProvider.java
index 90e08a2..c5ee329 100644
--- a/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -34,7 +34,11 @@
public static final String PROVIDER_NAME = "AndroidOpenSSL";
public OpenSSLProvider() {
- super(PROVIDER_NAME, 1.0, "Android's OpenSSL-backed security provider");
+ this(PROVIDER_NAME);
+ }
+
+ public OpenSSLProvider(String providerName) {
+ super(providerName, 1.0, "Android's OpenSSL-backed security provider");
// Make sure the platform is initialized.
Platform.setup();
@@ -81,6 +85,9 @@
put("KeyPairGenerator.RSA", prefix + "OpenSSLRSAKeyPairGenerator");
put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.1", "RSA");
+ put("KeyPairGenerator.DH", prefix + "OpenSSLDHKeyPairGenerator");
+ put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.3.1", "DH");
+
put("KeyPairGenerator.DSA", prefix + "OpenSSLDSAKeyPairGenerator");
put("KeyPairGenerator.EC", prefix + "OpenSSLECKeyPairGenerator");
@@ -89,6 +96,9 @@
put("KeyFactory.RSA", prefix + "OpenSSLRSAKeyFactory");
put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA");
+ put("KeyFactory.DH", prefix + "OpenSSLDHKeyFactory");
+ put("Alg.Alias.KeyFactory.1.2.840.113549.1.3.1", "DH");
+
put("KeyFactory.DSA", prefix + "OpenSSLDSAKeyFactory");
put("KeyFactory.EC", prefix + "OpenSSLECKeyFactory");
@@ -197,19 +207,32 @@
/*
* OpenSSL only supports a subset of modes, so we'll name them
* explicitly here.
+ *
+ * Moreover, OpenSSL only supports PKCS#7 padding. PKCS#5 padding
+ * is also supported because it's a special case of PKCS#7 for 64-bit
+ * blocks. PKCS#5 technically supports only 64-bit blocks and won't
+ * produce the same result as PKCS#7 for blocks that are not 64 bits
+ * long. However, everybody assumes PKCS#7 when they say PKCS#5. For
+ * example, lots of code uses PKCS#5 with AES whose blocks are longer
+ * than 64 bits. We solve this confusion by making PKCS7Padding an
+ * alias for PKCS5Padding.
*/
put("Cipher.AES/ECB/NoPadding", prefix + "OpenSSLCipher$AES$ECB$NoPadding");
put("Cipher.AES/ECB/PKCS5Padding", prefix + "OpenSSLCipher$AES$ECB$PKCS5Padding");
+ put("Alg.Alias.Cipher.AES/ECB/PKCS7Padding", "AES/ECB/PKCS5Padding");
put("Cipher.AES/CBC/NoPadding", prefix + "OpenSSLCipher$AES$CBC$NoPadding");
put("Cipher.AES/CBC/PKCS5Padding", prefix + "OpenSSLCipher$AES$CBC$PKCS5Padding");
+ put("Alg.Alias.Cipher.AES/CBC/PKCS7Padding", "AES/CBC/PKCS5Padding");
put("Cipher.AES/CFB/NoPadding", prefix + "OpenSSLCipher$AES$CFB");
put("Cipher.AES/CTR/NoPadding", prefix + "OpenSSLCipher$AES$CTR");
put("Cipher.AES/OFB/NoPadding", prefix + "OpenSSLCipher$AES$OFB");
put("Cipher.DESEDE/ECB/NoPadding", prefix + "OpenSSLCipher$DESEDE$ECB$NoPadding");
put("Cipher.DESEDE/ECB/PKCS5Padding", prefix + "OpenSSLCipher$DESEDE$ECB$PKCS5Padding");
+ put("Alg.Alias.Cipher.DESEDE/ECB/PKCS7Padding", "DESEDE/ECB/PKCS5Padding");
put("Cipher.DESEDE/CBC/NoPadding", prefix + "OpenSSLCipher$DESEDE$CBC$NoPadding");
put("Cipher.DESEDE/CBC/PKCS5Padding", prefix + "OpenSSLCipher$DESEDE$CBC$PKCS5Padding");
+ put("Alg.Alias.Cipher.DESEDE/CBC/PKCS7Padding", "DESEDE/CBC/PKCS5Padding");
put("Cipher.DESEDE/CFB/NoPadding", prefix + "OpenSSLCipher$DESEDE$CFB");
put("Cipher.DESEDE/OFB/NoPadding", prefix + "OpenSSLCipher$DESEDE$OFB");
diff --git a/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java b/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
index 1cbf5de..9e559a3 100644
--- a/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
+++ b/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
@@ -93,6 +93,14 @@
}
static OpenSSLKey getInstance(RSAPrivateCrtKey rsaPrivateKey) throws InvalidKeyException {
+ /**
+ * If the key is not encodable (PKCS11-like key), then wrap it and use
+ * JNI upcalls to satisfy requests.
+ */
+ if (rsaPrivateKey.getFormat() == null) {
+ return wrapPlatformKey(rsaPrivateKey);
+ }
+
BigInteger modulus = rsaPrivateKey.getModulus();
BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
diff --git a/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java b/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
index efb85b1..1aa4dba 100644
--- a/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
+++ b/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
@@ -89,7 +89,25 @@
return new OpenSSLRSAPrivateKey(key, params);
}
+ protected static OpenSSLKey wrapPlatformKey(RSAPrivateKey rsaPrivateKey)
+ throws InvalidKeyException {
+ OpenSSLKey wrapper = Platform.wrapRsaKey(rsaPrivateKey);
+ if (wrapper != null) {
+ return wrapper;
+ }
+ return new OpenSSLKey(NativeCrypto.getRSAPrivateKeyWrapper(rsaPrivateKey, rsaPrivateKey
+ .getModulus().toByteArray()));
+ }
+
static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException {
+ /**
+ * If the key is not encodable (PKCS11-like key), then wrap it and use
+ * JNI upcalls to satisfy requests.
+ */
+ if (rsaPrivateKey.getFormat() == null) {
+ return wrapPlatformKey(rsaPrivateKey);
+ }
+
final BigInteger modulus = rsaPrivateKey.getModulus();
final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
diff --git a/src/main/java/org/conscrypt/OpenSSLRandom.java b/src/main/java/org/conscrypt/OpenSSLRandom.java
index 1683bb8..c25f2ee 100644
--- a/src/main/java/org/conscrypt/OpenSSLRandom.java
+++ b/src/main/java/org/conscrypt/OpenSSLRandom.java
@@ -22,20 +22,58 @@
public class OpenSSLRandom extends SecureRandomSpi implements Serializable {
private static final long serialVersionUID = 8506210602917522860L;
+ private boolean mSeeded;
+
@Override
protected void engineSetSeed(byte[] seed) {
+ // NOTE: The contract of the SecureRandomSpi does not appear to prohibit self-seeding here
+ // (in addition to using the provided seed).
+ selfSeedIfNotSeeded();
NativeCrypto.RAND_seed(seed);
}
@Override
protected void engineNextBytes(byte[] bytes) {
+ selfSeedIfNotSeeded();
NativeCrypto.RAND_bytes(bytes);
}
@Override
protected byte[] engineGenerateSeed(int numBytes) {
+ selfSeedIfNotSeeded();
byte[] output = new byte[numBytes];
NativeCrypto.RAND_bytes(output);
return output;
}
+
+ /**
+ * Self-seeds this instance from the Linux RNG. Does nothing if this instance has already been
+ * seeded.
+ */
+ private void selfSeedIfNotSeeded() {
+ // NOTE: No need to worry about concurrent access to this field because the worst case is
+ // that the code below is executed multiple times (by different threads), which may only
+ // increase the entropy of the OpenSSL PRNG.
+ if (mSeeded) {
+ return;
+ }
+
+ seedOpenSSLPRNGFromLinuxRNG();
+ mSeeded = true;
+ }
+
+ /**
+ * Obtains a seed from the Linux RNG and mixes it into the OpenSSL PRNG (default RAND engine).
+ *
+ * <p>NOTE: This modifies the OpenSSL PRNG shared by all instances of OpenSSLRandom and other
+ * crypto primitives offered by or built on top of OpenSSL.
+ */
+ public static void seedOpenSSLPRNGFromLinuxRNG() {
+ int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
+ int bytesRead = NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
+ if (bytesRead != seedLengthInBytes) {
+ throw new SecurityException("Failed to read sufficient bytes from /dev/urandom."
+ + " Expected: " + seedLengthInBytes + ", actual: " + bytesRead);
+ }
+ }
}
diff --git a/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java b/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
index dbc3a19..25819ed 100644
--- a/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
+++ b/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
@@ -44,7 +44,7 @@
@Override
public String[] getDefaultCipherSuites() {
- return NativeCrypto.getDefaultCipherSuites();
+ return sslParameters.getEnabledCipherSuites();
}
@Override
diff --git a/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java b/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
index aa917c3..215da55 100644
--- a/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
+++ b/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
@@ -25,8 +25,6 @@
*/
public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
private final SSLParametersImpl sslParameters;
- private String[] enabledProtocols = NativeCrypto.getSupportedProtocols();
- private String[] enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
private boolean channelIdEnabled;
protected OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException {
@@ -81,7 +79,7 @@
*/
@Override
public String[] getEnabledProtocols() {
- return enabledProtocols.clone();
+ return sslParameters.getEnabledProtocols();
}
/**
@@ -95,7 +93,7 @@
*/
@Override
public void setEnabledProtocols(String[] protocols) {
- enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
+ sslParameters.setEnabledProtocols(protocols);
}
@Override
@@ -105,7 +103,7 @@
@Override
public String[] getEnabledCipherSuites() {
- return enabledCipherSuites.clone();
+ return sslParameters.getEnabledCipherSuites();
}
/**
@@ -132,7 +130,7 @@
*/
@Override
public void setEnabledCipherSuites(String[] suites) {
- enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
+ sslParameters.setEnabledCipherSuites(suites);
}
@Override
@@ -167,9 +165,7 @@
@Override
public Socket accept() throws IOException {
- OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters,
- enabledProtocols.clone(),
- enabledCipherSuites.clone());
+ OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters);
socket.setChannelIdEnabled(channelIdEnabled);
implAccept(socket);
return socket;
diff --git a/src/main/java/org/conscrypt/OpenSSLSignature.java b/src/main/java/org/conscrypt/OpenSSLSignature.java
index 53b0df0..33e6b65 100644
--- a/src/main/java/org/conscrypt/OpenSSLSignature.java
+++ b/src/main/java/org/conscrypt/OpenSSLSignature.java
@@ -21,29 +21,19 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.Signature;
import java.security.SignatureException;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
+import java.security.SignatureSpi;
/**
* Implements the subset of the JDK Signature interface needed for
* signature verification using OpenSSL.
*/
-public class OpenSSLSignature extends Signature {
+public class OpenSSLSignature extends SignatureSpi {
private static enum EngineType {
RSA, DSA, EC,
};
- /**
- * Holds a pointer to the native message digest context.
- */
- private long ctx;
+ private OpenSSLDigestContext ctx;
/**
* The current OpenSSL key we're operating on.
@@ -58,7 +48,7 @@
/**
* Holds the OpenSSL name of the algorithm (lower case, no dashes).
*/
- private final String evpAlgorithm;
+ private final long evpAlgorithm;
/**
* Holds a dummy buffer for writing single bytes to the digest.
@@ -66,23 +56,33 @@
private final byte[] singleByte = new byte[1];
/**
+ * True when engine is initialized to signing.
+ */
+ private boolean signing;
+
+ /**
* Creates a new OpenSSLSignature instance for the given algorithm name.
*
* @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1".
*/
- private OpenSSLSignature(String algorithm, EngineType engineType)
+ private OpenSSLSignature(long algorithm, EngineType engineType)
throws NoSuchAlgorithmException {
- super(algorithm);
-
- // We don't support MD2
- if ("RSA-MD2".equals(algorithm)) {
- throw new NoSuchAlgorithmException(algorithm);
- }
-
this.engineType = engineType;
this.evpAlgorithm = algorithm;
}
+ private final void resetContext() {
+ OpenSSLDigestContext ctxLocal = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
+ NativeCrypto.EVP_MD_CTX_init(ctxLocal);
+ if (signing) {
+ enableDSASignatureNonceHardeningIfApplicable();
+ NativeCrypto.EVP_SignInit(ctxLocal, evpAlgorithm);
+ } else {
+ NativeCrypto.EVP_VerifyInit(ctxLocal, evpAlgorithm);
+ }
+ this.ctx = ctxLocal;
+ }
+
@Override
protected void engineUpdate(byte input) {
singleByte[0] = input;
@@ -91,26 +91,11 @@
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
- if (state == SIGN) {
- if (ctx == 0) {
- try {
- ctx = NativeCrypto.EVP_SignInit(evpAlgorithm);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- NativeCrypto.EVP_SignUpdate(ctx, input, offset, len);
+ final OpenSSLDigestContext ctxLocal = ctx;
+ if (signing) {
+ NativeCrypto.EVP_SignUpdate(ctxLocal, input, offset, len);
} else {
- if (ctx == 0) {
- try {
- ctx = NativeCrypto.EVP_VerifyInit(evpAlgorithm);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- NativeCrypto.EVP_VerifyUpdate(ctx, input, offset, len);
+ NativeCrypto.EVP_VerifyUpdate(ctxLocal, input, offset, len);
}
}
@@ -125,98 +110,64 @@
switch (engineType) {
case RSA:
if (pkeyType != NativeCrypto.EVP_PKEY_RSA) {
- throw new InvalidKeyException("Signature not initialized as RSA");
+ throw new InvalidKeyException("Signature initialized as " + engineType
+ + " (not RSA)");
}
break;
case DSA:
if (pkeyType != NativeCrypto.EVP_PKEY_DSA) {
- throw new InvalidKeyException("Signature not initialized as DSA");
+ throw new InvalidKeyException("Signature initialized as " + engineType
+ + " (not DSA)");
}
break;
case EC:
if (pkeyType != NativeCrypto.EVP_PKEY_EC) {
- throw new InvalidKeyException("Signature not initialized as EC");
+ throw new InvalidKeyException("Signature initialized as " + engineType
+ + " (not EC)");
}
break;
default:
- throw new InvalidKeyException("Need DSA or RSA or EC private key");
+ throw new InvalidKeyException("Key must be of type " + engineType);
}
}
+ private void initInternal(OpenSSLKey newKey, boolean signing) throws InvalidKeyException {
+ checkEngineType(newKey);
+ key = newKey;
+
+ this.signing = signing;
+ resetContext();
+ }
+
@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
- destroyContextIfExists();
+ initInternal(OpenSSLKey.fromPrivateKey(privateKey), true);
+ }
- if (privateKey instanceof OpenSSLKeyHolder) {
- OpenSSLKey pkey = ((OpenSSLKeyHolder) privateKey).getOpenSSLKey();
- checkEngineType(pkey);
- key = pkey;
- } else if (privateKey instanceof RSAPrivateCrtKey) {
- if (engineType != EngineType.RSA) {
- throw new InvalidKeyException("Signature not initialized as RSA");
- }
-
- RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
- key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
- } else if (privateKey instanceof RSAPrivateKey) {
- if (engineType != EngineType.RSA) {
- throw new InvalidKeyException("Signature not initialized as RSA");
- }
-
- RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
- key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
- } else if (privateKey instanceof DSAPrivateKey) {
- if (engineType != EngineType.DSA) {
- throw new InvalidKeyException("Signature not initialized as DSA");
- }
-
- DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privateKey;
- key = OpenSSLDSAPrivateKey.getInstance(dsaPrivateKey);
- } else if (privateKey instanceof ECPrivateKey) {
- if (engineType != EngineType.EC) {
- throw new InvalidKeyException("Signature not initialized as EC");
- }
-
- ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey;
- key = OpenSSLECPrivateKey.getInstance(ecPrivateKey);
- } else {
- throw new InvalidKeyException("Need DSA or RSA or EC private key");
+ /**
+ * Enables a mitigation against private key leakage through DSA and ECDSA signatures when weak
+ * nonces (per-message k values) are used. To mitigate the issue, private key and message being
+ * signed is mixed into the randomly generated nonce (k).
+ *
+ * <p>Does nothing for signatures that are neither DSA nor ECDSA.
+ */
+ private void enableDSASignatureNonceHardeningIfApplicable() {
+ final OpenSSLKey key = this.key;
+ switch (engineType) {
+ case DSA:
+ NativeCrypto.set_DSA_flag_nonce_from_hash(key.getPkeyContext());
+ break;
+ case EC:
+ NativeCrypto.EC_KEY_set_nonce_from_hash(key.getPkeyContext(), true);
+ break;
+ default:
+ // Hardening not applicable
}
}
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
- // If we had an existing context, destroy it first.
- destroyContextIfExists();
-
- if (publicKey instanceof OpenSSLKeyHolder) {
- OpenSSLKey pkey = ((OpenSSLKeyHolder) publicKey).getOpenSSLKey();
- checkEngineType(pkey);
- key = pkey;
- } else if (publicKey instanceof RSAPublicKey) {
- if (engineType != EngineType.RSA) {
- throw new InvalidKeyException("Signature not initialized as RSA");
- }
-
- RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
- key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
- } else if (publicKey instanceof DSAPublicKey) {
- if (engineType != EngineType.DSA) {
- throw new InvalidKeyException("Signature not initialized as DSA");
- }
-
- DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
- key = OpenSSLDSAPublicKey.getInstance(dsaPublicKey);
- } else if (publicKey instanceof ECPublicKey) {
- if (engineType != EngineType.EC) {
- throw new InvalidKeyException("Signature not initialized as EC");
- }
-
- ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
- key = OpenSSLECPublicKey.getInstance(ecPublicKey);
- } else {
- throw new InvalidKeyException("Need DSA or RSA or EC public key");
- }
+ initInternal(OpenSSLKey.fromPublicKey(publicKey), false);
}
@Override
@@ -230,9 +181,11 @@
throw new SignatureException("Need DSA or RSA or EC private key");
}
+ final OpenSSLDigestContext ctxLocal = ctx;
try {
byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getPkeyContext())];
- int bytesWritten = NativeCrypto.EVP_SignFinal(ctx, buffer, 0, key.getPkeyContext());
+ int bytesWritten = NativeCrypto.EVP_SignFinal(ctxLocal, buffer, 0,
+ key.getPkeyContext());
byte[] signature = new byte[bytesWritten];
System.arraycopy(buffer, 0, signature, 0, bytesWritten);
@@ -245,7 +198,7 @@
* Java expects the digest context to be reset completely after sign
* calls.
*/
- destroyContextIfExists();
+ resetContext();
}
}
@@ -267,86 +220,80 @@
* Java expects the digest context to be reset completely after
* verify calls.
*/
- destroyContextIfExists();
- }
- }
-
- private void destroyContextIfExists() {
- if (ctx != 0) {
- NativeCrypto.EVP_MD_CTX_destroy(ctx);
- ctx = 0;
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (ctx != 0) {
- NativeCrypto.EVP_MD_CTX_destroy(ctx);
- }
- } finally {
- super.finalize();
+ resetContext();
}
}
public static final class MD5RSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-MD5");
public MD5RSA() throws NoSuchAlgorithmException {
- super("RSA-MD5", EngineType.RSA);
+ super(EVP_MD, EngineType.RSA);
}
}
public static final class SHA1RSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA1");
public SHA1RSA() throws NoSuchAlgorithmException {
- super("RSA-SHA1", EngineType.RSA);
+ super(EVP_MD, EngineType.RSA);
}
}
public static final class SHA224RSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA224");
public SHA224RSA() throws NoSuchAlgorithmException {
- super("RSA-SHA224", EngineType.RSA);
+ super(EVP_MD, EngineType.RSA);
}
}
public static final class SHA256RSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA256");
public SHA256RSA() throws NoSuchAlgorithmException {
- super("RSA-SHA256", EngineType.RSA);
+ super(EVP_MD, EngineType.RSA);
}
}
public static final class SHA384RSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA384");
public SHA384RSA() throws NoSuchAlgorithmException {
- super("RSA-SHA384", EngineType.RSA);
+ super(EVP_MD, EngineType.RSA);
}
}
public static final class SHA512RSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA512");
public SHA512RSA() throws NoSuchAlgorithmException {
- super("RSA-SHA512", EngineType.RSA);
+ super(EVP_MD, EngineType.RSA);
}
}
public static final class SHA1DSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("DSA-SHA1");
public SHA1DSA() throws NoSuchAlgorithmException {
- super("DSA-SHA1", EngineType.DSA);
+ super(EVP_MD, EngineType.DSA);
}
}
public static final class SHA1ECDSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA1");
public SHA1ECDSA() throws NoSuchAlgorithmException {
- super("SHA1", EngineType.EC);
+ super(EVP_MD, EngineType.EC);
}
}
public static final class SHA224ECDSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA224");
public SHA224ECDSA() throws NoSuchAlgorithmException {
- super("SHA224", EngineType.EC);
+ super(EVP_MD, EngineType.EC);
}
}
public static final class SHA256ECDSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA256");
public SHA256ECDSA() throws NoSuchAlgorithmException {
- super("SHA256", EngineType.EC);
+ super(EVP_MD, EngineType.EC);
}
}
public static final class SHA384ECDSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA384");
public SHA384ECDSA() throws NoSuchAlgorithmException {
- super("SHA384", EngineType.EC);
+ super(EVP_MD, EngineType.EC);
}
}
public static final class SHA512ECDSA extends OpenSSLSignature {
+ private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA512");
public SHA512ECDSA() throws NoSuchAlgorithmException {
- super("SHA512", EngineType.EC);
+ super(EVP_MD, EngineType.EC);
}
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java b/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java
index 9c4e4ad..7d4faf0 100644
--- a/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java
+++ b/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java
@@ -18,11 +18,10 @@
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.Signature;
import java.security.SignatureException;
+import java.security.SignatureSpi;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
@@ -31,7 +30,7 @@
* Implements the JDK Signature interface needed for RAW RSA signature
* generation and verification using OpenSSL.
*/
-public class OpenSSLSignatureRawRSA extends Signature {
+public class OpenSSLSignatureRawRSA extends SignatureSpi {
/**
* The current OpenSSL key we're operating on.
*/
@@ -52,13 +51,6 @@
*/
private boolean inputIsTooLong;
- /**
- * Creates a new OpenSSLSignature instance for the given algorithm name.
- */
- public OpenSSLSignatureRawRSA() throws NoSuchAlgorithmException {
- super("NONEwithRSA");
- }
-
@Override
protected void engineUpdate(byte input) {
final int oldOffset = inputOffset++;
diff --git a/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java b/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
index 9367e1f..53df874 100644
--- a/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
+++ b/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
@@ -47,7 +47,7 @@
@Override
public String[] getDefaultCipherSuites() {
- return NativeCrypto.getDefaultCipherSuites();
+ return sslParameters.getEnabledCipherSuites();
}
@Override
diff --git a/src/main/java/org/conscrypt/OpenSSLSocketImpl.java b/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
index d05322c..8303b00 100644
--- a/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
+++ b/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
@@ -16,6 +16,7 @@
package org.conscrypt;
+import org.conscrypt.util.Arrays;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
@@ -30,26 +31,18 @@
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
-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.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;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.Streams;
-import libcore.io.StructTimeval;
-
-import static libcore.io.OsConstants.SOL_SOCKET;
-import static libcore.io.OsConstants.SO_SNDTIMEO;
/**
* Implementation of the class OpenSSLSocketImpl based on OpenSSL.
@@ -63,7 +56,8 @@
*/
public class OpenSSLSocketImpl
extends javax.net.ssl.SSLSocket
- implements NativeCrypto.SSLHandshakeCallbacks {
+ implements NativeCrypto.SSLHandshakeCallbacks, SSLParametersImpl.AliasChooser,
+ SSLParametersImpl.PSKCallbacks {
private static final boolean DBG_STATE = false;
@@ -138,29 +132,19 @@
private final SSLParametersImpl sslParameters;
private final CloseGuard guard = CloseGuard.get();
- private String[] enabledProtocols;
- private String[] enabledCipherSuites;
- private byte[] npnProtocols;
- private byte[] alpnProtocols;
- private boolean useSessionTickets;
- private boolean useSni;
+ private ArrayList<HandshakeCompletedListener> listeners;
/**
- * Whether the TLS Channel ID extension is enabled. This field is
- * server-side only.
+ * Private key for the TLS Channel ID extension. This field is client-side
+ * only. Set during startHandshake.
*/
- private boolean channelIdEnabled;
-
- /**
- * Private key for the TLS Channel ID extension. This field is
- * client-side only. Set during startHandshake.
- */
- private OpenSSLKey channelIdPrivateKey;
+ OpenSSLKey channelIdPrivateKey;
/** Set during startHandshake. */
private OpenSSLSessionImpl sslSession;
- private ArrayList<HandshakeCompletedListener> listeners;
+ /** Used during handshake callbacks. */
+ private OpenSSLSessionImpl handshakeSession;
/**
* Local cache of timeout to avoid getsockopt on every read and
@@ -179,20 +163,6 @@
this.wrappedPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
- this.enabledProtocols = NativeCrypto.getDefaultProtocols();
- this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
- }
-
- protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
- String[] enabledProtocols,
- String[] enabledCipherSuites) throws IOException {
- this.socket = this;
- this.wrappedHost = null;
- this.wrappedPort = -1;
- this.autoClose = false;
- this.sslParameters = sslParameters;
- this.enabledProtocols = enabledProtocols;
- this.enabledCipherSuites = enabledCipherSuites;
}
protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
@@ -203,8 +173,6 @@
this.wrappedPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
- this.enabledProtocols = NativeCrypto.getDefaultProtocols();
- this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
}
protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
@@ -215,8 +183,6 @@
this.wrappedPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
- this.enabledProtocols = NativeCrypto.getDefaultProtocols();
- this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
}
@@ -229,8 +195,6 @@
this.wrappedPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
- this.enabledProtocols = NativeCrypto.getDefaultProtocols();
- this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
}
protected OpenSSLSocketImpl(InetAddress address, int port,
@@ -242,8 +206,6 @@
this.wrappedPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
- this.enabledProtocols = NativeCrypto.getDefaultProtocols();
- this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
}
/**
@@ -257,55 +219,12 @@
this.wrappedPort = port;
this.autoClose = autoClose;
this.sslParameters = sslParameters;
- this.enabledProtocols = NativeCrypto.getDefaultProtocols();
- this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
// this.timeout is not set intentionally.
// OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout
// to wrapped socket
}
- /**
- * Gets the suitable session reference from the session cache container.
- */
- private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
- String hostname = getPeerHostName();
- if (hostname == null) {
- return null;
- }
- int port = getPeerPort();
- OpenSSLSessionImpl session = (OpenSSLSessionImpl) 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;
- }
-
private void checkOpen() throws SocketException {
if (isClosed()) {
throw new SocketException("Socket is closed");
@@ -343,117 +262,26 @@
final boolean client = sslParameters.getUseClientMode();
- final long sslCtxNativePointer = (client) ?
- sslParameters.getClientSessionContext().sslCtxNativePointer :
- sslParameters.getServerSessionContext().sslCtxNativePointer;
-
sslNativePointer = 0;
boolean releaseResources = true;
try {
+ final AbstractSessionContext sessionContext = sslParameters.getSessionContext();
+ final long sslCtxNativePointer = sessionContext.sslCtxNativePointer;
sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
guard.open("close");
- if (npnProtocols != null) {
- NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer);
- }
-
- if (client && alpnProtocols != null) {
- NativeCrypto.SSL_CTX_set_alpn_protos(sslCtxNativePointer, alpnProtocols);
- }
-
- NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
- NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
- if (useSessionTickets) {
- NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
- }
- if (useSni) {
- NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, getPeerHostName());
- }
-
- // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites with TLSv1 and
- // SSLv3).
- NativeCrypto.SSL_set_mode(
- sslNativePointer, NativeCrypto.SSL_MODE_CBC_RECORD_SPLITTING);
-
- boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
+ boolean enableSessionCreation = getEnableSessionCreation();
if (!enableSessionCreation) {
NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
- enableSessionCreation);
+ enableSessionCreation);
}
- AbstractSessionContext sessionContext;
- OpenSSLSessionImpl sessionToReuse;
- if (client) {
- // look for client session to reuse
- ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
- sessionContext = clientSessionContext;
- sessionToReuse = getCachedClientSession(clientSessionContext);
- if (sessionToReuse != null) {
- NativeCrypto.SSL_set_session(sslNativePointer,
- sessionToReuse.sslSessionNativePointer);
- }
- } else {
- sessionContext = sslParameters.getServerSessionContext();
- sessionToReuse = null;
- }
-
- // setup server certificates and private keys.
- // clients will receive a call back to request certificates.
- if (!client) {
- Set<String> keyTypes = new HashSet<String>();
- for (long sslCipherNativePointer : NativeCrypto.SSL_get_ciphers(sslNativePointer)) {
- String keyType = getServerKeyType(sslCipherNativePointer);
- if (keyType != null) {
- keyTypes.add(keyType);
- }
- }
- for (String keyType : keyTypes) {
- try {
- setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
- null,
- this));
- } catch (CertificateEncodingException e) {
- throw new IOException(e);
- }
- }
- }
-
- // setup peer certificate verification
- if (client) {
- // TODO support for anonymous cipher would require us to
- // conditionally use SSL_VERIFY_NONE
- } else {
- // needing client auth takes priority...
- boolean certRequested;
- if (sslParameters.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 (sslParameters.getWantClientAuth()) {
- NativeCrypto.SSL_set_verify(sslNativePointer,
- NativeCrypto.SSL_VERIFY_PEER);
- certRequested = true;
- // ... and it defaults properly so don't call SSL_set_verify in the common case.
- } else {
- certRequested = false;
- }
-
- if (certRequested) {
- X509TrustManager trustManager = sslParameters.getTrustManager();
- 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);
- }
- }
- }
+ final OpenSSLSessionImpl sessionToReuse = sslParameters.getSessionToReuse(
+ sslNativePointer, getPeerHostName(), getPeerPort());
+ sslParameters.setSSLParameters(sslCtxNativePointer, sslNativePointer, this, this,
+ getPeerHostName());
+ sslParameters.setCertificateValidation(sslNativePointer);
+ sslParameters.setTlsChannelId(sslNativePointer, channelIdPrivateKey);
// Temporarily use a different timeout for the handshake process
int savedReadTimeoutMilliseconds = getSoTimeout();
@@ -463,21 +291,6 @@
setSoWriteTimeout(handshakeTimeoutMilliseconds);
}
- // TLS Channel ID
- if (channelIdEnabled) {
- if (client) {
- // 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.getPkeyContext());
- } else {
- // Server-side TLS Channel ID
- NativeCrypto.SSL_enable_tls_channel_id(sslNativePointer);
- }
- }
-
synchronized (stateLock) {
if (state == STATE_CLOSED) {
return;
@@ -487,8 +300,8 @@
long sslSessionNativePointer;
try {
sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer,
- socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols,
- client ? null : alpnProtocols);
+ Platform.getFileDescriptor(socket), this, getSoTimeout(), client,
+ sslParameters.npnProtocols, client ? null : sslParameters.alpnProtocols);
} catch (CertificateException e) {
SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
wrapper.initCause(e);
@@ -507,6 +320,15 @@
}
}
+ // 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",
+ getPeerHostName());
+ Platform.logEvent(logMessage);
+ }
+
throw e;
}
@@ -519,27 +341,8 @@
}
}
- byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
- if (sessionToReuse != null && Arrays.equals(sessionToReuse.getId(), sessionId)) {
- this.sslSession = sessionToReuse;
- sslSession.lastAccessedTime = System.currentTimeMillis();
- NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
- } else {
- if (!enableSessionCreation) {
- // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
- throw new IllegalStateException("SSL Session may not be created");
- }
- X509Certificate[] localCertificates
- = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer));
- X509Certificate[] peerCertificates
- = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
- this.sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
- peerCertificates, getPeerHostName(), getPeerPort(), sessionContext);
- // if not, putSession later in handshakeCompleted() callback
- if (handshakeCompleted) {
- sessionContext.putSession(sslSession);
- }
- }
+ sslSession = sslParameters.setupSession(sslSessionNativePointer, sslNativePointer,
+ sessionToReuse, getPeerHostName(), getPeerPort(), handshakeCompleted);
// Restore the original timeout now that the handshake is complete
if (handshakeTimeoutMilliseconds >= 0) {
@@ -568,7 +371,8 @@
}
}
} catch (SSLProtocolException e) {
- throw new SSLHandshakeException(e);
+ throw (SSLHandshakeException) new SSLHandshakeException("Handshake failed")
+ .initCause(e);
} finally {
// on exceptional exit, treat the socket as closed
if (releaseResources) {
@@ -591,15 +395,6 @@
}
}
- private 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;
- }
-
String getPeerHostName() {
if (wrappedHost != null) {
return wrappedHost;
@@ -615,90 +410,33 @@
return wrappedHost == null ? super.getPort() : wrappedPort;
}
- /**
- * Return a possibly null array of X509Certificates given the
- * possibly null array of DER encoded bytes.
- */
- private static OpenSSLX509Certificate[] createCertChain(long[] certificateRefs)
- throws IOException {
- if (certificateRefs == null) {
- return null;
- }
- OpenSSLX509Certificate[] certificates = new OpenSSLX509Certificate[certificateRefs.length];
- for (int i = 0; i < certificateRefs.length; i++) {
- certificates[i] = new OpenSSLX509Certificate(certificateRefs[i]);
- }
- return certificates;
- }
-
- private void setCertificate(String alias) throws CertificateEncodingException, SSLException {
- if (alias == null) {
- return;
- }
- PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
- if (privateKey == null) {
- return;
- }
- X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
- if (certificates == null) {
- return;
- }
-
- /*
- * 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);
-
- try {
- final OpenSSLKey key = OpenSSLKey.fromPrivateKey(privateKey);
- NativeCrypto.SSL_use_PrivateKey(sslNativePointer, key.getPkeyContext());
- } catch (InvalidKeyException e) {
- throw new SSLException(e);
- }
-
- // checks the last installed private key and certificate,
- // so need to do this once per loop iteration
- NativeCrypto.SSL_check_private_key(sslNativePointer);
- }
-
@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);
+ }
- String[] keyTypes = new String[keyTypeBytes.length];
- for (int i = 0; i < keyTypeBytes.length; i++) {
- keyTypes[i] = getClientKeyType(keyTypeBytes[i]);
- }
+ @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);
+ }
- 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]);
- }
- }
- setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, 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 handshakeCompleted() {
+ public void onSSLStateChange(long sslSessionNativePtr, int type, int val) {
+ if (type != NativeCrypto.SSL_CB_HANDSHAKE_DONE) {
+ return;
+ }
+
synchronized (stateLock) {
if (state == STATE_HANDSHAKE_STARTED) {
// If sslSession is null, the handshake was completed during
@@ -763,9 +501,13 @@
@SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
@Override
- public void verifyCertificateChain(long[] certRefs, String authMethod)
+ public void verifyCertificateChain(long sslSessionNativePtr, 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");
}
@@ -773,25 +515,25 @@
for (int i = 0; i < certRefs.length; i++) {
peerCertChain[i] = new OpenSSLX509Certificate(certRefs[i]);
}
+
+ // Used for verifyCertificateChain callback
+ handshakeSession = new OpenSSLSessionImpl(sslSessionNativePtr, null, peerCertChain,
+ getPeerHostName(), getPeerPort(), null);
+
boolean client = sslParameters.getUseClientMode();
if (client) {
- X509TrustManager x509tm = sslParameters.getTrustManager();
- if (x509tm instanceof TrustManagerImpl) {
- TrustManagerImpl tm = (TrustManagerImpl) x509tm;
- tm.checkServerTrusted(peerCertChain, authMethod, getPeerHostName());
- } else {
- x509tm.checkServerTrusted(peerCertChain, authMethod);
- }
+ Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, getPeerHostName());
} else {
String authType = peerCertChain[0].getPublicKey().getAlgorithm();
- sslParameters.getTrustManager().checkClientTrusted(peerCertChain,
- authType);
+ x509tm.checkClientTrusted(peerCertChain, authType);
}
-
} catch (CertificateException e) {
throw e;
} catch (Exception e) {
throw new CertificateException(e);
+ } finally {
+ // Clear this before notifying handshake completed listeners
+ handshakeSession = null;
}
}
@@ -901,7 +643,9 @@
*/
@Override
public int read() throws IOException {
- return Streams.readSingleByte(this);
+ byte[] buffer = new byte[1];
+ int result = read(buffer, 0, 1);
+ return (result != -1) ? buffer[0] & 0xff : -1;
}
/**
@@ -927,7 +671,7 @@
if (DBG_STATE) assertReadableOrWriteableState();
}
- return NativeCrypto.SSL_read(sslNativePointer, socket.getFileDescriptor$(),
+ return NativeCrypto.SSL_read(sslNativePointer, Platform.getFileDescriptor(socket),
OpenSSLSocketImpl.this, buf, offset, byteCount, getSoTimeout());
}
}
@@ -966,7 +710,9 @@
*/
@Override
public void write(int oneByte) throws IOException {
- Streams.writeSingleByte(this, oneByte);
+ byte[] buffer = new byte[1];
+ buffer[0] = (byte) (oneByte & 0xff);
+ write(buffer);
}
/**
@@ -991,7 +737,7 @@
if (DBG_STATE) assertReadableOrWriteableState();
}
- NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(),
+ NativeCrypto.SSL_write(sslNativePointer, Platform.getFileDescriptor(socket),
OpenSSLSocketImpl.this, buf, offset, byteCount, writeTimeoutMilliseconds);
}
}
@@ -1017,7 +763,7 @@
} catch (IOException e) {
// return an invalid session with
// invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
- return SSLSessionImpl.getNullSession();
+ return SSLNullSession.getNullSession();
}
}
return sslSession;
@@ -1068,12 +814,12 @@
@Override
public String[] getEnabledCipherSuites() {
- return enabledCipherSuites.clone();
+ return sslParameters.getEnabledCipherSuites();
}
@Override
public void setEnabledCipherSuites(String[] suites) {
- enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
+ sslParameters.setEnabledCipherSuites(suites);
}
@Override
@@ -1083,12 +829,12 @@
@Override
public String[] getEnabledProtocols() {
- return enabledProtocols.clone();
+ return sslParameters.getEnabledProtocols();
}
@Override
public void setEnabledProtocols(String[] protocols) {
- enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
+ sslParameters.setEnabledProtocols(protocols);
}
/**
@@ -1097,7 +843,7 @@
* @param useSessionTickets True to enable session tickets
*/
public void setUseSessionTickets(boolean useSessionTickets) {
- this.useSessionTickets = useSessionTickets;
+ sslParameters.useSessionTickets = useSessionTickets;
}
/**
@@ -1106,7 +852,7 @@
* @param hostname the desired SNI hostname, or null to disable
*/
public void setHostname(String hostname) {
- useSni = hostname != null;
+ sslParameters.setUseSni(hostname != null);
wrappedHost = hostname;
}
@@ -1131,7 +877,7 @@
+ " begun.");
}
}
- this.channelIdEnabled = enabled;
+ sslParameters.channelIdEnabled = enabled;
}
/**
@@ -1184,12 +930,12 @@
}
if (privateKey == null) {
- this.channelIdEnabled = false;
- this.channelIdPrivateKey = null;
+ sslParameters.channelIdEnabled = false;
+ channelIdPrivateKey = null;
} else {
- this.channelIdEnabled = true;
+ sslParameters.channelIdEnabled = true;
try {
- this.channelIdPrivateKey = OpenSSLKey.fromPrivateKey(privateKey);
+ channelIdPrivateKey = OpenSSLKey.fromPrivateKey(privateKey);
} catch (InvalidKeyException e) {
// Will have error in startHandshake
}
@@ -1259,12 +1005,7 @@
public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
this.writeTimeoutMilliseconds = writeTimeoutMilliseconds;
- StructTimeval tv = StructTimeval.fromMillis(writeTimeoutMilliseconds);
- try {
- Libcore.os.setsockoptTimeval(getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
- } catch (ErrnoException errnoException) {
- throw errnoException.rethrowAsSocketException();
- }
+ Platform.setSocketTimeout(this, writeTimeoutMilliseconds);
}
/**
@@ -1347,7 +1088,7 @@
private void shutdownAndFreeSslNative() throws IOException {
try {
BlockGuard.getThreadPolicy().onNetwork();
- NativeCrypto.SSL_shutdown(sslNativePointer, socket.getFileDescriptor$(),
+ NativeCrypto.SSL_shutdown(sslNativePointer, Platform.getFileDescriptor(socket),
this);
} catch (IOException ignored) {
/*
@@ -1411,12 +1152,12 @@
}
}
- @Override
+ /* @Override */
public FileDescriptor getFileDescriptor$() {
if (socket == this) {
- return super.getFileDescriptor$();
+ return Platform.getFileDescriptorFromSSLSocket(this);
} else {
- return socket.getFileDescriptor$();
+ return Platform.getFileDescriptor(socket);
}
}
@@ -1449,7 +1190,7 @@
if (npnProtocols != null && npnProtocols.length == 0) {
throw new IllegalArgumentException("npnProtocols.length == 0");
}
- this.npnProtocols = npnProtocols;
+ sslParameters.npnProtocols = npnProtocols;
}
/**
@@ -1466,97 +1207,32 @@
if (alpnProtocols != null && alpnProtocols.length == 0) {
throw new IllegalArgumentException("alpnProtocols.length == 0");
}
- this.alpnProtocols = alpnProtocols;
+ sslParameters.alpnProtocols = alpnProtocols;
}
- /** Key type: RSA. */
- private static final String KEY_TYPE_RSA = "RSA";
-
- /** Key type: DSA. */
- private static final String KEY_TYPE_DSA = "DSA";
-
- /** Key type: Diffie-Hellman with RSA signature. */
- private static final String KEY_TYPE_DH_RSA = "DH_RSA";
-
- /** Key type: Diffie-Hellman with DSA signature. */
- private static final String KEY_TYPE_DH_DSA = "DH_DSA";
-
- /** Key type: Elliptic Curve. */
- private static final String KEY_TYPE_EC = "EC";
-
- /** Key type: Eliiptic Curve with ECDSA signature. */
- private static final String KEY_TYPE_EC_EC = "EC_EC";
-
- /** Key type: Eliiptic Curve 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 anonymous key
- * exchanges.
- */
- private static String getServerKeyType(long sslCipherNative) throws SSLException {
- int algorithm_mkey = NativeCrypto.get_SSL_CIPHER_algorithm_mkey(sslCipherNative);
- int algorithm_auth = NativeCrypto.get_SSL_CIPHER_algorithm_auth(sslCipherNative);
- switch (algorithm_mkey) {
- case NativeCrypto.SSL_kRSA:
- return KEY_TYPE_RSA;
- case NativeCrypto.SSL_kEDH:
- switch (algorithm_auth) {
- case NativeCrypto.SSL_aDSS:
- return KEY_TYPE_DSA;
- case NativeCrypto.SSL_aRSA:
- return KEY_TYPE_RSA;
- case NativeCrypto.SSL_aNULL:
- return null;
- }
- break;
- case NativeCrypto.SSL_kECDHr:
- return KEY_TYPE_EC_RSA;
- case NativeCrypto.SSL_kECDHe:
- return KEY_TYPE_EC_EC;
- case NativeCrypto.SSL_kEECDH:
- switch (algorithm_auth) {
- case NativeCrypto.SSL_aECDSA:
- return KEY_TYPE_EC_EC;
- case NativeCrypto.SSL_aRSA:
- return KEY_TYPE_RSA;
- case NativeCrypto.SSL_aNULL:
- return null;
- }
- break;
- }
-
- throw new SSLException("Unsupported key exchange. "
- + "mkey: 0x" + Long.toHexString(algorithm_mkey & 0xffffffffL)
- + ", auth: 0x" + Long.toHexString(algorithm_auth & 0xffffffffL));
+ @Override
+ public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
+ return keyManager.chooseServerAlias(keyType, null, this);
}
- /**
- * Similar to getServerKeyType, but returns value given TLS
- * ClientCertificateType byte values from a CertificateRequest
- * message for use with X509KeyManager.chooseClientAlias or
- * X509ExtendedKeyManager.chooseEngineClientAlias.
- */
- public static String getClientKeyType(byte keyType) {
- // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
- switch (keyType) {
- case NativeCrypto.TLS_CT_RSA_SIGN:
- return KEY_TYPE_RSA; // RFC rsa_sign
- case NativeCrypto.TLS_CT_DSS_SIGN:
- return KEY_TYPE_DSA; // RFC dss_sign
- case NativeCrypto.TLS_CT_RSA_FIXED_DH:
- return KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
- case NativeCrypto.TLS_CT_DSS_FIXED_DH:
- return KEY_TYPE_DH_DSA; // RFC dss_fixed_dh
- case NativeCrypto.TLS_CT_ECDSA_SIGN:
- return KEY_TYPE_EC; // RFC ecdsa_sign
- case NativeCrypto.TLS_CT_RSA_FIXED_ECDH:
- return KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
- case NativeCrypto.TLS_CT_ECDSA_FIXED_ECDH:
- return KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh
- default:
- return null;
- }
+ @Override
+ public String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
+ String[] keyTypes) {
+ return keyManager.chooseClientAlias(keyTypes, null, this);
+ }
+
+ @Override
+ public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
+ return keyManager.chooseServerKeyIdentityHint(this);
+ }
+
+ @Override
+ public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
+ return keyManager.chooseClientKeyIdentity(identityHint, this);
+ }
+
+ @Override
+ public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
+ return keyManager.getKey(identityHint, identity, this);
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLX509CRL.java b/src/main/java/org/conscrypt/OpenSSLX509CRL.java
index 56b99cc..58571e7 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509CRL.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509CRL.java
@@ -63,7 +63,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
}
@@ -77,7 +77,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length);
@@ -102,7 +102,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
}
@@ -117,7 +117,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length);
@@ -373,7 +373,7 @@
NativeCrypto.X509_CRL_print(bioCtx, mContext);
return os.toString();
} finally {
- NativeCrypto.BIO_free(bioCtx);
+ NativeCrypto.BIO_free_all(bioCtx);
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java b/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java
index 470cc98..c5b0bac 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java
@@ -129,7 +129,7 @@
NativeCrypto.X509_REVOKED_print(bioCtx, mContext);
return os.toString();
} finally {
- NativeCrypto.BIO_free(bioCtx);
+ NativeCrypto.BIO_free_all(bioCtx);
}
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLX509CertPath.java b/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
index 57568b8..6cea051 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
@@ -32,7 +32,9 @@
import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
public class OpenSSLX509CertPath extends CertPath {
- private static final byte[] PKCS7_MARKER = "-----BEGIN PKCS7".getBytes();
+ private static final byte[] PKCS7_MARKER = new byte[] {
+ '-', '-', '-', '-', '-', 'B', 'E', 'G', 'I', 'N', ' ', 'P', 'K', 'C', 'S', '7'
+ };
private static final int PUSHBACK_SIZE = 64;
@@ -153,7 +155,7 @@
}
throw new CertificateException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
if (certRefs == null) {
diff --git a/src/main/java/org/conscrypt/OpenSSLX509Certificate.java b/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
index 142978a..e3d8b01 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
@@ -72,7 +72,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
}
@@ -95,7 +95,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
if (certRefs == null) {
@@ -127,7 +127,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
}
@@ -143,7 +143,7 @@
} catch (Exception e) {
throw new ParsingException(e);
} finally {
- NativeCrypto.BIO_free(bis.getBioContext());
+ bis.release();
}
final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>(
@@ -226,11 +226,13 @@
public void checkValidity(Date date) throws CertificateExpiredException,
CertificateNotYetValidException {
if (getNotBefore().compareTo(date) > 0) {
- throw new CertificateNotYetValidException();
+ throw new CertificateNotYetValidException("Certificate not valid until "
+ + getNotBefore().toString() + " (compared to " + date.toString() + ")");
}
if (getNotAfter().compareTo(date) < 0) {
- throw new CertificateExpiredException();
+ throw new CertificateExpiredException("Certificate expired at "
+ + getNotAfter().toString() + " (compared to " + date.toString() + ")");
}
}
@@ -401,7 +403,7 @@
NativeCrypto.X509_print_ex(bioCtx, mContext, 0, 0);
return os.toString();
} finally {
- NativeCrypto.BIO_free(bioCtx);
+ NativeCrypto.BIO_free_all(bioCtx);
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java b/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
index 75fdedb..bae6c8e 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
@@ -34,7 +34,9 @@
import java.util.List;
public class OpenSSLX509CertificateFactory extends CertificateFactorySpi {
- private static final byte[] PKCS7_MARKER = "-----BEGIN PKCS7".getBytes();
+ private static final byte[] PKCS7_MARKER = new byte[] {
+ '-', '-', '-', '-', '-', 'B', 'E', 'G', 'I', 'N', ' ', 'P', 'K', 'C', 'S', '7'
+ };
private static final int PUSHBACK_SIZE = 64;
diff --git a/src/main/java/org/conscrypt/PRF.java b/src/main/java/org/conscrypt/PRF.java
deleted file mode 100644
index afbbb45..0000000
--- a/src/main/java/org/conscrypt/PRF.java
+++ /dev/null
@@ -1,198 +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.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLException;
-
-/**
- * This class provides functionality for computation
- * of PRF values for TLS (http://www.ietf.org/rfc/rfc2246.txt)
- * and SSL v3 (http://wp.netscape.com/eng/ssl3) protocols.
- */
-public class PRF {
- private static Logger.Stream logger = Logger.getStream("prf");
-
- private static Mac md5_mac;
- private static Mac sha_mac;
- protected static MessageDigest md5;
- protected static MessageDigest sha;
- private static int md5_mac_length;
- private static int sha_mac_length;
-
- static private void init() {
- try {
- md5_mac = Mac.getInstance("HmacMD5");
- sha_mac = Mac.getInstance("HmacSHA1");
- } catch (NoSuchAlgorithmException e) {
- throw new AlertException(AlertProtocol.INTERNAL_ERROR,
- new SSLException(
- "There is no provider of HmacSHA1 or HmacMD5 "
- + "algorithms installed in the system"));
- }
- md5_mac_length = md5_mac.getMacLength();
- sha_mac_length = sha_mac.getMacLength();
- try {
- md5 = MessageDigest.getInstance("MD5");
- sha = MessageDigest.getInstance("SHA-1");
- } catch (Exception e) {
- throw new AlertException(AlertProtocol.INTERNAL_ERROR,
- new SSLException(
- "Could not initialize the Digest Algorithms."));
- }
- }
-
- /**
- * Computes the value of SSLv3 pseudo random function.
- * @param out: the buffer to fill up with the value of the function.
- * @param secret: the buffer containing the secret value to generate prf.
- * @param seed: the seed to be used.
- */
- static synchronized void computePRF_SSLv3(byte[] out, byte[] secret, byte[] seed) {
- if (sha == null) {
- init();
- }
- int pos = 0;
- int iteration = 1;
- byte[] digest;
- while (pos < out.length) {
- byte[] pref = new byte[iteration];
- Arrays.fill(pref, (byte) (64 + iteration++));
- sha.update(pref);
- sha.update(secret);
- sha.update(seed);
- md5.update(secret);
- md5.update(sha.digest());
- digest = md5.digest(); // length == 16
- if (pos + 16 > out.length) {
- System.arraycopy(digest, 0, out, pos, out.length - pos);
- pos = out.length;
- } else {
- System.arraycopy(digest, 0, out, pos, 16);
- pos += 16;
- }
- }
- }
-
- /**
- * Computes the value of TLS pseudo random function.
- * @param out: the buffer to fill up with the value of the function.
- * @param secret: the buffer containing the secret value to generate prf.
- * @param str_bytes: the label bytes to be used.
- * @param seed: the seed to be used.
- */
- synchronized static void computePRF(byte[] out, byte[] secret,
- byte[] str_byts, byte[] seed) throws GeneralSecurityException {
- if (sha_mac == null) {
- init();
- }
- // Do concatenation of the label with the seed:
- // (metterings show that is is faster to concatenate the arrays
- // and to call HMAC.update on cancatenation, than twice call for
- // each of the part, i.e.:
- // time(HMAC.update(label+seed))
- // < time(HMAC.update(label)) + time(HMAC.update(seed))
- // but it takes more memmory (approximaty on 4%)
- /*
- byte[] tmp_seed = new byte[seed.length + str_byts.length];
- System.arraycopy(str_byts, 0, tmp_seed, 0, str_byts.length);
- System.arraycopy(seed, 0, tmp_seed, str_byts.length, seed.length);
- seed = tmp_seed;
- */
- SecretKeySpec keyMd5;
- SecretKeySpec keySha1;
- if ((secret == null) || (secret.length == 0)) {
- secret = new byte[8];
- keyMd5 = new SecretKeySpec(secret, "HmacMD5");
- keySha1 = new SecretKeySpec(secret, "HmacSHA1");
- } else {
- int length = secret.length >> 1; // division by 2
- int offset = secret.length & 1; // remainder
- keyMd5 = new SecretKeySpec(secret, 0, length + offset,
- "HmacMD5");
- keySha1 = new SecretKeySpec(secret, length, length
- + offset, "HmacSHA1");
- }
-
- //byte[] str_byts = label.getBytes();
-
- if (logger != null) {
- logger.println("secret["+secret.length+"]: ");
- logger.printAsHex(16, "", " ", secret);
- logger.println("label["+str_byts.length+"]: ");
- logger.printAsHex(16, "", " ", str_byts);
- logger.println("seed["+seed.length+"]: ");
- logger.printAsHex(16, "", " ", seed);
- logger.println("MD5 key:");
- logger.printAsHex(16, "", " ", keyMd5.getEncoded());
- logger.println("SHA1 key:");
- logger.printAsHex(16, "", " ", keySha1.getEncoded());
- }
-
- md5_mac.init(keyMd5);
- sha_mac.init(keySha1);
-
- int pos = 0;
- md5_mac.update(str_byts);
- byte[] hash = md5_mac.doFinal(seed); // A(1)
- while (pos < out.length) {
- md5_mac.update(hash);
- md5_mac.update(str_byts);
- md5_mac.update(seed);
- if (pos + md5_mac_length < out.length) {
- md5_mac.doFinal(out, pos);
- pos += md5_mac_length;
- } else {
- System.arraycopy(md5_mac.doFinal(), 0, out,
- pos, out.length - pos);
- break;
- }
- // make A(i)
- hash = md5_mac.doFinal(hash);
- }
- if (logger != null) {
- logger.println("P_MD5:");
- logger.printAsHex(md5_mac_length, "", " ", out);
- }
-
- pos = 0;
- sha_mac.update(str_byts);
- hash = sha_mac.doFinal(seed); // A(1)
- byte[] sha1hash;
- while (pos < out.length) {
- sha_mac.update(hash);
- sha_mac.update(str_byts);
- sha1hash = sha_mac.doFinal(seed);
- for (int i = 0; (i < sha_mac_length) & (pos < out.length); i++) {
- out[pos++] ^= sha1hash[i];
- }
- // make A(i)
- hash = sha_mac.doFinal(hash);
- }
-
- if (logger != null) {
- logger.println("PRF:");
- logger.printAsHex(sha_mac_length, "", " ", out);
- }
- }
-}
diff --git a/src/main/java/org/conscrypt/PSKKeyManager.java b/src/main/java/org/conscrypt/PSKKeyManager.java
new file mode 100644
index 0000000..d32b49d
--- /dev/null
+++ b/src/main/java/org/conscrypt/PSKKeyManager.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import java.net.Socket;
+import javax.crypto.SecretKey;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Provider of key material for pre-shared key (PSK) key exchange used in TLS-PSK cipher suites.
+ *
+ * <h3>Overview of TLS-PSK</h3>
+ *
+ * <p>TLS-PSK is a set of TLS/SSL cipher suites which rely on a symmetric pre-shared key (PSK) to
+ * secure the TLS/SSL connection and mutually authenticate its peers. These cipher suites may be
+ * a more natural fit compared to conventional public key based cipher suites in some scenarios
+ * where communication between peers is bootstrapped via a separate step (for example, a pairing
+ * step) and requires both peers to authenticate each other. In such scenarios a symmetric key (PSK)
+ * can be exchanged during the bootstrapping step, removing the need to generate and exchange public
+ * key pairs and X.509 certificates.</p>
+ *
+ * <p>When a TLS-PSK cipher suite is used, both peers have to use the same key for the TLS/SSL
+ * handshake to succeed. Thus, both peers are implicitly authenticated by a successful handshake.
+ * This removes the need to use a {@code TrustManager} in conjunction with this {@code KeyManager}.
+ * </p>
+ *
+ * <h3>Supporting multiple keys</h3>
+ *
+ * <p>A peer may have multiple keys to choose from. To help choose the right key, during the
+ * handshake the server can provide a <em>PSK identity hint</em> to the client, and the client can
+ * provide a <em>PSK identity</em> to the server. The contents of these two pieces of information
+ * are specific to application-level protocols.</p>
+ *
+ * <p><em>NOTE: Both the PSK identity hint and the PSK identity are transmitted in cleartext.
+ * Moreover, these data are received and processed prior to peer having been authenticated. Thus,
+ * they must not contain or leak key material or other sensitive information, and should be
+ * treated (e.g., parsed) with caution, as untrusted data.</em></p>
+ *
+ * <p>The high-level flow leading to peers choosing a key during TLS/SSL handshake is as follows:
+ * <ol>
+ * <li>Server receives a handshake request from client.
+ * <li>Server replies, optionally providing a PSK identity hint to client.</li>
+ * <li>Client chooses the key.</li>
+ * <li>Client provides a PSK identity of the chosen key to server.</li>
+ * <li>Server chooses the key.</li>
+ * </ol></p>
+ *
+ * <p>In the flow above, either peer can signal that they do not have a suitable key, in which case
+ * the the handshake will be aborted immediately. This may enable a network attacker who does not
+ * know the key to learn which PSK identity hints or PSK identities are supported. If this is a
+ * concern then a randomly generated key should be used in the scenario where no key is available.
+ * This will lead to the handshake aborting later, due to key mismatch -- same as in the scenario
+ * where a key is available -- making it appear to the attacker that all PSK identity hints and PSK
+ * identities are supported.</p>
+ *
+ * <h3>Maximum sizes</h3>
+ *
+ * <p>The maximum supported sizes are as follows:
+ * <ul>
+ * <li>256 bytes for keys (see {@link #MAX_KEY_LENGTH_BYTES}),</li>
+ * <li>128 bytes for PSK identity and PSK identity hint (in modified UTF-8 representation) (see
+ * {@link #MAX_IDENTITY_LENGTH_BYTES} and {@link #MAX_IDENTITY_HINT_LENGTH_BYTES}).</li>
+ * </ul></p>
+ *
+ * <h3>Example</h3>
+ * The following example illustrates how to create an {@code SSLContext} which enables the use of
+ * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained
+ * from it.
+ * <pre> {@code
+ * PSKKeyManager myPskKeyManager = ...;
+ *
+ * SSLContext sslContext = SSLContext.getInstance("TLS");
+ * sslContext.init(
+ * new KeyManager[] {myPskKeyManager},
+ * new TrustManager[0], // No TrustManagers needed for TLS-PSK
+ * null // Use the default source of entropy
+ * );
+ *
+ * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...);
+ * }</pre>
+ */
+public interface PSKKeyManager extends KeyManager {
+
+ /**
+ * Maximum supported length (in bytes) for PSK identity hint (in modified UTF-8 representation).
+ */
+ int MAX_IDENTITY_HINT_LENGTH_BYTES = 128;
+
+ /** Maximum supported length (in bytes) for PSK identity (in modified UTF-8 representation). */
+ int MAX_IDENTITY_LENGTH_BYTES = 128;
+
+ /** Maximum supported length (in bytes) for PSK key. */
+ int MAX_KEY_LENGTH_BYTES = 256;
+
+ /**
+ * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+ * socket.
+ *
+ * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+ */
+ String chooseServerKeyIdentityHint(Socket socket);
+
+ /**
+ * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+ * engine.
+ *
+ * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+ */
+ String chooseServerKeyIdentityHint(SSLEngine engine);
+
+ /**
+ * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+ * socket.
+ *
+ * @param identityHint identity hint provided by the server or {@code null} if none provided.
+ *
+ * @return PSK identity to provide to the server. {@code null} is permitted but will be
+ * converted into an empty string.
+ */
+ String chooseClientKeyIdentity(String identityHint, Socket socket);
+
+ /**
+ * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+ * engine.
+ *
+ * @param identityHint identity hint provided by the server or {@code null} if none provided.
+ *
+ * @return PSK identity to provide to the server. {@code null} is permitted but will be
+ * converted into an empty string.
+ */
+ String chooseClientKeyIdentity(String identityHint, SSLEngine engine);
+
+ /**
+ * Gets the PSK to use for the provided socket.
+ *
+ * @param identityHint identity hint provided by the server to help select the key or
+ * {@code null} if none provided.
+ * @param identity identity provided by the client to help select the key.
+ *
+ * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+ * the handshake.
+ */
+ SecretKey getKey(String identityHint, String identity, Socket socket);
+
+ /**
+ * Gets the PSK to use for the provided engine.
+ *
+ * @param identityHint identity hint provided by the server to help select the key or
+ * {@code null} if none provided.
+ * @param identity identity provided by the client to help select the key.
+ *
+ * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+ * the handshake.
+ */
+ SecretKey getKey(String identityHint, String identity, SSLEngine engine);
+}
\ No newline at end of file
diff --git a/src/main/java/org/conscrypt/Platform.java b/src/main/java/org/conscrypt/Platform.java
deleted file mode 100644
index 3a577ce..0000000
--- a/src/main/java/org/conscrypt/Platform.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 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 org.apache.harmony.security.utils.AlgNameMapper;
-import org.apache.harmony.security.utils.AlgNameMapperSource;
-
-class 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.
- */
- public static void setup() {
- NoPreloadHolder.MAPPER.ping();
- }
-
- /**
- * Just a placeholder to make sure the class is initialized.
- */
- private void ping() {
- }
-
- private Platform() {
- AlgNameMapper.setSource(new OpenSSLMapper());
- }
-
- private static class OpenSSLMapper implements AlgNameMapperSource {
- @Override
- public String mapNameToOid(String algName) {
- return NativeCrypto.OBJ_txt2nid_oid(algName);
- }
-
- @Override
- public String mapOidToName(String oid) {
- return NativeCrypto.OBJ_txt2nid_longName(oid);
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/org/conscrypt/ProtocolVersion.java b/src/main/java/org/conscrypt/ProtocolVersion.java
deleted file mode 100644
index 4e2555c..0000000
--- a/src/main/java/org/conscrypt/ProtocolVersion.java
+++ /dev/null
@@ -1,145 +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.util.Hashtable;
-
-/**
- *
- * Represents Protocol Version
- */
-public class ProtocolVersion {
- /**
- * Protocols supported by this provider implementation
- */
- public static final String[] supportedProtocols = new String[] { "TLSv1",
- "SSLv3" };
-
- private static Hashtable<String, ProtocolVersion> protocolsByName = new Hashtable<String, ProtocolVersion>(4);
-
- /**
- *
- * Returns true if protocol version is supported
- *
- * @param version
- */
- public static boolean isSupported(byte[] version) {
- if (version[0] != 3 || (version[1] != 0 && version[1] != 1)) {
- return false;
- }
- return true;
- }
-
- /**
- * Returns ProtocolVersion
- *
- * @param version
- * @return
- */
- public static ProtocolVersion getByVersion(byte[] version) {
- if (version[0] == 3) {
- if (version[1] == 1) {
- return TLSv1;
- }
- if (version[1] == 0) {
- return SSLv3;
- }
- }
- return null;
- }
-
- /**
- * Returns true if provider supports protocol version
- *
- * @param name
- * @return
- */
- public static boolean isSupported(String name) {
- return protocolsByName.containsKey(name);
- }
-
- /**
- * Returns ProtocolVersion
- *
- * @param name
- * @return
- */
- public static ProtocolVersion getByName(String name) {
- return protocolsByName.get(name);
- }
-
- /**
- * Highest protocol version supported by provider implementation
- *
- * @param protocols
- * @return
- */
- public static ProtocolVersion getLatestVersion(String[] protocols) {
- if (protocols == null || protocols.length == 0) {
- return null;
- }
- ProtocolVersion latest = getByName(protocols[0]);
- ProtocolVersion current;
- for (int i = 1; i < protocols.length; i++) {
- current = getByName(protocols[i]);
- if (current == null) {
- continue;
- }
- if ((latest == null)
- || (latest.version[0] < current.version[0])
- || (latest.version[0] == current.version[0] && latest.version[1] < current.version[1])) {
- latest = current;
- }
- }
- return latest;
-
- }
-
- /**
- * SSL 3.0 protocol version
- */
- public static final ProtocolVersion SSLv3 = new ProtocolVersion("SSLv3",
- new byte[] { 3, 0 });
-
- /**
- * TLS 1.0 protocol version
- */
- public static final ProtocolVersion TLSv1 = new ProtocolVersion("TLSv1",
- new byte[] { 3, 1 });
-
- static {
- protocolsByName.put(SSLv3.name, SSLv3);
- protocolsByName.put(TLSv1.name, TLSv1);
- protocolsByName.put("SSL", SSLv3);
- protocolsByName.put("TLS", TLSv1);
- }
-
- /**
- * Protocol name
- */
- public final String name;
-
- /**
- * Protocol version as byte array
- */
- public final byte[] version;
-
- private ProtocolVersion(String name, byte[] version) {
- this.name = name;
- this.version = version;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/org/conscrypt/SSLBufferedInput.java b/src/main/java/org/conscrypt/SSLBufferedInput.java
deleted file mode 100644
index d2834d3..0000000
--- a/src/main/java/org/conscrypt/SSLBufferedInput.java
+++ /dev/null
@@ -1,75 +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.nio.ByteBuffer;
-
-/**
- * This is a wrapper input stream for ByteBuffer data source.
- * Among with the read functionality it provides info
- * about number of cunsumed bytes from the source ByteBuffer.
- * The source ByteBuffer object can be reseted.
- * So one instance of this wrapper can be reused for several
- * ByteBuffer data sources.
- */
-public class SSLBufferedInput extends SSLInputStream {
-
- private ByteBuffer in;
- private int bytik;
- private int consumed = 0;
-
- /**
- * Constructor
- */
- protected SSLBufferedInput() {}
-
- /**
- * Sets the buffer as a data source
- */
- protected void setSourceBuffer(ByteBuffer in) {
- consumed = 0;
- this.in = in;
- }
-
- @Override
- public int available() throws IOException {
- // in assumption that the buffer has been set
- return in.remaining();
- }
-
- /**
- * Returns the number of consumed bytes.
- */
- protected int consumed() {
- return consumed;
- }
-
- /**
- * Reads the following byte value. If there are no bytes in the source
- * buffer, method throws java.nio.BufferUnderflowException.
- */
- @Override
- public int read() throws IOException {
- // TODO: implement optimized read(int)
- // and read(byte[], int, int) methods
- bytik = in.get() & 0x00FF;
- consumed ++;
- return bytik;
- }
-}
diff --git a/src/main/java/org/conscrypt/SSLEngineAppData.java b/src/main/java/org/conscrypt/SSLEngineAppData.java
deleted file mode 100644
index d1b87a2..0000000
--- a/src/main/java/org/conscrypt/SSLEngineAppData.java
+++ /dev/null
@@ -1,94 +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.nio.ByteBuffer;
-import javax.net.ssl.SSLException;
-
-/**
- * This class is used to retrieve the application data
- * arrived for the SSLEngine.
- */
-public class SSLEngineAppData implements Appendable {
-
- /**
- * Buffer containing received application data.
- */
- byte[] buffer;
-
- /**
- * Constructor
- */
- protected SSLEngineAppData() {}
-
- /**
- * Stores received data. The source data is not cloned,
- * just the array reference is remembered into the buffer field.
- */
- @Override
- public void append(byte[] src) {
- if (buffer != null) {
- throw new AlertException(
- AlertProtocol.INTERNAL_ERROR,
- new SSLException("Attempt to override the data"));
- }
- buffer = src;
- }
-
- /**
- * Places the data from the buffer into the array of destination
- * ByteBuffer objects.
- */
- protected int placeTo(ByteBuffer[] dsts, int offset, int length) {
- if (buffer == null) {
- return 0;
- }
- int pos = 0;
- int len = buffer.length;
- int rem;
- // write data to the buffers
- for (int i=offset; i<offset+length; i++) {
- rem = dsts[i].remaining();
- // TODO: optimization work - use hasArray, array(), arraycopy
- if (len - pos < rem) {
- // can fully write remaining data into buffer
- dsts[i].put(buffer, pos, len - pos);
- pos = len;
- // data was written, exit
- break;
- }
- // write chunk of data
- dsts[i].put(buffer, pos, rem);
- pos += rem;
- }
- if (pos != len) {
- // The data did not feet into the buffers,
- // it should not happen, because the destination buffers
- // had been checked for the space before record unwrapping.
- // But if it so, we should allert about internal error.
- throw new AlertException(
- AlertProtocol.INTERNAL_ERROR,
- new SSLException(
- "The received application data could not be fully written"
- + "into the destination buffers"));
- }
- buffer = null;
- return len;
- }
-}
-
diff --git a/src/main/java/org/conscrypt/SSLEngineDataStream.java b/src/main/java/org/conscrypt/SSLEngineDataStream.java
deleted file mode 100644
index 8dfdb9f..0000000
--- a/src/main/java/org/conscrypt/SSLEngineDataStream.java
+++ /dev/null
@@ -1,93 +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.nio.ByteBuffer;
-
-/**
- * This class provides the DataStream functionality
- * implemented over the array of ByteBuffer instances.
- * Among with the data chunks read functionality
- * it provides the info about amount of consumed data.
- * The source ByteBuffer objects can be replaced by other.
- * So one instance of this wrapper can be reused for several
- * data sources.
- */
-public class SSLEngineDataStream implements DataStream {
-
- private ByteBuffer[] srcs;
- private int offset;
- private int limit;
-
- private int available;
- private int consumed;
-
- protected SSLEngineDataStream() {}
-
- protected void setSourceBuffers(ByteBuffer[] srcs, int offset, int length) {
- this.srcs = srcs;
- this.offset = offset;
- this.limit = offset+length;
- this.consumed = 0;
- this.available = 0;
- for (int i=offset; i<limit; i++) {
- if (srcs[i] == null) {
- throw new IllegalStateException(
- "Some of the input parameters are null");
- }
- available += srcs[i].remaining();
- }
- }
-
- public int available() {
- return available;
- }
-
- @Override
- public boolean hasData() {
- return available > 0;
- }
-
- @Override
- public byte[] getData(int length) {
- // TODO: optimization work:
- // use ByteBuffer.get(byte[],int,int)
- // and ByteBuffer.hasArray() methods
- int len = (length < available) ? length : available;
- available -= len;
- consumed += len;
- byte[] res = new byte[len];
- int pos = 0;
- loop:
- for (; offset<limit; offset++) {
- while (srcs[offset].hasRemaining()) {
- res[pos++] = srcs[offset].get();
- len --;
- if (len == 0) {
- break loop;
- }
- }
- }
- return res;
- }
-
- protected int consumed() {
- return consumed;
- }
-}
-
diff --git a/src/main/java/org/conscrypt/SSLEngineImpl.java b/src/main/java/org/conscrypt/SSLEngineImpl.java
deleted file mode 100644
index 3ed9980..0000000
--- a/src/main/java/org/conscrypt/SSLEngineImpl.java
+++ /dev/null
@@ -1,753 +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.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ReadOnlyBufferException;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLSession;
-
-/**
- * Implementation of SSLEngine.
- * @see javax.net.ssl.SSLEngine class documentation for more information.
- */
-public class SSLEngineImpl extends SSLEngine {
-
- // indicates if peer mode was set
- private boolean peer_mode_was_set = false;
- // indicates if handshake has been started
- private boolean handshake_started = false;
- // indicates if inbound operations finished
- private boolean isInboundDone = false;
- // indicates if outbound operations finished
- private boolean isOutboundDone = false;
- // indicates if close_notify alert had been sent to another peer
- private boolean close_notify_was_sent = false;
- // indicates if close_notify alert had been received from another peer
- private boolean close_notify_was_received = false;
- // indicates if engine was closed (it means that
- // all the works on it are done, except (probably) some finalizing work)
- private boolean engine_was_closed = false;
- // indicates if engine was shutted down (it means that
- // all cleaning work had been done and the engine is not operable)
- private boolean engine_was_shutteddown = false;
-
- // record protocol to be used
- protected SSLRecordProtocol recordProtocol;
- // input stream for record protocol
- private SSLBufferedInput recProtIS;
- // handshake protocol to be used
- private HandshakeProtocol handshakeProtocol;
- // alert protocol to be used
- private AlertProtocol alertProtocol;
- // place where application data will be stored
- private SSLEngineAppData appData;
- // outcoming application data stream
- private SSLEngineDataStream dataStream = new SSLEngineDataStream();
- // active session object
- private SSLSessionImpl session;
-
- // peer configuration parameters
- protected SSLParametersImpl sslParameters;
-
- // in case of emergency situations when data could not be
- // placed in destination buffers it will be stored in this
- // fields
- private byte[] remaining_wrapped_data = null;
- private byte[] remaining_hsh_data = null;
-
- // logger
- private Logger.Stream logger = Logger.getStream("engine");
-
- protected SSLEngineImpl(SSLParametersImpl sslParameters) {
- this.sslParameters = sslParameters;
- }
-
- protected SSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) {
- super(host, port);
- this.sslParameters = sslParameters;
- }
-
- /**
- * Starts the handshake.
- * @throws SSLException
- * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation
- * for more information
- */
- @Override
- public void beginHandshake() throws SSLException {
- if (engine_was_closed) {
- throw new SSLException("Engine has already been closed.");
- }
- if (!peer_mode_was_set) {
- throw new IllegalStateException("Client/Server mode was not set");
- }
- if (!handshake_started) {
- handshake_started = true;
- if (getUseClientMode()) {
- handshakeProtocol = new ClientHandshakeImpl(this);
- } else {
- handshakeProtocol = new ServerHandshakeImpl(this);
- }
- appData = new SSLEngineAppData();
- alertProtocol = new AlertProtocol();
- recProtIS = new SSLBufferedInput();
- recordProtocol = new SSLRecordProtocol(handshakeProtocol,
- alertProtocol, recProtIS, appData);
- }
- handshakeProtocol.start();
- }
-
- /**
- * Closes inbound operations of this engine
- * @throws SSLException
- * @see javax.net.ssl.SSLEngine#closeInbound() method documentation
- * for more information
- */
- @Override
- public void closeInbound() throws SSLException {
- if (logger != null) {
- logger.println("closeInbound() "+isInboundDone);
- }
- if (isInboundDone) {
- return;
- }
- isInboundDone = true;
- engine_was_closed = true;
- if (handshake_started) {
- if (!close_notify_was_received) {
- if (session != null) {
- session.invalidate();
- }
- alertProtocol.alert(AlertProtocol.FATAL,
- AlertProtocol.INTERNAL_ERROR);
- throw new SSLException("Inbound is closed before close_notify "
- + "alert has been received.");
- }
- } else {
- // engine is closing before initial handshake has been made
- shutdown();
- }
- }
-
- /**
- * Closes outbound operations of this engine
- * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation
- * for more information
- */
- @Override
- public void closeOutbound() {
- if (logger != null) {
- logger.println("closeOutbound() "+isOutboundDone);
- }
- if (isOutboundDone) {
- return;
- }
- isOutboundDone = true;
- if (handshake_started) {
- // initial handshake had been started
- alertProtocol.alert(AlertProtocol.WARNING,
- AlertProtocol.CLOSE_NOTIFY);
- close_notify_was_sent = true;
- } else {
- // engine is closing before initial handshake has been made
- shutdown();
- }
- engine_was_closed = true;
- }
-
- /**
- * Returns handshake's delegated tasks to be run
- * @return the delegated task to be executed.
- * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation
- * for more information
- */
- @Override
- public Runnable getDelegatedTask() {
- return handshakeProtocol.getTask();
- }
-
- /**
- * Returns names of supported cipher suites.
- * @return array of strings containing the names of supported cipher suites
- * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method
- * documentation for more information
- */
- @Override
- public String[] getSupportedCipherSuites() {
- return CipherSuite.getSupportedCipherSuiteNames();
- }
-
- // --------------- SSLParameters based methods ---------------------
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method
- * documentation for more information
- */
- @Override
- public String[] getEnabledCipherSuites() {
- return sslParameters.getEnabledCipherSuites();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[]) method
- * documentation for more information
- */
- @Override
- public void setEnabledCipherSuites(String[] suites) {
- sslParameters.setEnabledCipherSuites(suites);
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method
- * documentation for more information
- */
- @Override
- public String[] getSupportedProtocols() {
- return ProtocolVersion.supportedProtocols.clone();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method
- * documentation for more information
- */
- @Override
- public String[] getEnabledProtocols() {
- return sslParameters.getEnabledProtocols();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String[]) method
- * documentation for more information
- */
- @Override
- public void setEnabledProtocols(String[] protocols) {
- sslParameters.setEnabledProtocols(protocols);
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method
- * documentation for more information
- */
- @Override
- public void setUseClientMode(boolean mode) {
- if (handshake_started) {
- throw new IllegalArgumentException(
- "Could not change the mode after the initial handshake has begun.");
- }
- sslParameters.setUseClientMode(mode);
- peer_mode_was_set = true;
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getUseClientMode() method
- * documentation for more information
- */
- @Override
- public boolean getUseClientMode() {
- return sslParameters.getUseClientMode();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method
- * documentation for more information
- */
- @Override
- public void setNeedClientAuth(boolean need) {
- sslParameters.setNeedClientAuth(need);
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method
- * documentation for more information
- */
- @Override
- public boolean getNeedClientAuth() {
- return sslParameters.getNeedClientAuth();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method
- * documentation for more information
- */
- @Override
- public void setWantClientAuth(boolean want) {
- sslParameters.setWantClientAuth(want);
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getWantClientAuth() method
- * documentation for more information
- */
- @Override
- public boolean getWantClientAuth() {
- return sslParameters.getWantClientAuth();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method
- * documentation for more information
- */
- @Override
- public void setEnableSessionCreation(boolean flag) {
- sslParameters.setEnableSessionCreation(flag);
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method
- * documentation for more information
- */
- @Override
- public boolean getEnableSessionCreation() {
- return sslParameters.getEnableSessionCreation();
- }
-
- // -----------------------------------------------------------------
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method
- * documentation for more information
- */
- @Override
- public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
- if (!handshake_started || engine_was_shutteddown) {
- // initial handshake has not been started yet
- return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
- }
- if (alertProtocol.hasAlert()) {
- // need to send an alert
- return SSLEngineResult.HandshakeStatus.NEED_WRAP;
- }
- if (close_notify_was_sent && !close_notify_was_received) {
- // waiting for "close_notify" response
- return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
- }
- return handshakeProtocol.getStatus();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#getSession() method
- * documentation for more information
- */
- @Override
- public SSLSession getSession() {
- if (session != null) {
- return session;
- }
- return SSLSessionImpl.getNullSession();
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#isInboundDone() method
- * documentation for more information
- */
- @Override
- public boolean isInboundDone() {
- return isInboundDone || engine_was_closed;
- }
-
- /**
- * This method works according to the specification of implemented class.
- * @see javax.net.ssl.SSLEngine#isOutboundDone() method
- * documentation for more information
- */
- @Override
- public boolean isOutboundDone() {
- return isOutboundDone;
- }
-
- /**
- * Decodes one complete SSL/TLS record provided in the source buffer.
- * If decoded record contained application data, this data will
- * be placed in the destination buffers.
- * For more information about TLS record fragmentation see
- * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
- * @param src source buffer containing SSL/TLS record.
- * @param dsts destination buffers to place received application data.
- * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int)
- * method documentation for more information
- */
- @Override
- public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts,
- int offset, int length) throws SSLException {
- if (engine_was_shutteddown) {
- return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
- SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
- }
- if ((src == null) || (dsts == null)) {
- throw new IllegalStateException(
- "Some of the input parameters are null");
- }
-
- if (!handshake_started) {
- beginHandshake();
- }
-
- SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
- // If is is initial handshake or connection closure stage,
- // check if this call was made in spite of handshake status
- if ((session == null || engine_was_closed) && (
- handshakeStatus.equals(
- SSLEngineResult.HandshakeStatus.NEED_WRAP) ||
- handshakeStatus.equals(
- SSLEngineResult.HandshakeStatus.NEED_TASK))) {
- return new SSLEngineResult(
- getEngineStatus(), handshakeStatus, 0, 0);
- }
-
- if (src.remaining() < recordProtocol.getMinRecordSize()) {
- return new SSLEngineResult(
- SSLEngineResult.Status.BUFFER_UNDERFLOW,
- getHandshakeStatus(), 0, 0);
- }
-
- try {
- src.mark();
- // check the destination buffers and count their capacity
- int capacity = 0;
- for (int i=offset; i<offset+length; i++) {
- if (dsts[i] == null) {
- throw new IllegalStateException(
- "Some of the input parameters are null");
- }
- if (dsts[i].isReadOnly()) {
- throw new ReadOnlyBufferException();
- }
- capacity += dsts[i].remaining();
- }
- if (capacity < recordProtocol.getDataSize(src.remaining())) {
- return new SSLEngineResult(
- SSLEngineResult.Status.BUFFER_OVERFLOW,
- getHandshakeStatus(), 0, 0);
- }
- recProtIS.setSourceBuffer(src);
- // unwrap the record contained in source buffer, pass it
- // to appropriate client protocol (alert, handshake, or app)
- // and retrieve the type of unwrapped data
- int type = recordProtocol.unwrap();
- // process the data and return the result
- switch (type) {
- case ContentType.HANDSHAKE:
- case ContentType.CHANGE_CIPHER_SPEC:
- if (handshakeProtocol.getStatus().equals(
- SSLEngineResult.HandshakeStatus.FINISHED)) {
- session = recordProtocol.getSession();
- }
- break;
- case ContentType.APPLICATION_DATA:
- break;
- case ContentType.ALERT:
- if (alertProtocol.isFatalAlert()) {
- alertProtocol.setProcessed();
- if (session != null) {
- session.invalidate();
- }
- String description = "Fatal alert received "
- + alertProtocol.getAlertDescription();
- shutdown();
- throw new SSLException(description);
- } else {
- if (logger != null) {
- logger.println("Warning allert has been received: "
- + alertProtocol.getAlertDescription());
- }
- switch(alertProtocol.getDescriptionCode()) {
- case AlertProtocol.CLOSE_NOTIFY:
- alertProtocol.setProcessed();
- close_notify_was_received = true;
- if (!close_notify_was_sent) {
- closeOutbound();
- closeInbound();
- } else {
- closeInbound();
- shutdown();
- }
- break;
- case AlertProtocol.NO_RENEGOTIATION:
- alertProtocol.setProcessed();
- if (session == null) {
- // message received during the initial
- // handshake
- throw new AlertException(
- AlertProtocol.HANDSHAKE_FAILURE,
- new SSLHandshakeException(
- "Received no_renegotiation "
- + "during the initial handshake"));
- } else {
- // just stop the handshake
- handshakeProtocol.stop();
- }
- break;
- default:
- alertProtocol.setProcessed();
- }
- }
- break;
- }
- return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(),
- recProtIS.consumed(),
- // place the app. data (if any) into the dest. buffers
- // and get the number of produced bytes:
- appData.placeTo(dsts, offset, length));
- } catch (BufferUnderflowException e) {
- // there was not enought data ource buffer to make complete packet
- src.reset();
- return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
- getHandshakeStatus(), 0, 0);
- } catch (AlertException e) {
- // fatal alert occured
- alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
- engine_was_closed = true;
- src.reset();
- if (session != null) {
- session.invalidate();
- }
- // shutdown work will be made after the alert will be sent
- // to another peer (by wrap method)
- throw e.getReason();
- } catch (SSLException e) {
- throw e;
- } catch (IOException e) {
- alertProtocol.alert(AlertProtocol.FATAL,
- AlertProtocol.INTERNAL_ERROR);
- engine_was_closed = true;
- // shutdown work will be made after the alert will be sent
- // to another peer (by wrap method)
- throw new SSLException(e.getMessage());
- }
- }
-
- /**
- * Encodes the application data into SSL/TLS record. If handshake status
- * of the engine differs from NOT_HANDSHAKING the operation can work
- * without consuming of the source data.
- * For more information about TLS record fragmentation see
- * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
- * @param srcs the source buffers with application data to be encoded
- * into SSL/TLS record.
- * @param offset the offset in the destination buffers array pointing to
- * the first buffer with the source data.
- * @param len specifies the maximum number of buffers to be procesed.
- * @param dst the destination buffer where encoded data will be placed.
- * @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method
- * documentation for more information
- */
- @Override
- public SSLEngineResult wrap(ByteBuffer[] srcs, int offset,
- int len, ByteBuffer dst) throws SSLException {
- if (engine_was_shutteddown) {
- return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
- SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
- }
- if ((srcs == null) || (dst == null)) {
- throw new IllegalStateException(
- "Some of the input parameters are null");
- }
- if (dst.isReadOnly()) {
- throw new ReadOnlyBufferException();
- }
-
- if (!handshake_started) {
- beginHandshake();
- }
-
- SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
- // If it is an initial handshake or connection closure stage,
- // check if this call was made in spite of handshake status
- if ((session == null || engine_was_closed) && (
- handshakeStatus.equals(
- SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
- handshakeStatus.equals(
- SSLEngineResult.HandshakeStatus.NEED_TASK))) {
- return new SSLEngineResult(
- getEngineStatus(), handshakeStatus, 0, 0);
- }
-
- int capacity = dst.remaining();
- int produced = 0;
-
- if (alertProtocol.hasAlert()) {
- // we have an alert to be sent
- if (capacity < recordProtocol.getRecordSize(2)) {
- return new SSLEngineResult(
- SSLEngineResult.Status.BUFFER_OVERFLOW,
- handshakeStatus, 0, 0);
- }
- byte[] alert_data = alertProtocol.wrap();
- // place the alert record into destination
- dst.put(alert_data);
- if (alertProtocol.isFatalAlert()) {
- alertProtocol.setProcessed();
- if (session != null) {
- session.invalidate();
- }
- // fatal alert has been sent, so shut down the engine
- shutdown();
- return new SSLEngineResult(
- SSLEngineResult.Status.CLOSED,
- SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
- 0, alert_data.length);
- } else {
- alertProtocol.setProcessed();
- // check if the works on this engine have been done
- if (close_notify_was_sent && close_notify_was_received) {
- shutdown();
- return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
- SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
- 0, alert_data.length);
- }
- return new SSLEngineResult(
- getEngineStatus(),
- getHandshakeStatus(),
- 0, alert_data.length);
- }
- }
-
- if (capacity < recordProtocol.getMinRecordSize()) {
- if (logger != null) {
- logger.println("Capacity of the destination("
- +capacity+") < MIN_PACKET_SIZE("
- +recordProtocol.getMinRecordSize()+")");
- }
- return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
- handshakeStatus, 0, 0);
- }
-
- try {
- if (!handshakeStatus.equals(
- SSLEngineResult.HandshakeStatus.NEED_WRAP)) {
- // so we wraps application data
- dataStream.setSourceBuffers(srcs, offset, len);
- if ((capacity < SSLRecordProtocol.MAX_SSL_PACKET_SIZE) &&
- (capacity < recordProtocol.getRecordSize(
- dataStream.available()))) {
- if (logger != null) {
- logger.println("The destination buffer("
- +capacity+") can not take the resulting packet("
- + recordProtocol.getRecordSize(
- dataStream.available())+")");
- }
- return new SSLEngineResult(
- SSLEngineResult.Status.BUFFER_OVERFLOW,
- handshakeStatus, 0, 0);
- }
- if (remaining_wrapped_data == null) {
- remaining_wrapped_data =
- recordProtocol.wrap(ContentType.APPLICATION_DATA,
- dataStream);
- }
- if (capacity < remaining_wrapped_data.length) {
- // It should newer happen because we checked the destination
- // buffer size, but there is a possibility
- // (if dest buffer was filled outside)
- // so we just remember the data into remaining_wrapped_data
- // and will enclose it during the the next call
- return new SSLEngineResult(
- SSLEngineResult.Status.BUFFER_OVERFLOW,
- handshakeStatus, dataStream.consumed(), 0);
- } else {
- dst.put(remaining_wrapped_data);
- produced = remaining_wrapped_data.length;
- remaining_wrapped_data = null;
- return new SSLEngineResult(getEngineStatus(),
- handshakeStatus, dataStream.consumed(), produced);
- }
- } else {
- if (remaining_hsh_data == null) {
- remaining_hsh_data = handshakeProtocol.wrap();
- }
- if (capacity < remaining_hsh_data.length) {
- // It should newer happen because we checked the destination
- // buffer size, but there is a possibility
- // (if dest buffer was filled outside)
- // so we just remember the data into remaining_hsh_data
- // and will enclose it during the the next call
- return new SSLEngineResult(
- SSLEngineResult.Status.BUFFER_OVERFLOW,
- handshakeStatus, 0, 0);
- } else {
- dst.put(remaining_hsh_data);
- produced = remaining_hsh_data.length;
- remaining_hsh_data = null;
-
- handshakeStatus = handshakeProtocol.getStatus();
- if (handshakeStatus.equals(
- SSLEngineResult.HandshakeStatus.FINISHED)) {
- session = recordProtocol.getSession();
- }
- }
- return new SSLEngineResult(
- getEngineStatus(), getHandshakeStatus(), 0, produced);
- }
- } catch (AlertException e) {
- // fatal alert occured
- alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
- engine_was_closed = true;
- if (session != null) {
- session.invalidate();
- }
- // shutdown work will be made after the alert will be sent
- // to another peer (by wrap method)
- throw e.getReason();
- }
- }
-
- // Shutdownes the engine and makes all cleanup work.
- private void shutdown() {
- engine_was_closed = true;
- engine_was_shutteddown = true;
- isOutboundDone = true;
- isInboundDone = true;
- if (handshake_started) {
- alertProtocol.shutdown();
- alertProtocol = null;
- handshakeProtocol.shutdown();
- handshakeProtocol = null;
- recordProtocol.shutdown();
- recordProtocol = null;
- }
- }
-
-
- private SSLEngineResult.Status getEngineStatus() {
- return (engine_was_closed)
- ? SSLEngineResult.Status.CLOSED
- : SSLEngineResult.Status.OK;
- }
-}
diff --git a/src/main/java/org/conscrypt/SSLInputStream.java b/src/main/java/org/conscrypt/SSLInputStream.java
deleted file mode 100644
index c8a5eca..0000000
--- a/src/main/java/org/conscrypt/SSLInputStream.java
+++ /dev/null
@@ -1,113 +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.io.InputStream;
-
-/**
- * This class is a base for all input stream classes used
- * in protocol implementation. It extends an InputStream with
- * some additional read methods allowing to read TLS specific
- * data types such as uint8, uint32 etc (see TLS v 1 specification
- * at http://www.ietf.org/rfc/rfc2246.txt).
- */
-public abstract class SSLInputStream extends InputStream {
-
- @Override
- public abstract int available() throws IOException;
-
- /**
- * Reads the following byte value. Note that in the case of
- * reaching of the end of the data this methods throws the
- * exception, not return -1. The type of exception depends
- * on implementation. It was done for simplifying and speeding
- * up of processing of such cases.
- * @see org.conscrypt.SSLStreamedInput#read()
- * @see org.conscrypt.SSLBufferedInput#read()
- * @see org.conscrypt.HandshakeIODataStream#read()
- */
- @Override
- public abstract int read() throws IOException;
-
- /**
- * Reads and returns uint8 value.
- */
- public int readUint8() throws IOException {
- return read() & 0x00FF;
- }
-
- /**
- * Reads and returns uint16 value.
- */
- public int readUint16() throws IOException {
- return (read() << 8) | (read() & 0x00FF);
- }
-
- /**
- * Reads and returns uint24 value.
- */
- public int readUint24() throws IOException {
- return (read() << 16) | (read() << 8) | (read() & 0x00FF);
- }
-
- /**
- * Reads and returns uint32 value.
- */
- public long readUint32() throws IOException {
- return (read() << 24) | (read() << 16)
- | (read() << 8) | (read() & 0x00FF);
- }
-
- /**
- * Reads and returns uint64 value.
- */
- public long readUint64() throws IOException {
- long hi = readUint32();
- long lo = readUint32();
- return (hi << 32) | lo;
- }
-
- /**
- * Returns the vector of opaque values of specified length;
- * @param length - the length of the vector to be read.
- * @return the read data
- * @throws IOException if read operation could not be finished.
- */
- public byte[] read(int length) throws IOException {
- byte[] res = new byte[length];
- for (int i=0; i<length; i++) {
- res[i] = (byte) read();
- }
- return res;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int read_b;
- int i = 0;
- do {
- if ((read_b = read()) == -1) {
- return (i == 0) ? -1 : i;
- }
- b[off+i] = (byte) read_b;
- i++;
- } while ((available() != 0) && (i<len));
- return i;
- }
-}
diff --git a/src/main/java/org/conscrypt/SSLNullSession.java b/src/main/java/org/conscrypt/SSLNullSession.java
new file mode 100644
index 0000000..7fbf072
--- /dev/null
+++ b/src/main/java/org/conscrypt/SSLNullSession.java
@@ -0,0 +1,180 @@
+/*
+ * 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 org.conscrypt.util.EmptyArray;
+
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+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;
+
+public final class SSLNullSession implements SSLSession, Cloneable {
+
+ /*
+ * Holds default instances so class preloading doesn't create an instance of
+ * it.
+ */
+ private static class DefaultHolder {
+ public static final SSLNullSession NULL_SESSION = new SSLNullSession();
+ }
+
+ private final HashMap<String, Object> values = new HashMap<String, Object>();
+
+ long creationTime;
+ long lastAccessedTime;
+
+ public static SSLSession getNullSession() {
+ return DefaultHolder.NULL_SESSION;
+ }
+
+ public SSLNullSession() {
+ creationTime = System.currentTimeMillis();
+ lastAccessedTime = creationTime;
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ return SSLRecordProtocol.MAX_DATA_LENGTH;
+ }
+
+ @Override
+ public String getCipherSuite() {
+ return "SSL_NULL_WITH_NULL_NULL";
+ }
+
+ @Override
+ public long getCreationTime() {
+ return creationTime;
+ }
+
+ @Override
+ public byte[] getId() {
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ return lastAccessedTime;
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ return null;
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ return null;
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ return SSLRecordProtocol.MAX_SSL_PACKET_SIZE;
+ }
+
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+
+ @Override
+ public String getPeerHost() {
+ return null;
+ }
+
+ @Override
+ public int getPeerPort() {
+ return -1;
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+
+ @Override
+ public String getProtocol() {
+ return "NONE";
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ return null;
+ }
+
+ @Override
+ public Object getValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name == null");
+ }
+ return values.get(name);
+ }
+
+ @Override
+ public String[] getValueNames() {
+ return values.keySet().toArray(new String[values.size()]);
+ }
+
+ @Override
+ public void invalidate() {
+ }
+
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ @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));
+ }
+
+ }
+
+ @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));
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/SSLParametersImpl.java b/src/main/java/org/conscrypt/SSLParametersImpl.java
index 21b8ae2..1201b05 100644
--- a/src/main/java/org/conscrypt/SSLParametersImpl.java
+++ b/src/main/java/org/conscrypt/SSLParametersImpl.java
@@ -17,19 +17,33 @@
package org.conscrypt;
+import org.conscrypt.util.EmptyArray;
+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.SecureRandom;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
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.TrustManager;
import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
/**
* The instances of this class encapsulate all the info
@@ -41,10 +55,10 @@
*/
public class SSLParametersImpl implements Cloneable {
- // default source of authentication keys
- private static volatile X509KeyManager defaultKeyManager;
- // default source of authentication trust decisions
- private static volatile X509TrustManager defaultTrustManager;
+ // default source of X.509 certificate based authentication keys
+ 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
@@ -56,20 +70,19 @@
// server session context contains the set of reusable
// server-side SSL sessions
private final ServerSessionContext serverSessionContext;
- // source of authentication keys
- private X509KeyManager keyManager;
- // source of authentication trust decisions
- private X509TrustManager trustManager;
+ // source of X.509 certificate based authentication keys or null if not provided
+ private final X509KeyManager x509KeyManager;
+ // source of Pre-Shared Key (PSK) authentication keys or null if not provided.
+ private final PSKKeyManager pskKeyManager;
+ // source of X.509 certificate based authentication trust decisions or null if not provided
+ private final X509TrustManager x509TrustManager;
// source of random numbers
private SecureRandom secureRandom;
- // cipher suites available for SSL connection
- private CipherSuite[] enabledCipherSuites;
- // string representations of available cipher suites
- private String[] enabledCipherSuiteNames = null;
-
- // protocols available for SSL connection
- private String[] enabledProtocols = ProtocolVersion.supportedProtocols;
+ // protocols enabled for SSL connection
+ private String[] enabledProtocols;
+ // cipher suites enabled for SSL connection
+ private String[] enabledCipherSuites;
// if the peer with this parameters tuned to work in client mode
private boolean client_mode = true;
@@ -79,13 +92,18 @@
private boolean want_client_auth = false;
// if the peer with this parameters allowed to cteate new SSL session
private boolean enable_session_creation = true;
+ private String endpointIdentificationAlgorithm;
- protected CipherSuite[] getEnabledCipherSuitesMember() {
- if (enabledCipherSuites == null) {
- this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES;
- }
- return enabledCipherSuites;
- }
+ byte[] npnProtocols;
+ byte[] alpnProtocols;
+ boolean useSessionTickets;
+ boolean useSni;
+
+ /**
+ * Whether the TLS Channel ID extension is enabled. This field is
+ * server-side only.
+ */
+ boolean channelIdEnabled;
/**
* Initializes the parameters. Naturally this constructor is used
@@ -102,41 +120,36 @@
this.serverSessionContext = serverSessionContext;
this.clientSessionContext = clientSessionContext;
- // It's not described by the spec of SSLContext what should happen
- // if the arrays of length 0 are specified. This implementation
- // behave as for null arrays (i.e. use installed security providers)
-
- // initialize keyManager
- if ((kms == null) || (kms.length == 0)) {
- keyManager = getDefaultKeyManager();
+ // initialize key managers
+ if (kms == null) {
+ x509KeyManager = getDefaultX509KeyManager();
+ // There's no default PSK key manager
+ pskKeyManager = null;
} else {
- keyManager = findX509KeyManager(kms);
+ x509KeyManager = findFirstX509KeyManager(kms);
+ pskKeyManager = findFirstPSKKeyManager(kms);
}
- // initialize trustManager
- if ((tms == null) || (tms.length == 0)) {
- trustManager = getDefaultTrustManager();
+ // initialize x509TrustManager
+ if (tms == null) {
+ x509TrustManager = getDefaultX509TrustManager();
} else {
- trustManager = findX509TrustManager(tms);
+ x509TrustManager = findFirstX509TrustManager(tms);
}
+
// initialize secure random
- // BEGIN android-removed
- // if (sr == null) {
- // if (defaultSecureRandom == null) {
- // defaultSecureRandom = new SecureRandom();
- // }
- // secureRandom = defaultSecureRandom;
- // } else {
- // secureRandom = sr;
- // }
- // END android-removed
- // BEGIN android-added
// We simply use the SecureRandom passed in by the caller. If it's
// null, we don't replace it by a new instance. The native code below
// then directly accesses /dev/urandom. Not the most elegant solution,
// but faster than going through the SecureRandom object.
secureRandom = sr;
- // END android-added
+
+ // initialize the list of cipher suites and protocols enabled by default
+ enabledProtocols = getDefaultProtocols();
+ boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
+ boolean pskCipherSuitesNeeded = pskKeyManager != null;
+ enabledCipherSuites = getDefaultCipherSuites(
+ x509CipherSuitesNeeded, pskCipherSuitesNeeded);
}
protected static SSLParametersImpl getDefault() throws KeyManagementException {
@@ -153,6 +166,13 @@
}
/**
+ * Returns the appropriate session context.
+ */
+ public AbstractSessionContext getSessionContext() {
+ return client_mode ? clientSessionContext : serverSessionContext;
+ }
+
+ /**
* @return server session context
*/
protected ServerSessionContext getServerSessionContext() {
@@ -167,17 +187,24 @@
}
/**
- * @return key manager
+ * @return X.509 key manager or {@code null} for none.
*/
- protected X509KeyManager getKeyManager() {
- return keyManager;
+ protected X509KeyManager getX509KeyManager() {
+ return x509KeyManager;
}
/**
- * @return trust manager
+ * @return Pre-Shared Key (PSK) key manager or {@code null} for none.
*/
- protected X509TrustManager getTrustManager() {
- return trustManager;
+ protected PSKKeyManager getPSKKeyManager() {
+ return pskKeyManager;
+ }
+
+ /**
+ * @return X.509 trust manager or {@code null} for none.
+ */
+ protected X509TrustManager getX509TrustManager() {
+ return x509TrustManager;
}
/**
@@ -207,38 +234,14 @@
* @return the names of enabled cipher suites
*/
protected String[] getEnabledCipherSuites() {
- if (enabledCipherSuiteNames == null) {
- CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
- enabledCipherSuiteNames = new String[enabledCipherSuites.length];
- for (int i = 0; i< enabledCipherSuites.length; i++) {
- enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName();
- }
- }
- return enabledCipherSuiteNames.clone();
+ return enabledCipherSuites.clone();
}
/**
- * Sets the set of available cipher suites for use in SSL connection.
- * @param suites: String[]
- * @return
+ * Sets the enabled cipher suites after filtering through OpenSSL.
*/
- protected void setEnabledCipherSuites(String[] suites) {
- if (suites == null) {
- throw new IllegalArgumentException("suites == null");
- }
- CipherSuite[] cipherSuites = new CipherSuite[suites.length];
- for (int i=0; i<suites.length; i++) {
- String suite = suites[i];
- if (suite == null) {
- throw new IllegalArgumentException("suites[" + i + "] == null");
- }
- cipherSuites[i] = CipherSuite.getByName(suite);
- if (cipherSuites[i] == null || !cipherSuites[i].supported) {
- throw new IllegalArgumentException(suite + " is not supported.");
- }
- }
- enabledCipherSuites = cipherSuites;
- enabledCipherSuiteNames = suites;
+ protected void setEnabledCipherSuites(String[] cipherSuites) {
+ enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(cipherSuites).clone();
}
/**
@@ -253,19 +256,7 @@
* @param protocols String[]
*/
protected void setEnabledProtocols(String[] protocols) {
- if (protocols == null) {
- throw new IllegalArgumentException("protocols == null");
- }
- for (int i=0; i<protocols.length; i++) {
- String protocol = protocols[i];
- if (protocol == null) {
- throw new IllegalArgumentException("protocols[" + i + "] == null");
- }
- if (!ProtocolVersion.isSupported(protocol)) {
- throw new IllegalArgumentException("Protocol " + protocol + " is not supported.");
- }
- }
- enabledProtocols = protocols;
+ enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols).clone();
}
/**
@@ -313,7 +304,6 @@
/**
* Returns the value indicating if the peer with this parameters
* tuned to request client authentication
- * @return
*/
protected boolean getWantClientAuth() {
return want_client_auth;
@@ -336,6 +326,421 @@
}
/**
+ * Whether connections using this SSL connection should use the TLS
+ * extension Server Name Indication (SNI).
+ */
+ protected void setUseSni(boolean flag) {
+ useSni = flag;
+ }
+
+ /**
+ * Returns whether connections using this SSL connection should use the TLS
+ * extension Server Name Indication (SNI).
+ */
+ protected boolean getUseSni() {
+ return useSni;
+ }
+
+ 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;
+ }
+
+ /**
+ * Return a possibly null array of X509Certificates given the possibly null
+ * array of DER encoded bytes.
+ */
+ private static OpenSSLX509Certificate[] createCertChain(long[] certificateRefs)
+ throws IOException {
+ if (certificateRefs == null) {
+ return null;
+ }
+ OpenSSLX509Certificate[] certificates = new OpenSSLX509Certificate[certificateRefs.length];
+ for (int i = 0; i < certificateRefs.length; i++) {
+ certificates[i] = new OpenSSLX509Certificate(certificateRefs[i]);
+ }
+ return certificates;
+ }
+
+ OpenSSLSessionImpl getSessionToReuse(long sslNativePointer, String hostname, int port)
+ throws SSLException {
+ final OpenSSLSessionImpl sessionToReuse;
+ if (client_mode) {
+ // look for client session to reuse
+ sessionToReuse = getCachedClientSession(clientSessionContext, hostname, port);
+ if (sessionToReuse != null) {
+ NativeCrypto.SSL_set_session(sslNativePointer,
+ sessionToReuse.sslSessionNativePointer);
+ }
+ } else {
+ sessionToReuse = null;
+ }
+ 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.getPkeyContext());
+ } 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;
+ }
+
+ /*
+ * 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);
+
+ try {
+ final OpenSSLKey key = OpenSSLKey.fromPrivateKey(privateKey);
+ NativeCrypto.SSL_use_PrivateKey(sslNativePointer, key.getPkeyContext());
+ } catch (InvalidKeyException e) {
+ throw new SSLException(e);
+ }
+
+ // checks the last installed private key and certificate,
+ // so need to do this once per loop iteration
+ NativeCrypto.SSL_check_private_key(sslNativePointer);
+ }
+
+ void setSSLParameters(long sslCtxNativePointer, long sslNativePointer, AliasChooser chooser,
+ PSKCallbacks pskCallbacks, String hostname) throws SSLException, IOException {
+ if (npnProtocols != null) {
+ NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer);
+ }
+
+ if (client_mode && alpnProtocols != null) {
+ NativeCrypto.SSL_set_alpn_protos(sslNativePointer, 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);
+ }
+ }
+ }
+ }
+
+ // 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);
+ }
+ }
+ }
+
+ if (useSessionTickets) {
+ NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
+ }
+ if (useSni) {
+ NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname);
+ }
+
+ // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites
+ // with TLSv1 and SSLv3).
+ NativeCrypto.SSL_set_mode(sslNativePointer, NativeCrypto.SSL_MODE_CBC_RECORD_SPLITTING);
+
+ boolean enableSessionCreation = getEnableSessionCreation();
+ if (!enableSessionCreation) {
+ NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer, enableSessionCreation);
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+
+ OpenSSLSessionImpl setupSession(long sslSessionNativePointer, long sslNativePointer,
+ final OpenSSLSessionImpl sessionToReuse, String hostname, int port,
+ boolean handshakeCompleted) throws IOException {
+ OpenSSLSessionImpl sslSession = null;
+ byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
+ if (sessionToReuse != null && Arrays.equals(sessionToReuse.getId(), sessionId)) {
+ sslSession = sessionToReuse;
+ sslSession.lastAccessedTime = 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 = createCertChain(NativeCrypto
+ .SSL_get_certificate(sslNativePointer));
+ X509Certificate[] peerCertificates = createCertChain(NativeCrypto
+ .SSL_get_peer_cert_chain(sslNativePointer));
+ sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
+ peerCertificates, 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 {
+ String[] keyTypes = new String[keyTypeBytes.length];
+ for (int i = 0; i < keyTypeBytes.length; i++) {
+ keyTypes[i] = getClientKeyType(keyTypeBytes[i]);
+ }
+
+ 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[])
+ */
+ 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[])
+ */
+ 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.
+ */
+ OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext, String hostName,
+ int port) {
+ if (hostName == null) {
+ return null;
+ }
+ OpenSSLSessionImpl session = (OpenSSLSessionImpl) 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)}
+ * and
+ * {@link X509ExtendedKeyManager#chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine)}
+ */
+ public interface AliasChooser {
+ String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
+ String[] keyTypes);
+
+ String chooseServerAlias(X509KeyManager keyManager, String keyType);
+ }
+
+ /**
+ * For abstracting the {@code PSKKeyManager} calls between those taking an {@code SSLSocket} and
+ * those taking an {@code SSLEngine}.
+ */
+ public interface PSKCallbacks {
+ String chooseServerPSKIdentityHint(PSKKeyManager keyManager);
+ String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint);
+ SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity);
+ }
+
+ /**
* Returns the clone of this object.
* @return the clone.
*/
@@ -348,21 +753,26 @@
}
}
- private static X509KeyManager getDefaultKeyManager() throws KeyManagementException {
- X509KeyManager result = defaultKeyManager;
+ private static X509KeyManager getDefaultX509KeyManager() throws KeyManagementException {
+ X509KeyManager result = defaultX509KeyManager;
if (result == null) {
// single-check idiom
- defaultKeyManager = result = createDefaultKeyManager();
+ defaultX509KeyManager = result = createDefaultX509KeyManager();
}
return result;
}
- private static X509KeyManager createDefaultKeyManager() throws KeyManagementException {
+ private static X509KeyManager createDefaultX509KeyManager() throws KeyManagementException {
try {
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(null, null);
KeyManager[] kms = kmf.getKeyManagers();
- return findX509KeyManager(kms);
+ X509KeyManager result = findFirstX509KeyManager(kms);
+ if (result == null) {
+ throw new KeyManagementException("No X509KeyManager among default KeyManagers: "
+ + Arrays.toString(kms));
+ }
+ return result;
} catch (NoSuchAlgorithmException e) {
throw new KeyManagementException(e);
} catch (KeyStoreException e) {
@@ -371,35 +781,63 @@
throw new KeyManagementException(e);
}
}
- private static X509KeyManager findX509KeyManager(KeyManager[] kms) throws KeyManagementException {
+
+ /**
+ * Finds the first {@link X509KeyManager} element in the provided array.
+ *
+ * @return the first {@code X509KeyManager} or {@code null} if not found.
+ */
+ private static X509KeyManager findFirstX509KeyManager(KeyManager[] kms) {
for (KeyManager km : kms) {
if (km instanceof X509KeyManager) {
return (X509KeyManager)km;
}
}
- throw new KeyManagementException("Failed to find an X509KeyManager in " + Arrays.toString(kms));
+ return null;
}
/**
- * Gets the default trust manager.
+ * Finds the first {@link PSKKeyManager} element in the provided array.
+ *
+ * @return the first {@code PSKKeyManager} or {@code null} if not found.
+ */
+ private static PSKKeyManager findFirstPSKKeyManager(KeyManager[] kms) {
+ for (KeyManager km : kms) {
+ if (km instanceof PSKKeyManager) {
+ return (PSKKeyManager)km;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the default X.509 trust manager.
*
* TODO: Move this to a published API under dalvik.system.
*/
- public static X509TrustManager getDefaultTrustManager() throws KeyManagementException {
- X509TrustManager result = defaultTrustManager;
+ public static X509TrustManager getDefaultX509TrustManager()
+ throws KeyManagementException {
+ X509TrustManager result = defaultX509TrustManager;
if (result == null) {
// single-check idiom
- defaultTrustManager = result = createDefaultTrustManager();
+ defaultX509TrustManager = result = createDefaultX509TrustManager();
}
return result;
}
- private static X509TrustManager createDefaultTrustManager() throws KeyManagementException {
+
+ private static X509TrustManager createDefaultX509TrustManager()
+ throws KeyManagementException {
try {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init((KeyStore) null);
TrustManager[] tms = tmf.getTrustManagers();
- X509TrustManager trustManager = findX509TrustManager(tms);
+ X509TrustManager trustManager = findFirstX509TrustManager(tms);
+ if (trustManager == null) {
+ throw new KeyManagementException(
+ "No X509TrustManager in among default TrustManagers: "
+ + Arrays.toString(tms));
+ }
return trustManager;
} catch (NoSuchAlgorithmException e) {
throw new KeyManagementException(e);
@@ -407,12 +845,172 @@
throw new KeyManagementException(e);
}
}
- private static X509TrustManager findX509TrustManager(TrustManager[] tms) throws KeyManagementException {
+
+ /**
+ * Finds the first {@link X509TrustManager} element in the provided array.
+ *
+ * @return the first {@code X509TrustManager} or {@code null} if not found.
+ */
+ private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
- return (X509TrustManager)tm;
+ return (X509TrustManager) tm;
}
}
- throw new KeyManagementException("Failed to find an X509TrustManager in " + Arrays.toString(tms));
+ return null;
+ }
+
+ public String getEndpointIdentificationAlgorithm() {
+ return endpointIdentificationAlgorithm;
+ }
+
+ public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
+ this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
+ }
+
+ /** Key type: RSA. */
+ private static final String KEY_TYPE_RSA = "RSA";
+
+ /** Key type: DSA. */
+ private static final String KEY_TYPE_DSA = "DSA";
+
+ /** Key type: Diffie-Hellman with RSA signature. */
+ private static final String KEY_TYPE_DH_RSA = "DH_RSA";
+
+ /** Key type: Diffie-Hellman with DSA signature. */
+ private static final String KEY_TYPE_DH_DSA = "DH_DSA";
+
+ /** Key type: Elliptic Curve. */
+ private static final String KEY_TYPE_EC = "EC";
+
+ /** Key type: Eliiptic Curve with ECDSA signature. */
+ private static final String KEY_TYPE_EC_EC = "EC_EC";
+
+ /** Key type: Eliiptic Curve 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 {
+ int algorithm_mkey = NativeCrypto.get_SSL_CIPHER_algorithm_mkey(sslCipherNative);
+ int algorithm_auth = NativeCrypto.get_SSL_CIPHER_algorithm_auth(sslCipherNative);
+ switch (algorithm_mkey) {
+ case NativeCrypto.SSL_kRSA:
+ return KEY_TYPE_RSA;
+ case NativeCrypto.SSL_kEDH:
+ switch (algorithm_auth) {
+ case NativeCrypto.SSL_aDSS:
+ return KEY_TYPE_DSA;
+ case NativeCrypto.SSL_aRSA:
+ return KEY_TYPE_RSA;
+ case NativeCrypto.SSL_aNULL:
+ return null;
+ }
+ break;
+ case NativeCrypto.SSL_kECDHr:
+ return KEY_TYPE_EC_RSA;
+ case NativeCrypto.SSL_kECDHe:
+ return KEY_TYPE_EC_EC;
+ case NativeCrypto.SSL_kEECDH:
+ switch (algorithm_auth) {
+ case NativeCrypto.SSL_aECDSA:
+ return KEY_TYPE_EC_EC;
+ case NativeCrypto.SSL_aRSA:
+ return KEY_TYPE_RSA;
+ case NativeCrypto.SSL_aPSK:
+ return null;
+ case NativeCrypto.SSL_aNULL:
+ return null;
+ }
+ break;
+ case NativeCrypto.SSL_kPSK:
+ return null;
+ }
+
+ throw new SSLException("Unsupported key exchange. "
+ + "mkey: 0x" + Long.toHexString(algorithm_mkey & 0xffffffffL)
+ + ", auth: 0x" + Long.toHexString(algorithm_auth & 0xffffffffL));
+ }
+
+ /**
+ * Similar to getServerKeyType, but returns value given TLS
+ * ClientCertificateType byte values from a CertificateRequest
+ * message for use with X509KeyManager.chooseClientAlias or
+ * X509ExtendedKeyManager.chooseEngineClientAlias.
+ */
+ public static String getClientKeyType(byte keyType) {
+ // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
+ switch (keyType) {
+ case NativeCrypto.TLS_CT_RSA_SIGN:
+ return KEY_TYPE_RSA; // RFC rsa_sign
+ case NativeCrypto.TLS_CT_DSS_SIGN:
+ return KEY_TYPE_DSA; // RFC dss_sign
+ case NativeCrypto.TLS_CT_RSA_FIXED_DH:
+ return KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
+ case NativeCrypto.TLS_CT_DSS_FIXED_DH:
+ return KEY_TYPE_DH_DSA; // RFC dss_fixed_dh
+ case NativeCrypto.TLS_CT_ECDSA_SIGN:
+ return KEY_TYPE_EC; // RFC ecdsa_sign
+ case NativeCrypto.TLS_CT_RSA_FIXED_ECDH:
+ return KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
+ case NativeCrypto.TLS_CT_ECDSA_FIXED_ECDH:
+ return KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh
+ default:
+ return null;
+ }
+ }
+
+ private static String[] getDefaultCipherSuites(
+ boolean x509CipherSuitesNeeded,
+ boolean pskCipherSuitesNeeded) {
+ if (x509CipherSuitesNeeded) {
+ // X.509 based cipher suites need to be listed.
+ if (pskCipherSuitesNeeded) {
+ // Both X.509 and PSK based cipher suites need to be listed. Because TLS-PSK is not
+ // normally used, we assume that when PSK cipher suites are requested here they
+ // should be preferred over other cipher suites. Thus, we give PSK cipher suites
+ // higher priority than X.509 cipher suites.
+ // NOTE: There are cipher suites that use both X.509 and PSK (e.g., those based on
+ // RSA_PSK key exchange). However, these cipher suites are not currently supported.
+ return concat(
+ NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
+ NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
+ new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
+ } else {
+ // Only X.509 cipher suites need to be listed.
+ return concat(
+ NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
+ new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
+ }
+ } else if (pskCipherSuitesNeeded) {
+ // Only PSK cipher suites need to be listed.
+ return concat(
+ NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
+ new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
+ } else {
+ // Neither X.509 nor PSK cipher suites need to be listed.
+ return new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV};
+ }
+ }
+
+ private static String[] getDefaultProtocols() {
+ return NativeCrypto.DEFAULT_PROTOCOLS.clone();
+ }
+
+ private static String[] concat(String[]... arrays) {
+ int resultLength = 0;
+ for (String[] array : arrays) {
+ resultLength += array.length;
+ }
+ String[] result = new String[resultLength];
+ int resultOffset = 0;
+ for (String[] array : arrays) {
+ System.arraycopy(array, 0, result, resultOffset, array.length);
+ resultOffset += array.length;
+ }
+ return result;
}
}
diff --git a/src/main/java/org/conscrypt/SSLRecordProtocol.java b/src/main/java/org/conscrypt/SSLRecordProtocol.java
index b8a9ba2..515a522 100644
--- a/src/main/java/org/conscrypt/SSLRecordProtocol.java
+++ b/src/main/java/org/conscrypt/SSLRecordProtocol.java
@@ -17,485 +17,35 @@
package org.conscrypt;
-import java.io.IOException;
-import javax.net.ssl.SSLProtocolException;
/**
- * This class performs functionality dedicated to SSL record layer.
- * It unpacks and routes income data to the appropriate
- * client protocol (handshake, alert, application data protocols)
- * and packages outcome data into SSL/TLS records.
- * Initially created object has null connection state and does not
- * perform any cryptography computations over the income/outcome data.
- * After handshake protocol agreed upon security parameters they are placed
- * into SSLSessionImpl object and available for record protocol as
- * pending session. The order of setting up of the pending session
- * as an active session differs for client and server modes.
- * So for client mode the parameters are provided by handshake protocol
- * during retrieving of change_cipher_spec message to be sent (by calling of
- * getChangeCipherSpecMesage method).
- * For server side mode record protocol retrieves the parameters from
- * handshake protocol after receiving of client's change_cipher_spec message.
- * After the pending session has been set up as a current session,
- * new connection state object is created and used for encryption/decryption
- * of the messages.
- * Among with base functionality this class provides the information about
- * constrains on the data length, and information about correspondence
- * of plain and encrypted data lengths.
- * For more information on TLS v1 see http://www.ietf.org/rfc/rfc2246.txt,
- * on SSL v3 see http://wp.netscape.com/eng/ssl3,
- * on SSL v2 see http://wp.netscape.com/eng/security/SSL_2.html.
+ * This class contains some SSL constants.
*/
public class SSLRecordProtocol {
-
- /**
- * Maximum length of allowed plain data fragment
- * as specified by TLS specification.
- */
- protected static final int MAX_DATA_LENGTH = 16384; // 2^14
- /**
- * Maximum length of allowed compressed data fragment
- * as specified by TLS specification.
- */
- protected static final int MAX_COMPRESSED_DATA_LENGTH
- = MAX_DATA_LENGTH + 1024;
- /**
- * Maximum length of allowed ciphered data fragment
- * as specified by TLS specification.
- */
- protected static final int MAX_CIPHERED_DATA_LENGTH
- = MAX_COMPRESSED_DATA_LENGTH + 1024;
- /**
- * Maximum length of ssl record. It is counted as:
- * type(1) + version(2) + length(2) + MAX_CIPHERED_DATA_LENGTH
- */
- protected static final int MAX_SSL_PACKET_SIZE
- = MAX_CIPHERED_DATA_LENGTH + 5;
- // the SSL session used for connection
- private SSLSessionImpl session;
- // protocol version of the connection
- private byte[] version;
- // input stream of record protocol
- private SSLInputStream in;
- // handshake protocol object to which handshaking data will be transmitted
- private HandshakeProtocol handshakeProtocol;
- // alert protocol to indicate alerts occurred/received
- private AlertProtocol alertProtocol;
- // application data object to which application data will be transmitted
- private Appendable appData;
- // connection state holding object
- private ConnectionState
- activeReadState, activeWriteState, pendingConnectionState;
-
- // logger
- private Logger.Stream logger = Logger.getStream("record");
-
- // flag indicating if session object has been changed after
- // handshake phase (to distinguish session pending state)
- private boolean sessionWasChanged = false;
-
- // change cipher spec message content
- private static final byte[] change_cipher_spec_byte = new byte[] {1};
-
- /**
- * Creates an instance of record protocol and tunes
- * up the client protocols to use ut.
- * @param handshakeProtocol: HandshakeProtocol
- * @param alertProtocol: AlertProtocol
- * @param in: SSLInputStream
- * @param appData: Appendable
- */
- protected SSLRecordProtocol(HandshakeProtocol handshakeProtocol,
- AlertProtocol alertProtocol,
- SSLInputStream in,
- Appendable appData) {
- this.handshakeProtocol = handshakeProtocol;
- this.handshakeProtocol.setRecordProtocol(this);
- this.alertProtocol = alertProtocol;
- this.alertProtocol.setRecordProtocol(this);
- this.in = in;
- this.appData = appData;
+ private SSLRecordProtocol() {
}
/**
- * Returns the session obtained during the handshake negotiation.
- * If the handshake process was not completed, method returns null.
- * @return the session in effect.
+ * Maximum length of allowed plain data fragment as specified by TLS
+ * specification.
*/
- protected SSLSessionImpl getSession() {
- return session;
- }
+ static final int MAX_DATA_LENGTH = 16384; // 2^14
/**
- * Returns the minimum possible length of the SSL record.
- * @return
+ * Maximum length of allowed compressed data fragment as specified by TLS
+ * specification.
*/
- protected int getMinRecordSize() {
- return (activeReadState == null)
- ? 6 // type + version + length + 1 byte of data
- : 5 + activeReadState.getMinFragmentSize();
- }
+ static final int MAX_COMPRESSED_DATA_LENGTH = MAX_DATA_LENGTH + 1024;
/**
- * Returns the record length for the specified incoming data length.
- * If actual resulting record length is greater than
- * MAX_CIPHERED_DATA_LENGTH, MAX_CIPHERED_DATA_LENGTH is returned.
+ * Maximum length of allowed ciphered data fragment as specified by TLS
+ * specification.
*/
- protected int getRecordSize(int data_size) {
- if (activeWriteState == null) {
- return 5+data_size; // type + version + length + data_size
- } else {
- int res = 5 + activeWriteState.getFragmentSize(data_size);
- return (res > MAX_CIPHERED_DATA_LENGTH)
- ? MAX_CIPHERED_DATA_LENGTH // so the source data should be
- // split into several packets
- : res;
- }
- }
+ static final int MAX_CIPHERED_DATA_LENGTH = MAX_COMPRESSED_DATA_LENGTH + 1024;
/**
- * Returns the upper bound of length of data containing in the record with
- * specified length.
- * If the provided record_size is greater or equal to
- * MAX_CIPHERED_DATA_LENGTH the returned value will be
- * MAX_DATA_LENGTH
- * counted as for data with
- * MAX_CIPHERED_DATA_LENGTH length.
+ * Maximum length of ssl record. It is counted as: type(1) + version(2) +
+ * length(2) + MAX_CIPHERED_DATA_LENGTH
*/
- protected int getDataSize(int record_size) {
- record_size -= 5; // - (type + version + length + data_size)
- if (record_size > MAX_CIPHERED_DATA_LENGTH) {
- // the data of such size consists of the several packets
- return MAX_DATA_LENGTH;
- }
- if (activeReadState == null) {
- return record_size;
- }
- return activeReadState.getContentSize(record_size);
- }
-
- /**
- * Depending on the Connection State (Session) encrypts and compress the
- * provided data, and packs it into TLSCiphertext structure.
- *
- * @param content_type the SSL/TLS content type
- * @param dataStream the stream to read from
- * @return ssl packet created over the current connection state
- */
- protected byte[] wrap(byte content_type, DataStream dataStream) {
- if (content_type == ContentType.APPLICATION_DATA
- && session != null
- && (session.protocol == ProtocolVersion.SSLv3
- || session.protocol == ProtocolVersion.TLSv1)
- && session.cipherSuite.isInitialRecordSplit()) {
- byte[] first = wrap(content_type, dataStream, 1);
- if (!dataStream.hasData()) {
- return first;
- }
-
- byte[] second = wrap(content_type, dataStream, MAX_DATA_LENGTH);
- byte[] output = new byte[first.length + second.length];
- System.arraycopy(first, 0, output, 0, first.length);
- System.arraycopy(second, 0, output, first.length, second.length);
-
- return output;
- } else {
- return wrap(content_type, dataStream, MAX_DATA_LENGTH);
- }
- }
-
- private byte[] wrap(byte content_type, DataStream dataStream, int max_len) {
- byte[] fragment = dataStream.getData(max_len);
- return wrap(content_type, fragment, 0, fragment.length);
- }
-
- /**
- * Depending on the Connection State (Session) encrypts and compress
- * the provided data, and packs it into TLSCiphertext structure.
- * @param content_type: int
- * @param fragment: byte[]
- * @return ssl packet created over the current connection state
- */
- protected byte[] wrap(byte content_type,
- byte[] fragment, int offset, int len) {
- if (logger != null) {
- logger.println("SSLRecordProtocol.wrap: TLSPlaintext.fragment["
- +len+"]:");
- logger.print(fragment, offset, len);
- }
- if (len > MAX_DATA_LENGTH) {
- throw new AlertException(
- AlertProtocol.INTERNAL_ERROR,
- new SSLProtocolException(
- "The provided chunk of data is too big: " + len
- + " > MAX_DATA_LENGTH == "+MAX_DATA_LENGTH));
- }
- byte[] ciphered_fragment = fragment;
- if (activeWriteState != null) {
- ciphered_fragment =
- activeWriteState.encrypt(content_type, fragment, offset, len);
- if (ciphered_fragment.length > MAX_CIPHERED_DATA_LENGTH) {
- throw new AlertException(
- AlertProtocol.INTERNAL_ERROR,
- new SSLProtocolException(
- "The ciphered data increased more than on 1024 bytes"));
- }
- if (logger != null) {
- logger.println("SSLRecordProtocol.wrap: TLSCiphertext.fragment["
- +ciphered_fragment.length+"]:");
- logger.print(ciphered_fragment);
- }
- }
- return packetize(content_type, version, ciphered_fragment);
- }
-
- private byte[] packetize(byte type, byte[] version, byte[] fragment) {
- byte[] buff = new byte[5+fragment.length];
- buff[0] = type;
- if (version != null) {
- buff[1] = version[0];
- buff[2] = version[1];
- } else {
- buff[1] = 3;
- buff[2] = 1;
- }
- buff[3] = (byte) ((0x00FF00 & fragment.length) >> 8);
- buff[4] = (byte) (0x0000FF & fragment.length);
- System.arraycopy(fragment, 0, buff, 5, fragment.length);
- return buff;
- }
-
- /**
- * Set the ssl session to be used after sending the changeCipherSpec message
- * @param session: SSLSessionImpl
- */
- private void setSession(SSLSessionImpl session) {
- if (!sessionWasChanged) {
- // session was not changed for current handshake process
- if (logger != null) {
- logger.println("SSLRecordProtocol.setSession: Set pending session");
- logger.println(" cipher name: " + session.getCipherSuite());
- }
- this.session = session;
- // create new connection state
- pendingConnectionState = ((version == null) || (version[1] == 1))
- ? (ConnectionState) new ConnectionStateTLS(getSession())
- : (ConnectionState) new ConnectionStateSSLv3(getSession());
- sessionWasChanged = true;
- } else {
- // wait for rehandshaking's session
- sessionWasChanged = false;
- }
- }
-
- /**
- * Returns the change cipher spec message to be sent to another peer.
- * The pending connection state will be built on the base of provided
- * session object
- * The calling of this method triggers pending write connection state to
- * be active.
- * @return ssl record containing the "change cipher spec" message.
- */
- protected byte[] getChangeCipherSpecMesage(SSLSessionImpl session) {
- // make change_cipher_spec_message:
- byte[] change_cipher_spec_message;
- if (activeWriteState == null) {
- change_cipher_spec_message = new byte[] {
- ContentType.CHANGE_CIPHER_SPEC, version[0],
- version[1], 0, 1, 1
- };
- } else {
- change_cipher_spec_message =
- packetize(ContentType.CHANGE_CIPHER_SPEC, version,
- activeWriteState.encrypt(ContentType.CHANGE_CIPHER_SPEC,
- change_cipher_spec_byte, 0, 1));
- }
- setSession(session);
- activeWriteState = pendingConnectionState;
- if (logger != null) {
- logger.println("SSLRecordProtocol.getChangeCipherSpecMesage");
- logger.println("activeWriteState = pendingConnectionState");
- logger.print(change_cipher_spec_message);
- }
- return change_cipher_spec_message;
- }
-
- /**
- * Retrieves the fragment field of TLSCiphertext, and than
- * depending on the established Connection State
- * decrypts and decompresses it. The following structure is expected
- * on the input at the moment of the call:
- *
- * struct {
- * ContentType type;
- * ProtocolVersion version;
- * uint16 length;
- * select (CipherSpec.cipher_type) {
- * case stream: GenericStreamCipher;
- * case block: GenericBlockCipher;
- * } fragment;
- * } TLSCiphertext;
- *
- * (as specified by RFC 2246, TLS v1 Protocol specification)
- *
- * In addition this method can recognize SSLv2 hello message which
- * are often used to establish the SSL/TLS session.
- *
- * @throws IOException if some io errors have been occurred
- * @throws EndOfSourceException if underlying input stream
- * has ran out of data.
- * @throws EndOfBufferException if there was not enough data
- * to build complete ssl packet.
- * @return the type of unwrapped message.
- */
- protected int unwrap() throws IOException {
- if (logger != null) {
- logger.println("SSLRecordProtocol.unwrap: BEGIN [");
- }
- int type = in.readUint8();
- if ((type < ContentType.CHANGE_CIPHER_SPEC)
- || (type > ContentType.APPLICATION_DATA)) {
- if (logger != null) {
- logger.println("Non v3.1 message type:" + type);
- }
- if (type >= 0x80) {
- // it is probably SSL v2 client_hello message
- // (see SSL v2 spec at:
- // http://wp.netscape.com/eng/security/SSL_2.html)
- int length = (type & 0x7f) << 8 | in.read();
- byte[] fragment = in.read(length);
- handshakeProtocol.unwrapSSLv2(fragment);
- if (logger != null) {
- logger.println(
- "SSLRecordProtocol:unwrap ] END, SSLv2 type");
- }
- return ContentType.HANDSHAKE;
- }
- throw new AlertException(AlertProtocol.UNEXPECTED_MESSAGE,
- new SSLProtocolException(
- "Unexpected message type has been received: "+type));
- }
- if (logger != null) {
- logger.println("Got the message of type: " + type);
- }
- if (version != null) {
- if ((in.read() != version[0])
- || (in.read() != version[1])) {
- throw new AlertException(AlertProtocol.UNEXPECTED_MESSAGE,
- new SSLProtocolException(
- "Unexpected message type has been received: " +
- type));
- }
- } else {
- in.skip(2); // just skip the version number
- }
- int length = in.readUint16();
- if (logger != null) {
- logger.println("TLSCiphertext.fragment["+length+"]: ...");
- }
- if (length > MAX_CIPHERED_DATA_LENGTH) {
- throw new AlertException(AlertProtocol.RECORD_OVERFLOW,
- new SSLProtocolException(
- "Received message is too big."));
- }
- byte[] fragment = in.read(length);
- if (logger != null) {
- logger.print(fragment);
- }
- if (activeReadState != null) {
- fragment = activeReadState.decrypt((byte) type, fragment);
- if (logger != null) {
- logger.println("TLSPlaintext.fragment:");
- logger.print(fragment);
- }
- }
- if (fragment.length > MAX_DATA_LENGTH) {
- throw new AlertException(AlertProtocol.DECOMPRESSION_FAILURE,
- new SSLProtocolException(
- "Decompressed plain data is too big."));
- }
- switch (type) {
- case ContentType.CHANGE_CIPHER_SPEC:
- // notify handshake protocol:
- handshakeProtocol.receiveChangeCipherSpec();
- setSession(handshakeProtocol.getSession());
- // change cipher spec message has been received, so:
- if (logger != null) {
- logger.println("activeReadState = pendingConnectionState");
- }
- activeReadState = pendingConnectionState;
- break;
- case ContentType.ALERT:
- alert(fragment[0], fragment[1]);
- break;
- case ContentType.HANDSHAKE:
- handshakeProtocol.unwrap(fragment);
- break;
- case ContentType.APPLICATION_DATA:
- if (logger != null) {
- logger.println(
- "TLSCiphertext.unwrap: APP DATA["+length+"]:");
- logger.println(new String(fragment));
- }
- appData.append(fragment);
- break;
- default:
- throw new AlertException(AlertProtocol.UNEXPECTED_MESSAGE,
- new SSLProtocolException(
- "Unexpected message type has been received: " +
- type));
- }
- if (logger != null) {
- logger.println("SSLRecordProtocol:unwrap ] END, type: " + type);
- }
- return type;
- }
-
- /**
- * Passes the alert information to the alert protocol.
- * @param level: byte
- * @param description: byte
- */
- protected void alert(byte level, byte description) {
- if (logger != null) {
- logger.println("SSLRecordProtocol.allert: "+level+" "+description);
- }
- alertProtocol.alert(level, description);
- }
-
- /**
- * Sets up the SSL version used in this connection.
- * This method is calling from the handshake protocol after
- * it becomes known which protocol version will be used.
- * @param ver: byte[]
- * @return
- */
- protected void setVersion(byte[] ver) {
- this.version = ver;
- }
-
- /**
- * Shuts down the protocol. It will be impossible to use the instance
- * after the calling of this method.
- */
- protected void shutdown() {
- session = null;
- version = null;
- in = null;
- handshakeProtocol = null;
- alertProtocol = null;
- appData = null;
- if (pendingConnectionState != null) {
- pendingConnectionState.shutdown();
- }
- pendingConnectionState = null;
- if (activeReadState != null) {
- activeReadState.shutdown();
- }
- activeReadState = null;
- if (activeReadState != null) {
- activeReadState.shutdown();
- }
- activeWriteState = null;
- }
+ static final int MAX_SSL_PACKET_SIZE = MAX_CIPHERED_DATA_LENGTH + 5;
}
diff --git a/src/main/java/org/conscrypt/SSLSessionImpl.java b/src/main/java/org/conscrypt/SSLSessionImpl.java
deleted file mode 100644
index 63fa828..0000000
--- a/src/main/java/org/conscrypt/SSLSessionImpl.java
+++ /dev/null
@@ -1,257 +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.security.Principal;
-import java.security.SecureRandom;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.util.HashMap;
-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 org.conscrypt.util.EmptyArray;
-
-public final class SSLSessionImpl implements SSLSession, Cloneable {
-
- /*
- * Holds default instances so class preloading doesn't create an instance of
- * it.
- */
- private static class DefaultHolder {
- public static final SSLSessionImpl NULL_SESSION = new SSLSessionImpl(null);
- }
-
- private long creationTime;
- private boolean isValid = true;
- private final Map<String, Object> values = new HashMap<String, Object>();
-
- byte[] id;
- long lastAccessedTime;
- ProtocolVersion protocol;
- CipherSuite cipherSuite;
- SSLSessionContext context;
- X509Certificate[] localCertificates;
- X509Certificate[] peerCertificates;
- private String peerHost;
- private int peerPort = -1;
- byte[] master_secret;
- byte[] clientRandom;
- byte[] serverRandom;
- final boolean isServer;
-
- public static SSLSessionImpl getNullSession() {
- return DefaultHolder.NULL_SESSION;
- }
-
- public SSLSessionImpl(CipherSuite cipher_suite, SecureRandom secureRandom) {
- creationTime = System.currentTimeMillis();
- lastAccessedTime = creationTime;
- if (cipher_suite == null) {
- this.cipherSuite = CipherSuite.SSL_NULL_WITH_NULL_NULL;
- id = EmptyArray.BYTE;
- isServer = false;
- isValid = false;
- } else {
- this.cipherSuite = cipher_suite;
- id = new byte[32];
- secureRandom.nextBytes(id);
- long time = creationTime / 1000;
- id[28] = (byte) ((time & 0xFF000000) >>> 24);
- id[29] = (byte) ((time & 0x00FF0000) >>> 16);
- id[30] = (byte) ((time & 0x0000FF00) >>> 8);
- id[31] = (byte) ((time & 0x000000FF));
- isServer = true;
- }
-
- }
-
- public SSLSessionImpl(SecureRandom secureRandom) {
- this(null, secureRandom);
- }
-
- @Override
- public int getApplicationBufferSize() {
- return SSLRecordProtocol.MAX_DATA_LENGTH;
- }
-
- @Override
- public String getCipherSuite() {
- return cipherSuite.getName();
- }
-
- @Override
- public long getCreationTime() {
- return creationTime;
- }
-
- @Override
- public byte[] getId() {
- return id;
- }
-
- @Override
- public long getLastAccessedTime() {
- return lastAccessedTime;
- }
-
- @Override
- public Certificate[] getLocalCertificates() {
- return localCertificates;
- }
-
- @Override
- public Principal getLocalPrincipal() {
- if (localCertificates != null && localCertificates.length > 0) {
- return localCertificates[0].getSubjectX500Principal();
- }
- return null;
- }
-
- @Override
- public int getPacketBufferSize() {
- return SSLRecordProtocol.MAX_SSL_PACKET_SIZE;
- }
-
- @Override
- public javax.security.cert.X509Certificate[] getPeerCertificateChain()
- throws SSLPeerUnverifiedException {
- if (peerCertificates == null) {
- throw new SSLPeerUnverifiedException("No peer certificate");
- }
- javax.security.cert.X509Certificate[] certs = new javax.security.cert.X509Certificate[peerCertificates.length];
- for (int i = 0; i < certs.length; i++) {
- try {
- certs[i] = javax.security.cert.X509Certificate.getInstance(peerCertificates[i]
- .getEncoded());
- } catch (javax.security.cert.CertificateException ignored) {
- } catch (CertificateEncodingException ignored) {
- }
- }
- return certs;
- }
-
- @Override
- public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
- if (peerCertificates == null) {
- throw new SSLPeerUnverifiedException("No peer certificate");
- }
- return peerCertificates;
- }
-
- @Override
- public String getPeerHost() {
- return peerHost;
- }
-
- @Override
- public int getPeerPort() {
- return peerPort;
- }
-
- @Override
- public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
- if (peerCertificates == null) {
- throw new SSLPeerUnverifiedException("No peer certificate");
- }
- return peerCertificates[0].getSubjectX500Principal();
- }
-
- @Override
- public String getProtocol() {
- return (protocol == null) ? "NONE" : protocol.name;
- }
-
- @Override
- public SSLSessionContext getSessionContext() {
- return context;
- }
-
- @Override
- public Object getValue(String name) {
- if (name == null) {
- throw new IllegalArgumentException("name == null");
- }
- return values.get(name);
- }
-
- @Override
- public String[] getValueNames() {
- return values.keySet().toArray(new String[values.size()]);
- }
-
- @Override
- public void invalidate() {
- isValid = false;
- context = null;
- }
-
- @Override
- public boolean isValid() {
- if (isValid && context != null && context.getSessionTimeout() != 0
- && lastAccessedTime + context.getSessionTimeout() > System.currentTimeMillis()) {
- isValid = false;
- }
- return isValid;
- }
-
- @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));
- }
-
- }
-
- @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));
- }
- }
-
- @Override
- public Object clone() {
- try {
- return super.clone();
- } catch (CloneNotSupportedException e) {
- throw new AssertionError(e);
- }
- }
-
- void setPeer(String peerHost, int peerPort) {
- this.peerHost = peerHost;
- this.peerPort = peerPort;
- }
-}
diff --git a/src/main/java/org/conscrypt/SSLStreamedInput.java b/src/main/java/org/conscrypt/SSLStreamedInput.java
deleted file mode 100644
index 4c8a885..0000000
--- a/src/main/java/org/conscrypt/SSLStreamedInput.java
+++ /dev/null
@@ -1,57 +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.io.InputStream;
-
-/**
- * This class acts like a filtered input stream: it takes
- * the bytes from another InputStream.
- */
-public class SSLStreamedInput extends SSLInputStream {
-
- private InputStream in;
-
- public SSLStreamedInput(InputStream in) {
- this.in = in;
- }
-
- @Override
- public int available() throws IOException {
- return in.available();
- }
-
- /**
- * Read an opaque value from the stream.
- * @return the value read from the underlying stream.
- * @throws IOException if the data could not be read from
- * the underlying stream
- * @throws org.conscrypt.EndOfSourceException if the end of the underlying
- * stream has been reached.
- */
- @Override
- public int read() throws IOException {
- int res = in.read();
- if (res < 0) {
- throw new EndOfSourceException();
- }
- return res;
- }
-}
-
diff --git a/src/main/java/org/conscrypt/SSLv3Constants.java b/src/main/java/org/conscrypt/SSLv3Constants.java
deleted file mode 100644
index ea6482f..0000000
--- a/src/main/java/org/conscrypt/SSLv3Constants.java
+++ /dev/null
@@ -1,84 +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;
-
-/**
- *
- * Contains SSL 3.0 constants
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec.</a>
- */
-public class SSLv3Constants {
-
- /**
- * Client is a sender. Used in hash calculating for finished message.
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.6.9
- * Finished</a>
- */
- static final byte[] client = new byte[] { 0x43, 0x4C, 0x4E, 0x54 };
-
- /**
- * Server is a sender. Used in hash calculating for finished message.
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.6.9
- * Finished</a>
- */
- static final byte[] server = new byte[] { 0x53, 0x52, 0x56, 0x52 };
-
- /**
- * pad_1 for MD5
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
- * Null or standard stream cipher</a>
- */
- static final byte[] MD5pad1 = new byte[] { 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 };
-
- /**
- * pad_1 for SHA
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
- * Null or standard stream cipher</a>
- */
- static final byte[] SHApad1 = new byte[] { 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36 };
-
- /**
- * pad_2 for MD5
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
- * Null or standard stream cipher</a>
- */
- static final byte[] MD5pad2 = new byte[] { 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C };
-
- /**
- * pad_2 for SHA
- * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
- * Null or standard stream cipher</a>
- */
- static final byte[] SHApad2 = new byte[] { 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C };
-}
diff --git a/src/main/java/org/conscrypt/ServerHandshakeImpl.java b/src/main/java/org/conscrypt/ServerHandshakeImpl.java
deleted file mode 100644
index afbac56..0000000
--- a/src/main/java/org/conscrypt/ServerHandshakeImpl.java
+++ /dev/null
@@ -1,644 +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.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Arrays;
-import javax.crypto.Cipher;
-import javax.crypto.KeyAgreement;
-import javax.crypto.interfaces.DHPublicKey;
-import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.DHPublicKeySpec;
-import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509KeyManager;
-import javax.net.ssl.X509TrustManager;
-
-/**
- * Server side handshake protocol implementation.
- * Handshake protocol operates on top of the Record Protocol.
- * It responsible for negotiating a session.
- *
- * The implementation processes inbound client handshake messages,
- * creates and sends respond messages. Outbound messages are supplied
- * to Record Protocol. Detected errors are reported to the Alert protocol.
- *
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.
- * Handshake protocol.</a>
- *
- */
-public class ServerHandshakeImpl extends HandshakeProtocol {
-
- // private key used in key exchange
- private PrivateKey privKey;
-
- /**
- * Creates Server Handshake Implementation
- *
- * @param owner
- */
- public ServerHandshakeImpl(SSLEngineImpl owner) {
- super(owner);
- status = NEED_UNWRAP;
- }
-
- /**
- * Start session negotiation
- */
- @Override
- public void start() {
- if (session == null) { // initial handshake
- status = NEED_UNWRAP;
- return; // wait client hello
- }
- if (clientHello != null && this.status != FINISHED) {
- // current negotiation has not completed
- return; // ignore
- }
-
- // renegotiation
- sendHelloRequest();
- status = NEED_UNWRAP;
- }
-
- /**
- * Proceses inbound handshake messages
- * @param bytes
- */
- @Override
- public void unwrap(byte[] bytes) {
-
- io_stream.append(bytes);
- while (io_stream.available() > 0) {
- int handshakeType;
- int length;
- io_stream.mark();
- try {
- handshakeType = io_stream.read();
- length = io_stream.readUint24();
- if (io_stream.available() < length) {
- io_stream.reset();
- return;
- }
-
- switch (handshakeType) {
- case 1: // CLIENT_HELLO
- if (clientHello != null && this.status != FINISHED) {
- // Client hello has been received during handshake
- unexpectedMessage();
- return;
- }
- // if protocol planed to send Hello Request message
- // - cancel this demand.
- needSendHelloRequest = false;
- clientHello = new ClientHello(io_stream, length);
- if (nonBlocking) {
- delegatedTasks.add(new DelegatedTask(new Runnable() {
- @Override
- public void run() {
- processClientHello();
- }
- }, this));
- return;
- }
- processClientHello();
- break;
-
- case 11: // CLIENT CERTIFICATE
- if (isResuming || certificateRequest == null
- || serverHelloDone == null || clientCert != null) {
- unexpectedMessage();
- return;
- }
- clientCert = new CertificateMessage(io_stream, length);
- if (clientCert.certs.length == 0) {
- if (parameters.getNeedClientAuth()) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "HANDSHAKE FAILURE: no client certificate received");
- }
- } else {
- String authType = clientCert.getAuthType();
- try {
- parameters.getTrustManager().checkClientTrusted(
- clientCert.certs, authType);
- } catch (CertificateException e) {
- fatalAlert(AlertProtocol.BAD_CERTIFICATE,
- "Untrusted Client Certificate ", e);
- }
- session.peerCertificates = clientCert.certs;
- }
- break;
-
- case 15: // CERTIFICATE_VERIFY
- if (isResuming
- || clientKeyExchange == null
- || clientCert == null
- || clientKeyExchange.isEmpty() //client certificate
- // contains fixed DH
- // parameters
- || certificateVerify != null
- || changeCipherSpecReceived) {
- unexpectedMessage();
- return;
- }
- certificateVerify = new CertificateVerify(io_stream, length);
-
- String authType = clientCert.getAuthType();
- DigitalSignature ds = new DigitalSignature(authType);
- ds.init(clientCert.certs[0]);
- byte[] md5_hash = null;
- byte[] sha_hash = null;
-
- if ("RSA".equals(authType)) {
- md5_hash = io_stream.getDigestMD5withoutLast();
- sha_hash = io_stream.getDigestSHAwithoutLast();
- } else if ("DSA".equals(authType)) {
- sha_hash = io_stream.getDigestSHAwithoutLast();
- // The Signature should be empty in case of anonymous signature algorithm:
- // } else if ("DH".equals(authType)) {
- }
- ds.setMD5(md5_hash);
- ds.setSHA(sha_hash);
- if (!ds.verifySignature(certificateVerify.signedHash)) {
- fatalAlert(AlertProtocol.DECRYPT_ERROR,
- "DECRYPT ERROR: CERTIFICATE_VERIFY incorrect signature");
- }
- break;
- case 16: // CLIENT_KEY_EXCHANGE
- if (isResuming
- || serverHelloDone == null
- || clientKeyExchange != null
- || (clientCert == null && parameters.getNeedClientAuth())) {
- unexpectedMessage();
- return;
- }
- if (session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA
- || session.cipherSuite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- clientKeyExchange = new ClientKeyExchange(io_stream,
- length, serverHello.server_version[1] == 1,
- true);
- Cipher c = null;
- try {
- c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
- c.init(Cipher.UNWRAP_MODE, privKey);
- preMasterSecret = c.unwrap(clientKeyExchange.exchange_keys,
- "preMasterSecret",
- Cipher.SECRET_KEY).getEncoded();
- // check preMasterSecret:
- if (preMasterSecret.length != 48
- || preMasterSecret[0] != clientHello.client_version[0]
- || preMasterSecret[1] != clientHello.client_version[1]) {
- // incorrect preMasterSecret
- // prevent an attack (see TLS 1.0 spec., 7.4.7.1.)
- preMasterSecret = new byte[48];
- parameters.getSecureRandom().nextBytes(
- preMasterSecret);
- }
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "INTERNAL ERROR", e);
- }
- } else { // diffie hellman key exchange
- clientKeyExchange = new ClientKeyExchange(io_stream,
- length, serverHello.server_version[1] == 1,
- false);
- if (clientKeyExchange.isEmpty()) {
- // TODO check that client cert. DH params
- // matched server cert. DH params
-
- // client cert. contains fixed DH parameters
- preMasterSecret = ((DHPublicKey) clientCert.certs[0].getPublicKey()).getY().toByteArray();
- } else {
- try {
- KeyFactory kf = KeyFactory.getInstance("DH");
- KeyAgreement agreement = KeyAgreement.getInstance("DH");
- PublicKey clientPublic = kf.generatePublic(new DHPublicKeySpec(
- new BigInteger(
- 1,
- clientKeyExchange.exchange_keys),
- serverKeyExchange.par1,
- serverKeyExchange.par2));
- agreement.init(privKey);
- agreement.doPhase(clientPublic, true);
- preMasterSecret = agreement.generateSecret();
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR,
- "INTERNAL ERROR", e);
- return;
- }
- }
- }
-
- computerMasterSecret();
- break;
-
- case 20: // FINISHED
- if (!isResuming && !changeCipherSpecReceived) {
- unexpectedMessage();
- return;
- }
-
- clientFinished = new Finished(io_stream, length);
- verifyFinished(clientFinished.getData());
- session.context = parameters.getServerSessionContext();
- parameters.getServerSessionContext().putSession(session);
- if (!isResuming) {
- sendChangeCipherSpec();
- } else {
- session.lastAccessedTime = System.currentTimeMillis();
- status = FINISHED;
- }
- break;
- default:
- unexpectedMessage();
- return;
- }
- } catch (IOException e) {
- // io stream dosn't contain complete handshake message
- io_stream.reset();
- return;
- }
- }
- }
- /**
- * Processes SSLv2 Hello message
- * @ see TLS 1.0 spec., E.1. Version 2 client hello
- * @param bytes
- */
- @Override
- public void unwrapSSLv2(byte[] bytes) {
- io_stream.append(bytes);
- io_stream.mark();
- try {
- clientHello = new ClientHello(io_stream);
- } catch (IOException e) {
- io_stream.reset();
- return;
- }
- if (nonBlocking) {
- delegatedTasks.add(new DelegatedTask(new Runnable() {
- @Override
- public void run() {
- processClientHello();
- }
- }, this));
- return;
- }
- processClientHello();
- }
-
- /**
- *
- * Processes Client Hello message.
- * Server responds to client hello message with server hello
- * and (if necessary) server certificate, server key exchange,
- * certificate request, and server hello done messages.
- */
- void processClientHello() {
- CipherSuite cipher_suite;
-
- // check that clientHello contains CompressionMethod.null
- checkCompression: {
- for (int i = 0; i < clientHello.compression_methods.length; i++) {
- if (clientHello.compression_methods[i] == 0) {
- break checkCompression;
- }
- }
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "HANDSHAKE FAILURE. Incorrect client hello message");
- }
-
- byte[] server_version = clientHello.client_version;
- if (!ProtocolVersion.isSupported(clientHello.client_version)) {
- if (clientHello.client_version[0] >= 3) {
- // Protocol from the future, admit that the newest thing we know is TLSv1
- server_version = ProtocolVersion.TLSv1.version;
- } else {
- fatalAlert(AlertProtocol.PROTOCOL_VERSION,
- "PROTOCOL VERSION. Unsupported client version "
- + clientHello.client_version[0]
- + clientHello.client_version[1]);
- }
- }
-
- isResuming = false;
- FIND: if (clientHello.session_id.length != 0) {
- // client wishes to reuse session
-
- SSLSessionImpl sessionToResume;
- boolean reuseCurrent = false;
-
- // reuse current session
- if (session != null
- && Arrays.equals(session.id, clientHello.session_id)) {
- if (session.isValid()) {
- isResuming = true;
- break FIND;
- }
- reuseCurrent = true;
- }
-
- // find session in cash
- sessionToResume = findSessionToResume(clientHello.session_id);
- if (sessionToResume == null || !sessionToResume.isValid()) {
- if (!parameters.getEnableSessionCreation()) {
- if (reuseCurrent) {
- // we can continue current session
- sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
- status = NOT_HANDSHAKING;
- clearMessages();
- return;
- }
- // throw AlertException
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created");
- }
- session = null;
- } else {
- session = (SSLSessionImpl)sessionToResume.clone();
- isResuming = true;
- }
- }
-
- if (isResuming) {
- cipher_suite = session.cipherSuite;
- // clientHello.cipher_suites must include at least cipher_suite from the session
- checkCipherSuite: {
- for (int i = 0; i < clientHello.cipher_suites.length; i++) {
- if (cipher_suite.equals(clientHello.cipher_suites[i])) {
- break checkCipherSuite;
- }
- }
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "HANDSHAKE FAILURE. Incorrect client hello message");
- }
- } else {
- cipher_suite = selectSuite(clientHello.cipher_suites);
- if (cipher_suite == null) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "HANDSHAKE FAILURE. NO COMMON SUITE");
- }
- if (!parameters.getEnableSessionCreation()) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
- "SSL Session may not be created");
- }
- session = new SSLSessionImpl(cipher_suite, parameters.getSecureRandom());
- session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort());
- }
-
- recordProtocol.setVersion(server_version);
- session.protocol = ProtocolVersion.getByVersion(server_version);
- session.clientRandom = clientHello.random;
-
- // create server hello message
- serverHello = new ServerHello(parameters.getSecureRandom(),
- server_version,
- session.getId(), cipher_suite, (byte) 0); //CompressionMethod.null
- session.serverRandom = serverHello.random;
- send(serverHello);
- if (isResuming) {
- sendChangeCipherSpec();
- return;
- }
-
- // create and send server certificate message if needed
- if (!cipher_suite.isAnonymous()) { // need to send server certificate
- X509Certificate[] certs = null;
- String certType = cipher_suite.getServerKeyType();
- if (certType == null) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "NO CERT TYPE FOR " + cipher_suite.getName());
- }
- // obtain certificates from key manager
- String alias = null;
- X509KeyManager km = parameters.getKeyManager();
- if (km instanceof X509ExtendedKeyManager) {
- X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
- alias = ekm.chooseEngineServerAlias(certType, null,
- this.engineOwner);
- if (alias != null) {
- certs = ekm.getCertificateChain(alias);
- }
- } else {
- alias = km.chooseServerAlias(certType, null, null);
- if (alias != null) {
- certs = km.getCertificateChain(alias);
- }
- }
-
- if (certs == null) {
- fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "NO SERVER CERTIFICATE FOUND");
- return;
- }
- session.localCertificates = certs;
- serverCert = new CertificateMessage(certs);
- privKey = km.getPrivateKey(alias);
- send(serverCert);
- }
-
- // create and send server key exchange message if needed
- RSAPublicKey rsakey = null;
- DHPublicKeySpec dhkeySpec = null;
- byte[] hash = null;
- BigInteger p = null;
- BigInteger g = null;
-
- KeyPairGenerator kpg = null;
-
- try {
- if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- PublicKey pk = serverCert.certs[0].getPublicKey();
- if (getRSAKeyLength(pk) > 512) {
- // key is longer than 512 bits
- kpg = KeyPairGenerator.getInstance("RSA");
- kpg.initialize(512);
- }
- } else if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS
- || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_DSS_EXPORT
- || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA
- || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DHE_RSA_EXPORT
- || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon
- || cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_DH_anon_EXPORT) {
- kpg = KeyPairGenerator.getInstance("DH");
- p = new BigInteger(1, DHParameters.getPrime());
- g = new BigInteger("2");
- DHParameterSpec spec = new DHParameterSpec(p, g);
- kpg.initialize(spec);
- }
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
- }
-
- if (kpg != null) {
- // need to send server key exchange message
- DigitalSignature ds = new DigitalSignature(cipher_suite.authType);
- KeyPair kp = null;
- try {
- kp = kpg.genKeyPair();
- if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- rsakey = (RSAPublicKey) kp.getPublic();
- } else {
- DHPublicKey dhkey = (DHPublicKey) kp.getPublic();
- KeyFactory kf = KeyFactory.getInstance("DH");
- dhkeySpec = kf.getKeySpec(dhkey, DHPublicKeySpec.class);
- }
- if (!cipher_suite.isAnonymous()) { // calculate signed_params
-
- // init by private key which correspond to
- // server certificate
- ds.init(privKey);
-
- // use emphemeral key for key exchange
- privKey = kp.getPrivate();
- ds.update(clientHello.getRandom());
- ds.update(serverHello.getRandom());
-
-//FIXME 1_byte==0x00
- if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- ServerKeyExchange.updateSignatureRsa(ds, rsakey.getModulus(),
- rsakey.getPublicExponent());
- } else {
- ServerKeyExchange.updateSignatureDh(ds, dhkeySpec.getP(), dhkeySpec.getG(),
- dhkeySpec.getY());
- }
- hash = ds.sign();
- } else {
- privKey = kp.getPrivate(); // use emphemeral key for key exchange
- }
- } catch (Exception e) {
- fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
- }
-
- if (cipher_suite.keyExchange == CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- serverKeyExchange = new ServerKeyExchange(rsakey.getModulus(),
- rsakey.getPublicExponent(), null, hash);
- } else {
- serverKeyExchange = new ServerKeyExchange(p,
- g, dhkeySpec.getY(), hash);
- }
- send(serverKeyExchange);
- }
-
- // CERTIFICATE_REQUEST
- certRequest: if (parameters.getWantClientAuth()
- || parameters.getNeedClientAuth()) {
- X509Certificate[] accepted;
- try {
- X509TrustManager tm = parameters.getTrustManager();
- accepted = tm.getAcceptedIssuers();
- } catch (ClassCastException e) {
- // don't send certificateRequest
- break certRequest;
- }
- byte[] requestedClientCertTypes = { CipherSuite.TLS_CT_RSA_SIGN,
- CipherSuite.TLS_CT_DSS_SIGN };
- certificateRequest = new CertificateRequest(
- requestedClientCertTypes, accepted);
- send(certificateRequest);
- }
-
- // SERVER_HELLO_DONE
- serverHelloDone = new ServerHelloDone();
- send(serverHelloDone);
- status = NEED_UNWRAP;
- }
-
- /**
- * Creates and sends finished message
- */
- @Override
- protected void makeFinished() {
- byte[] verify_data;
- boolean isTLS = (serverHello.server_version[1] == 1); // TLS 1.0 protocol
- if (isTLS) {
- verify_data = new byte[12];
- computerVerifyDataTLS("server finished", verify_data);
- } else { // SSL 3.0 protocol (http://wp.netscape.com/eng/ssl3)
- verify_data = new byte[36];
- computerVerifyDataSSLv3(SSLv3Constants.server, verify_data);
- }
- serverFinished = new Finished(verify_data);
- send(serverFinished);
- if (isResuming) {
- if (isTLS) {
- computerReferenceVerifyDataTLS("client finished");
- } else {
- computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
- }
- status = NEED_UNWRAP;
- } else {
- session.lastAccessedTime = System.currentTimeMillis();
- status = FINISHED;
- }
- }
-
- // find sesssion in the session hash
- private SSLSessionImpl findSessionToResume(byte[] session_id) {
- return (SSLSessionImpl)parameters.getServerSessionContext().getSession(session_id);
- }
-
- // find appropriate cipher_suite in the client suites
- private CipherSuite selectSuite(CipherSuite[] clientSuites) {
- for (CipherSuite clientSuite : clientSuites) {
- if (!clientSuite.supported) {
- continue;
- }
- for (CipherSuite enabledCipherSuite : parameters.getEnabledCipherSuitesMember()) {
- if (clientSuite.equals(enabledCipherSuite)) {
- return clientSuite;
- }
- }
- }
- return null;
- }
-
- /**
- * Processes inbound ChangeCipherSpec message
- */
- @Override
- public void receiveChangeCipherSpec() {
- if (isResuming) {
- if (serverFinished == null) {
- unexpectedMessage();
- } else {
- changeCipherSpecReceived = true;
- }
- } else {
- if ((parameters.getNeedClientAuth() && clientCert == null)
- || clientKeyExchange == null
- || (clientCert != null && clientCert.certs.length > 0
- && !clientKeyExchange.isEmpty()
- && certificateVerify == null)) {
- unexpectedMessage();
- } else {
- changeCipherSpecReceived = true;
- }
- if (serverHello.server_version[1] == 1) {
- computerReferenceVerifyDataTLS("client finished");
- } else {
- computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
- }
- }
- }
-
-}
diff --git a/src/main/java/org/conscrypt/ServerHello.java b/src/main/java/org/conscrypt/ServerHello.java
deleted file mode 100644
index 3cc3b46..0000000
--- a/src/main/java/org/conscrypt/ServerHello.java
+++ /dev/null
@@ -1,136 +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.security.SecureRandom;
-import libcore.io.Streams;
-
-/**
- *
- * Represents server hello message.
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.3.
- * Server hello.</a>
- */
-public class ServerHello extends Message {
-
- /**
- * Server version
- */
- byte[] server_version = new byte[2];
-
- /**
- * Random bytes
- */
- byte[] random = new byte[32];
-
- /**
- * Session id
- */
- byte[] session_id;
-
- /**
- * Selected cipher suite
- */
- CipherSuite cipher_suite;
-
- /**
- * Selected compression method
- */
- byte compression_method;
-
- /**
- * Creates outbound message
- * @param sr
- * @param server_version
- * @param session_id
- * @param cipher_suite
- * @param compression_method
- */
- public ServerHello(SecureRandom sr, byte[] server_version,
- byte[] session_id, CipherSuite cipher_suite, byte compression_method) {
- long gmt_unix_time = new java.util.Date().getTime() / 1000;
- sr.nextBytes(random);
- random[0] = (byte) ((gmt_unix_time & 0xFF000000) >>> 24);
- random[1] = (byte) ((gmt_unix_time & 0xFF0000) >>> 16);
- random[2] = (byte) ((gmt_unix_time & 0xFF00) >>> 8);
- random[3] = (byte) (gmt_unix_time & 0xFF);
- this.session_id = session_id;
- this.cipher_suite = cipher_suite;
- this.compression_method = compression_method;
- this.server_version = server_version;
- length = 38 + session_id.length;
- }
-
- /**
- * Creates inbound message
- * @param in
- * @param length
- * @throws IOException
- */
- public ServerHello(HandshakeIODataStream in, int length) throws IOException {
-
- server_version[0] = (byte) in.read();
- server_version[1] = (byte) in.read();
- Streams.readFully(in, random);
- int size = in.readUint8();
- session_id = new byte[size];
- in.read(session_id, 0, size);
- byte b0 = (byte) in.read();
- byte b1 = (byte) in.read();
- cipher_suite = CipherSuite.getByCode(b0, b1);
- compression_method = (byte) in.read();
- this.length = 38 + session_id.length;
- if (this.length != length) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ServerHello");
- }
-
- }
-
- /**
- * Sends message
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- out.write(server_version);
- out.write(random);
- out.writeUint8(session_id.length);
- out.write(session_id);
- out.write(cipher_suite.toBytes());
- out.write(compression_method);
- length = 38 + session_id.length;
- }
-
- /**
- * Returns server random
- * @return
- */
- public byte[] getRandom() {
- return random;
- }
-
- /**
- * Returns message type
- * @return
- */
- @Override
- public int getType() {
- return Handshake.SERVER_HELLO;
- }
-}
diff --git a/src/main/java/org/conscrypt/ServerHelloDone.java b/src/main/java/org/conscrypt/ServerHelloDone.java
deleted file mode 100644
index 3e40f9a..0000000
--- a/src/main/java/org/conscrypt/ServerHelloDone.java
+++ /dev/null
@@ -1,76 +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;
-
-/**
- *
- * Represents server hello done message
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.5.
- * Server hello done</a>
- *
- */
-public class ServerHelloDone extends Message {
-
- /**
- * Creates outbound message
- *
- */
- public ServerHelloDone() {
- }
-
- /**
- * Creates inbound message
- * @param in
- * @param length
- * @throws IOException
- */
- public ServerHelloDone(HandshakeIODataStream in, int length)
- throws IOException {
- if (length != 0) {
- fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ServerHelloDone");
- }
- }
-
- /**
- * Sends message
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- }
-
- /**
- * Returns message length
- * @return
- */
- @Override
- public int length() {
- return 0;
- }
-
- /**
- * Returns message type
- * @return
- */
- @Override
- public int getType() {
- return Handshake.SERVER_HELLO_DONE;
- }
-}
diff --git a/src/main/java/org/conscrypt/ServerKeyExchange.java b/src/main/java/org/conscrypt/ServerKeyExchange.java
deleted file mode 100644
index d949850..0000000
--- a/src/main/java/org/conscrypt/ServerKeyExchange.java
+++ /dev/null
@@ -1,245 +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.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPublicKeySpec;
-
-/**
- *
- * Represents server key exchange message.
- * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.3.
- * Server key exchange message.</a>
- *
- */
-public class ServerKeyExchange extends Message {
-
- // ServerRSAParams ServerDHParams
- final BigInteger par1; // rsa_modulus dh_p
- final byte[] bytes1;
-
- final BigInteger par2; // rsa_exponent dh_g
- final byte[] bytes2;
-
- final BigInteger par3; // dh_Ys
- final byte[] bytes3;
-
- /**
- * Signature
- */
- final byte[] hash;
-
- private RSAPublicKey key;
-
- /**
- * Creates outbound message
- * @param par1 rsa_modulus or dh_p
- * @param par2 rsa_exponent or dh_g
- * @param par3 dh_Ys for ServerDHParams; should be null for ServerRSAParams
- * @param hash should be null for anonymous SignatureAlgorithm
- */
- public ServerKeyExchange(BigInteger par1, BigInteger par2, BigInteger par3,
- byte[] hash) {
- this.par1 = par1;
- this.par2 = par2;
- this.par3 = par3;
- this.hash = hash;
-
- bytes1 = toUnsignedByteArray(this.par1);
-
- bytes2 = toUnsignedByteArray(this.par2);
-
- length = 4 + bytes1.length + bytes2.length;
- if (hash != null) {
- length += 2 + hash.length;
- }
- if (par3 == null) {
- bytes3 = null;
- return;
- }
- bytes3 = toUnsignedByteArray(this.par3);
- length += 2 + bytes3.length;
- }
-
- /**
- * Remove first byte if 0. Needed because BigInteger.toByteArray() sometimes
- * returns a zero prefix.
- */
- public static byte[] toUnsignedByteArray(BigInteger bi) {
- if (bi == null) {
- return null;
- }
- byte[] bb = bi.toByteArray();
- // bb is not null, and has at least 1 byte - ZERO is represented as [0]
- if (bb[0] == 0) {
- byte[] noZero = new byte[bb.length - 1];
- System.arraycopy(bb, 1, noZero, 0, noZero.length);
- return noZero;
- } else {
- return bb;
- }
- }
-
- public static void updateSignatureRsa(DigitalSignature ds, BigInteger modulus,
- BigInteger publicExponent) {
- byte[] tmp;
- byte[] tmpLength = new byte[2];
- tmp = ServerKeyExchange.toUnsignedByteArray(modulus);
- tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
- tmpLength[1] = (byte) (tmp.length & 0xFF);
- ds.update(tmpLength);
- ds.update(tmp);
- tmp = ServerKeyExchange.toUnsignedByteArray(publicExponent);
- tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
- tmpLength[1] = (byte) (tmp.length & 0xFF);
- ds.update(tmpLength);
- ds.update(tmp);
- }
-
- public static void updateSignatureDh(DigitalSignature ds, BigInteger p, BigInteger g,
- BigInteger y) {
- byte[] tmp;
- byte[] tmpLength = new byte[2];
- tmp = ServerKeyExchange.toUnsignedByteArray(p);
- tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
- tmpLength[1] = (byte) (tmp.length & 0xFF);
- ds.update(tmpLength);
- ds.update(tmp);
- tmp = ServerKeyExchange.toUnsignedByteArray(g);
- tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
- tmpLength[1] = (byte) (tmp.length & 0xFF);
- ds.update(tmpLength);
- ds.update(tmp);
- tmp = ServerKeyExchange.toUnsignedByteArray(y);
- tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
- tmpLength[1] = (byte) (tmp.length & 0xFF);
- ds.update(tmpLength);
- ds.update(tmp);
- }
-
- public boolean verifySignature(DigitalSignature ds) {
- if (par3 != null) {
- updateSignatureDh(ds, par1, par2, par3);
- } else {
- updateSignatureRsa(ds, par1, par2);
- }
- return ds.verifySignature(hash);
- }
-
- /**
- * Will return {@code true} if the signature is {@code null} since this is
- * considered anonymous.
- */
- public boolean isAnonymous() {
- return hash == null;
- }
-
- /**
- * Creates inbound message
- * @param in
- * @param length
- * @param keyExchange
- * @throws IOException
- */
- public ServerKeyExchange(HandshakeIODataStream in, int length,
- int keyExchange) throws IOException {
-
- int size = in.readUint16();
- bytes1 = in.read(size);
- par1 = new BigInteger(1, bytes1);
- this.length = 2 + bytes1.length;
- size = in.readUint16();
- bytes2 = in.read(size);
- par2 = new BigInteger(1, bytes2);
- this.length += 2 + bytes2.length;
- if (keyExchange != CipherSuite.KEY_EXCHANGE_RSA_EXPORT) {
- size = in.readUint16();
- bytes3 = in.read(size);
- par3 = new BigInteger(1, bytes3);
- this.length += 2 + bytes3.length;
- } else {
- par3 = null;
- bytes3 = null;
- }
- if (keyExchange != CipherSuite.KEY_EXCHANGE_DH_anon_EXPORT
- && keyExchange != CipherSuite.KEY_EXCHANGE_DH_anon) {
- size = in.readUint16();
- hash = in.read(size);
- this.length += 2 + hash.length;
- } else {
- hash = null;
- }
- if (this.length != length) {
- fatalAlert(AlertProtocol.DECODE_ERROR,
- "DECODE ERROR: incorrect ServerKeyExchange");
- }
- }
-
- /**
- * Sends message
- * @param out
- */
- @Override
- public void send(HandshakeIODataStream out) {
- out.writeUint16(bytes1.length);
- out.write(bytes1);
- out.writeUint16(bytes2.length);
- out.write(bytes2);
- if (bytes3 != null) {
- out.writeUint16(bytes3.length);
- out.write(bytes3);
- }
- if (hash != null) {
- out.writeUint16(hash.length);
- out.write(hash);
- }
- }
-
- /**
- * Returns RSAPublicKey generated using ServerRSAParams
- * (rsa_modulus and rsa_exponent).
- *
- * @return
- */
- public RSAPublicKey getRSAPublicKey() {
- if (key != null) {
- return key;
- }
- try {
- KeyFactory kf = KeyFactory.getInstance("RSA");
- key = (RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(par1,
- par2));
- } catch (Exception e) {
- return null;
- }
- return key;
- }
-
- /**
- * Returns message type
- * @return
- */
- @Override
- public int getType() {
- return Handshake.SERVER_KEY_EXCHANGE;
- }
-
-}
diff --git a/src/main/java/org/conscrypt/util/Arrays.java b/src/main/java/org/conscrypt/util/Arrays.java
new file mode 100644
index 0000000..dbfcb13
--- /dev/null
+++ b/src/main/java/org/conscrypt/util/Arrays.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.util;
+
+/**
+ * Compatibility utility for Arrays.
+ */
+public final class Arrays {
+ private Arrays() {
+ }
+
+ /**
+ * Checks that the range described by {@code offset} and {@code count}
+ * doesn't exceed {@code arrayLength}.
+ *
+ * @hide
+ */
+ public static final void checkOffsetAndCount(int arrayLength, int offset, int count) {
+ if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
+ throw new ArrayIndexOutOfBoundsException("length=" + arrayLength + "; regionStart="
+ + offset + "; regionLength=" + count);
+ }
+ }
+}
diff --git a/src/main/native/org_conscrypt_NativeCrypto.cpp b/src/main/native/org_conscrypt_NativeCrypto.cpp
index 077e96c..8d0ac72 100644
--- a/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -21,17 +21,24 @@
#define TO_STRING1(x) #x
#define TO_STRING(x) TO_STRING1(x)
#ifndef JNI_JARJAR_PREFIX
-#define CONSCRYPT_UNBUNDLED
-#define JNI_JARJAR_PREFIX
+ #ifndef CONSCRYPT_NOT_UNBUNDLED
+ #define CONSCRYPT_UNBUNDLED
+ #endif
+ #define JNI_JARJAR_PREFIX
#endif
#define LOG_TAG "NativeCrypto"
#include <arpa/inet.h>
#include <fcntl.h>
+#include <pthread.h>
#include <sys/socket.h>
#include <unistd.h>
+#ifdef CONSCRYPT_UNBUNDLED
+#include <dlfcn.h>
+#endif
+
#include <jni.h>
#include <openssl/asn1t.h>
@@ -43,19 +50,46 @@
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
+#include "crypto/ecdsa/ecs_locl.h"
-#include "AsynchronousSocketCloseMonitor.h"
+#ifndef CONSCRYPT_UNBUNDLED
+/* If we're compiled unbundled from Android system image, we use the
+ * CompatibilityCloseMonitor
+ */
+#include "AsynchronousCloseMonitor.h"
+#endif
+
+#ifndef CONSCRYPT_UNBUNDLED
#include "cutils/log.h"
+#else
+#include <android/log.h>
+#define ALOGD(...) \
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);
+#define ALOGE(...) \
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);
+#define ALOGV(...) \
+ __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__);
+#endif
+
+#ifndef CONSCRYPT_UNBUNDLED
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
-#include "NetFd.h"
+#else
+#define NATIVE_METHOD(className, functionName, signature) \
+ { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
+#define REGISTER_NATIVE_METHODS(jni_class_name) \
+ RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
+#endif
+
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
#include "UniquePtr.h"
+#include "NetFd.h"
#undef WITH_JNI_TRACE
+#undef WITH_JNI_TRACE_MD
#undef WITH_JNI_TRACE_DATA
/*
@@ -84,11 +118,19 @@
#else
#define JNI_TRACE(...) ((void)0)
#endif
+#ifdef WITH_JNI_TRACE_MD
+#define JNI_TRACE_MD(...) \
+ ((void)ALOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__));
+#else
+#define JNI_TRACE_MD(...) ((void)0)
+#endif
// don't overwhelm logcat
#define WITH_JNI_TRACE_DATA_CHUNK_SIZE 512
static JavaVM* gJavaVM;
-static jclass openSslOutputStreamClass;
+static jclass cryptoUpcallsClass;
+static jclass openSslInputStreamClass;
+static jclass openSslNativeReferenceClass;
static jclass byteArrayClass;
static jclass calendarClass;
@@ -99,6 +141,8 @@
static jclass outputStreamClass;
static jclass stringClass;
+static jfieldID openSslNativeReference_context;
+
static jmethodID calendar_setMethod;
static jmethodID inputStream_readMethod;
static jmethodID integer_valueOfMethod;
@@ -115,7 +159,7 @@
struct BIO_Delete {
void operator()(BIO* p) const {
- BIO_free(p);
+ BIO_free_all(p);
}
};
typedef UniquePtr<BIO, BIO_Delete> Unique_BIO;
@@ -510,6 +554,14 @@
/**
* Throws a javax.net.ssl.SSLException with the given string as a message.
*/
+static void throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) {
+ JNI_TRACE("throwSSLExceptionStr %s", message);
+ jniThrowException(env, "javax/net/ssl/SSLHandshakeException", message);
+}
+
+/**
+ * Throws a javax.net.ssl.SSLException with the given string as a message.
+ */
static void throwSSLExceptionStr(JNIEnv* env, const char* message) {
JNI_TRACE("throwSSLExceptionStr %s", message);
jniThrowException(env, "javax/net/ssl/SSLException", message);
@@ -533,8 +585,8 @@
* SSL_ERROR_NONE to probe with ERR_get_error
* @param message null-ok; general error message
*/
-static void throwSSLExceptionWithSslErrors(
- JNIEnv* env, SSL* ssl, int sslErrorCode, const char* message) {
+static void throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode,
+ const char* message, void (*actualThrow)(JNIEnv*, const char*) = throwSSLExceptionStr) {
if (message == NULL) {
message = "SSL error";
@@ -582,7 +634,7 @@
char* str;
if (asprintf(&str, "%s: ssl=%p: %s", message, ssl, sslErrorStr) <= 0) {
// problem with asprintf, just throw argument message, log everything
- throwSSLExceptionStr(env, message);
+ actualThrow(env, message);
ALOGV("%s: ssl=%p: %s", message, ssl, sslErrorStr);
freeOpenSslErrorState();
return;
@@ -638,7 +690,7 @@
if (sslErrorCode == SSL_ERROR_SSL) {
throwSSLProtocolExceptionStr(env, allocStr);
} else {
- throwSSLExceptionStr(env, allocStr);
+ actualThrow(env, allocStr);
}
ALOGV("%s", allocStr);
@@ -697,6 +749,16 @@
return ssl_cipher;
}
+template<typename T>
+static T* fromContextObject(JNIEnv* env, jobject contextObject) {
+ T* ref = reinterpret_cast<T*>(env->GetLongField(contextObject, openSslNativeReference_context));
+ if (ref == NULL) {
+ JNI_TRACE("ctx == null");
+ jniThrowNullPointerException(env, "ctx == null");
+ }
+ return ref;
+}
+
/**
* Converts a Java byte[] two's complement to an OpenSSL BIGNUM. This will
* allocate the BIGNUM if *dest == NULL. Returns true on success. If the
@@ -890,6 +952,38 @@
return x509;
}
+/*
+ * Sets the read and write BIO for an SSL connection and removes it when it goes out of scope.
+ * We hang on to BIO with a JNI GlobalRef and we want to remove them as soon as possible.
+ */
+class ScopedSslBio {
+public:
+ ScopedSslBio(SSL *ssl, BIO* rbio, BIO* wbio) : ssl_(ssl) {
+ SSL_set_bio(ssl_, rbio, wbio);
+ CRYPTO_add(&rbio->references,1,CRYPTO_LOCK_BIO);
+ CRYPTO_add(&wbio->references,1,CRYPTO_LOCK_BIO);
+ }
+
+ ~ScopedSslBio() {
+ SSL_set_bio(ssl_, NULL, NULL);
+ }
+
+private:
+ SSL* const ssl_;
+};
+
+/**
+ * Obtains the current thread's JNIEnv
+ */
+static JNIEnv* getJNIEnv() {
+ JNIEnv* env;
+ if (gJavaVM->AttachCurrentThread(&env, NULL) < 0) {
+ ALOGE("Could not attach JavaVM to find current JNIEnv");
+ return NULL;
+ }
+ return env;
+}
+
/**
* BIO for InputStream
*/
@@ -897,12 +991,12 @@
public:
BIO_Stream(jobject stream) :
mEof(false) {
- JNIEnv* env = getEnv();
+ JNIEnv* env = getJNIEnv();
mStream = env->NewGlobalRef(stream);
}
~BIO_Stream() {
- JNIEnv* env = getEnv();
+ JNIEnv* env = getJNIEnv();
env->DeleteGlobalRef(mStream);
}
@@ -913,11 +1007,16 @@
}
int flush() {
- JNIEnv* env = getEnv();
+ JNIEnv* env = getJNIEnv();
if (env == NULL) {
return -1;
}
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("BIO_Stream::flush called with pending exception");
+ return -1;
+ }
+
env->CallVoidMethod(mStream, outputStream_flushMethod);
if (env->ExceptionCheck()) {
return -1;
@@ -935,16 +1034,6 @@
mEof = eof;
}
- JNIEnv* getEnv() {
- JNIEnv* env;
-
- if (gJavaVM->AttachCurrentThread(&env, NULL) < 0) {
- return NULL;
- }
-
- return env;
- }
-
private:
jobject mStream;
bool mEof;
@@ -973,12 +1062,17 @@
private:
int read_internal(char *buf, int len, jmethodID method) {
- JNIEnv* env = getEnv();
+ JNIEnv* env = getJNIEnv();
if (env == NULL) {
JNI_TRACE("BIO_InputStream::read could not get JNIEnv");
return -1;
}
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("BIO_InputStream::read called with pending exception");
+ return -1;
+ }
+
ScopedLocalRef<jbyteArray> javaBytes(env, env->NewByteArray(len));
if (javaBytes.get() == NULL) {
JNI_TRACE("BIO_InputStream::read failed call to NewByteArray");
@@ -1014,12 +1108,17 @@
}
int write(const char *buf, int len) {
- JNIEnv* env = getEnv();
+ JNIEnv* env = getJNIEnv();
if (env == NULL) {
JNI_TRACE("BIO_OutputStream::write => could not get JNIEnv");
return -1;
}
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("BIO_OutputStream::write => called with pending exception");
+ return -1;
+ }
+
ScopedLocalRef<jbyteArray> javaBytes(env, env->NewByteArray(len));
if (javaBytes.get() == NULL) {
JNI_TRACE("BIO_OutputStream::write => failed call to NewByteArray");
@@ -1062,11 +1161,19 @@
}
static int bio_stream_read(BIO *b, char *buf, int len) {
+ BIO_clear_retry_flags(b);
BIO_InputStream* stream = static_cast<BIO_InputStream*>(b->ptr);
- return stream->read(buf, len);
+ int ret = stream->read(buf, len);
+ if (ret == 0) {
+ // EOF is indicated by -1 with a BIO flag.
+ BIO_set_retry_read(b);
+ return -1;
+ }
+ return ret;
}
static int bio_stream_write(BIO *b, const char *buf, int len) {
+ BIO_clear_retry_flags(b);
BIO_OutputStream* stream = static_cast<BIO_OutputStream*>(b->ptr);
return stream->write(buf, len);
}
@@ -1111,6 +1218,497 @@
NULL, /* no bio_callback_ctrl */
};
+static jbyteArray rawSignDigestWithPrivateKey(JNIEnv* env, jobject privateKey,
+ const char* message, size_t message_len) {
+ ScopedLocalRef<jbyteArray> messageArray(env, env->NewByteArray(message_len));
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("rawSignDigestWithPrivateKey(%p) => threw exception", privateKey);
+ return NULL;
+ }
+
+ {
+ ScopedByteArrayRW messageBytes(env, messageArray.get());
+ if (messageBytes.get() == NULL) {
+ JNI_TRACE("rawSignDigestWithPrivateKey(%p) => using byte array failed", privateKey);
+ return NULL;
+ }
+
+ memcpy(messageBytes.get(), message, message_len);
+ }
+
+ jmethodID rawSignMethod = env->GetStaticMethodID(cryptoUpcallsClass,
+ "rawSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;[B)[B");
+ if (rawSignMethod == NULL) {
+ ALOGE("Could not find rawSignDigestWithPrivateKey");
+ return NULL;
+ }
+
+ return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod(
+ cryptoUpcallsClass, rawSignMethod, privateKey, messageArray.get()));
+}
+
+static jbyteArray rawCipherWithPrivateKey(JNIEnv* env, jobject privateKey, jboolean encrypt,
+ const char* ciphertext, size_t ciphertext_len) {
+ ScopedLocalRef<jbyteArray> ciphertextArray(env, env->NewByteArray(ciphertext_len));
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("rawCipherWithPrivateKey(%p) => threw exception", privateKey);
+ return NULL;
+ }
+
+ {
+ ScopedByteArrayRW ciphertextBytes(env, ciphertextArray.get());
+ if (ciphertextBytes.get() == NULL) {
+ JNI_TRACE("rawCipherWithPrivateKey(%p) => using byte array failed", privateKey);
+ return NULL;
+ }
+
+ memcpy(ciphertextBytes.get(), ciphertext, ciphertext_len);
+ }
+
+ jmethodID rawCipherMethod = env->GetStaticMethodID(cryptoUpcallsClass,
+ "rawCipherWithPrivateKey", "(Ljava/security/PrivateKey;Z[B)[B");
+ if (rawCipherMethod == NULL) {
+ ALOGE("Could not find rawCipherWithPrivateKey");
+ return NULL;
+ }
+
+ return reinterpret_cast<jbyteArray>(env->CallStaticObjectMethod(
+ cryptoUpcallsClass, rawCipherMethod, privateKey, encrypt, ciphertextArray.get()));
+}
+
+// *********************************************
+// From keystore_openssl.cpp in Chromium source.
+// *********************************************
+
+// Custom RSA_METHOD that uses the platform APIs.
+// Note that for now, only signing through RSA_sign() is really supported.
+// all other method pointers are either stubs returning errors, or no-ops.
+// See <openssl/rsa.h> for exact declaration of RSA_METHOD.
+
+int RsaMethodPubEnc(int /* flen */,
+ const unsigned char* /* from */,
+ unsigned char* /* to */,
+ RSA* /* rsa */,
+ int /* padding */) {
+ RSAerr(RSA_F_RSA_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
+ return -1;
+}
+
+int RsaMethodPubDec(int flen,
+ const unsigned char* from,
+ unsigned char* to,
+ RSA* rsa,
+ int padding) {
+ RSAerr(RSA_F_RSA_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
+ return -1;
+}
+
+// See RSA_eay_private_encrypt in
+// third_party/openssl/openssl/crypto/rsa/rsa_eay.c for the default
+// implementation of this function.
+int RsaMethodPrivEnc(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding) {
+ if (padding != RSA_PKCS1_PADDING) {
+ // TODO(davidben): If we need to, we can implement RSA_NO_PADDING
+ // by using javax.crypto.Cipher and picking either the
+ // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as
+ // appropriate. I believe support for both of these was added in
+ // the same Android version as the "NONEwithRSA"
+ // java.security.Signature algorithm, so the same version checks
+ // for GetRsaLegacyKey should work.
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+ return -1;
+ }
+
+ // Retrieve private key JNI reference.
+ jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
+ if (!private_key) {
+ ALOGE("Null JNI reference passed to RsaMethodPrivEnc!");
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ JNIEnv* env = getJNIEnv();
+ if (env == NULL) {
+ return -1;
+ }
+
+ // For RSA keys, this function behaves as RSA_private_encrypt with
+ // PKCS#1 padding.
+ ScopedLocalRef<jbyteArray> signature(
+ env, rawSignDigestWithPrivateKey(env, private_key,
+ reinterpret_cast<const char*>(from), flen));
+ if (signature.get() == NULL) {
+ ALOGE("Could not sign message in RsaMethodPrivEnc!");
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ ScopedByteArrayRO signatureBytes(env, signature.get());
+ size_t expected_size = static_cast<size_t>(RSA_size(rsa));
+ if (signatureBytes.size() > expected_size) {
+ ALOGE("RSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(),
+ expected_size);
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ // Copy result to OpenSSL-provided buffer. rawSignDigestWithPrivateKey
+ // should pad with leading 0s, but if it doesn't, pad the result.
+ size_t zero_pad = expected_size - signatureBytes.size();
+ memset(to, 0, zero_pad);
+ memcpy(to + zero_pad, signatureBytes.get(), signatureBytes.size());
+
+ return expected_size;
+}
+
+int RsaMethodPrivDec(int flen,
+ const unsigned char* from,
+ unsigned char* to,
+ RSA* rsa,
+ int padding) {
+ if (padding != RSA_PKCS1_PADDING) {
+ RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+ return -1;
+ }
+
+ // Retrieve private key JNI reference.
+ jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
+ if (!private_key) {
+ ALOGE("Null JNI reference passed to RsaMethodPrivDec!");
+ RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ JNIEnv* env = getJNIEnv();
+ if (env == NULL) {
+ return -1;
+ }
+
+ // For RSA keys, this function behaves as RSA_private_decrypt with
+ // PKCS#1 padding.
+ ScopedLocalRef<jbyteArray> cleartext(env, rawCipherWithPrivateKey(env, private_key, false,
+ reinterpret_cast<const char*>(from), flen));
+ if (cleartext.get() == NULL) {
+ ALOGE("Could not decrypt message in RsaMethodPrivDec!");
+ RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ ScopedByteArrayRO cleartextBytes(env, cleartext.get());
+ size_t expected_size = static_cast<size_t>(RSA_size(rsa));
+ if (cleartextBytes.size() > expected_size) {
+ ALOGE("RSA ciphertext size mismatch, actual: %zd, expected <= %zd", cleartextBytes.size(),
+ expected_size);
+ RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ // Copy result to OpenSSL-provided buffer.
+ memcpy(to, cleartextBytes.get(), cleartextBytes.size());
+
+ return cleartextBytes.size();
+}
+
+int RsaMethodInit(RSA*) {
+ return 0;
+}
+
+int RsaMethodFinish(RSA* rsa) {
+ // Ensure the global JNI reference created with this wrapper is
+ // properly destroyed with it.
+ jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
+ if (key != NULL) {
+ RSA_set_app_data(rsa, NULL);
+ JNIEnv* env = getJNIEnv();
+ env->DeleteGlobalRef(key);
+ }
+ // Actual return value is ignored by OpenSSL. There are no docs
+ // explaining what this is supposed to be.
+ return 0;
+}
+
+const RSA_METHOD android_rsa_method = {
+ /* .name = */ "Android signing-only RSA method",
+ /* .rsa_pub_enc = */ RsaMethodPubEnc,
+ /* .rsa_pub_dec = */ RsaMethodPubDec,
+ /* .rsa_priv_enc = */ RsaMethodPrivEnc,
+ /* .rsa_priv_dec = */ RsaMethodPrivDec,
+ /* .rsa_mod_exp = */ NULL,
+ /* .bn_mod_exp = */ NULL,
+ /* .init = */ RsaMethodInit,
+ /* .finish = */ RsaMethodFinish,
+ // This flag is necessary to tell OpenSSL to avoid checking the content
+ // (i.e. internal fields) of the private key. Otherwise, it will complain
+ // it's not valid for the certificate.
+ /* .flags = */ RSA_METHOD_FLAG_NO_CHECK,
+ /* .app_data = */ NULL,
+ /* .rsa_sign = */ NULL,
+ /* .rsa_verify = */ NULL,
+ /* .rsa_keygen = */ NULL,
+};
+
+// Custom DSA_METHOD that uses the platform APIs.
+// Note that for now, only signing through DSA_sign() is really supported.
+// all other method pointers are either stubs returning errors, or no-ops.
+// See <openssl/dsa.h> for exact declaration of DSA_METHOD.
+//
+// Note: There is no DSA_set_app_data() and DSA_get_app_data() functions,
+// but RSA_set_app_data() is defined as a simple macro that calls
+// RSA_set_ex_data() with a hard-coded index of 0, so this code
+// does the same thing here.
+
+DSA_SIG* DsaMethodDoSign(const unsigned char* dgst,
+ int dlen,
+ DSA* dsa) {
+ // Extract the JNI reference to the PrivateKey object.
+ jobject private_key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa, 0));
+ if (private_key == NULL) return NULL;
+
+ JNIEnv* env = getJNIEnv();
+ if (env == NULL) {
+ return NULL;
+ }
+
+ // Sign the message with it, calling platform APIs.
+ ScopedLocalRef<jbyteArray> signature(
+ env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(dgst),
+ dlen));
+ if (signature.get() == NULL) {
+ ALOGE("Could not sign message in DsaMethodDoSign!");
+ return NULL;
+ }
+
+ ScopedByteArrayRO signatureBytes(env, signature.get());
+ // Note: With DSA, the actual signature might be smaller than DSA_size().
+ size_t max_expected_size = static_cast<size_t>(DSA_size(dsa));
+ if (signatureBytes.size() > max_expected_size) {
+ ALOGE("DSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(),
+ max_expected_size);
+ return NULL;
+ }
+
+ // Convert the signature into a DSA_SIG object.
+ const unsigned char* sigbuf = reinterpret_cast<const unsigned char*>(signatureBytes.get());
+ int siglen = static_cast<size_t>(signatureBytes.size());
+ DSA_SIG* dsa_sig = d2i_DSA_SIG(NULL, &sigbuf, siglen);
+ return dsa_sig;
+}
+
+int DsaMethodSignSetup(DSA* /* dsa */,
+ BN_CTX* /* ctx_in */,
+ BIGNUM** /* kinvp */,
+ BIGNUM** /* rp */,
+ const unsigned char* /* dgst */,
+ int /* dlen */) {
+ DSAerr(DSA_F_DSA_SIGN_SETUP, DSA_R_INVALID_DIGEST_TYPE);
+ return -1;
+}
+
+int DsaMethodDoVerify(const unsigned char* /* dgst */,
+ int /* dgst_len */,
+ DSA_SIG* /* sig */,
+ DSA* /* dsa */) {
+ DSAerr(DSA_F_DSA_DO_VERIFY, DSA_R_INVALID_DIGEST_TYPE);
+ return -1;
+}
+
+int DsaMethodFinish(DSA* dsa) {
+ // Free the global JNI reference that was created with this
+ // wrapper key.
+ jobject key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa, 0));
+ if (key != NULL) {
+ DSA_set_ex_data(dsa, 0, NULL);
+ JNIEnv* env = getJNIEnv();
+ env->DeleteGlobalRef(key);
+ }
+ // Actual return value is ignored by OpenSSL. There are no docs
+ // explaining what this is supposed to be.
+ return 0;
+}
+
+const DSA_METHOD android_dsa_method = {
+ /* .name = */ "Android signing-only DSA method",
+ /* .dsa_do_sign = */ DsaMethodDoSign,
+ /* .dsa_sign_setup = */ DsaMethodSignSetup,
+ /* .dsa_do_verify = */ DsaMethodDoVerify,
+ /* .dsa_mod_exp = */ NULL,
+ /* .bn_mod_exp = */ NULL,
+ /* .init = */ NULL, // nothing to do here.
+ /* .finish = */ DsaMethodFinish,
+ /* .flags = */ 0,
+ /* .app_data = */ NULL,
+ /* .dsa_paramgem = */ NULL,
+ /* .dsa_keygen = */ NULL};
+
+// Used to ensure that the global JNI reference associated with a custom
+// EC_KEY + ECDSA_METHOD wrapper is released when its EX_DATA is destroyed
+// (this function is called when EVP_PKEY_free() is called on the wrapper).
+void ExDataFree(void* /* parent */,
+ void* ptr,
+ CRYPTO_EX_DATA* ad,
+ int idx,
+ long /* argl */,
+ void* /* argp */) {
+ jobject private_key = reinterpret_cast<jobject>(ptr);
+ if (private_key == NULL) return;
+
+ CRYPTO_set_ex_data(ad, idx, NULL);
+ JNIEnv* env = getJNIEnv();
+ env->DeleteGlobalRef(private_key);
+}
+
+int ExDataDup(CRYPTO_EX_DATA* /* to */,
+ CRYPTO_EX_DATA* /* from */,
+ void* /* from_d */,
+ int /* idx */,
+ long /* argl */,
+ void* /* argp */) {
+ // This callback shall never be called with the current OpenSSL
+ // implementation (the library only ever duplicates EX_DATA items
+ // for SSL and BIO objects). But provide this to catch regressions
+ // in the future.
+ // Return value is currently ignored by OpenSSL.
+ return 0;
+}
+
+class EcdsaExDataIndex {
+ public:
+ int ex_data_index() { return ex_data_index_; }
+
+ static EcdsaExDataIndex& Instance() {
+ static EcdsaExDataIndex singleton;
+ return singleton;
+ }
+
+ private:
+ EcdsaExDataIndex() {
+ ex_data_index_ = ECDSA_get_ex_new_index(0, NULL, NULL, ExDataDup, ExDataFree);
+ }
+ EcdsaExDataIndex(EcdsaExDataIndex const&);
+ ~EcdsaExDataIndex() {}
+ EcdsaExDataIndex& operator=(EcdsaExDataIndex const&);
+
+ int ex_data_index_;
+};
+
+// Returns the index of the custom EX_DATA used to store the JNI reference.
+int EcdsaGetExDataIndex(void) {
+ EcdsaExDataIndex& exData = EcdsaExDataIndex::Instance();
+ return exData.ex_data_index();
+}
+
+ECDSA_SIG* EcdsaMethodDoSign(const unsigned char* dgst, int dgst_len, const BIGNUM* /* inv */,
+ const BIGNUM* /* rp */, EC_KEY* eckey) {
+ // Retrieve private key JNI reference.
+ jobject private_key =
+ reinterpret_cast<jobject>(ECDSA_get_ex_data(eckey, EcdsaGetExDataIndex()));
+ if (!private_key) {
+ ALOGE("Null JNI reference passed to EcdsaMethodDoSign!");
+ return NULL;
+ }
+ JNIEnv* env = getJNIEnv();
+ if (env == NULL) {
+ return NULL;
+ }
+
+ // Sign message with it through JNI.
+ ScopedLocalRef<jbyteArray> signature(
+ env, rawSignDigestWithPrivateKey(env, private_key, reinterpret_cast<const char*>(dgst),
+ dgst_len));
+ if (signature.get() == NULL) {
+ ALOGE("Could not sign message in EcdsaMethodDoSign!");
+ return NULL;
+ }
+
+ ScopedByteArrayRO signatureBytes(env, signature.get());
+ // Note: With ECDSA, the actual signature may be smaller than
+ // ECDSA_size().
+ size_t max_expected_size = static_cast<size_t>(ECDSA_size(eckey));
+ if (signatureBytes.size() > max_expected_size) {
+ ALOGE("ECDSA Signature size mismatch, actual: %zd, expected <= %zd", signatureBytes.size(),
+ max_expected_size);
+ return NULL;
+ }
+
+ // Convert signature to ECDSA_SIG object
+ const unsigned char* sigbuf = reinterpret_cast<const unsigned char*>(signatureBytes.get());
+ long siglen = static_cast<long>(signatureBytes.size());
+ return d2i_ECDSA_SIG(NULL, &sigbuf, siglen);
+}
+
+int EcdsaMethodSignSetup(EC_KEY* /* eckey */,
+ BN_CTX* /* ctx */,
+ BIGNUM** /* kinv */,
+ BIGNUM** /* r */,
+ const unsigned char*,
+ int) {
+ ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ECDSA_R_ERR_EC_LIB);
+ return -1;
+}
+
+int EcdsaMethodDoVerify(const unsigned char* /* dgst */,
+ int /* dgst_len */,
+ const ECDSA_SIG* /* sig */,
+ EC_KEY* /* eckey */) {
+ ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_ERR_EC_LIB);
+ return -1;
+}
+
+const ECDSA_METHOD android_ecdsa_method = {
+ /* .name = */ "Android signing-only ECDSA method",
+ /* .ecdsa_do_sign = */ EcdsaMethodDoSign,
+ /* .ecdsa_sign_setup = */ EcdsaMethodSignSetup,
+ /* .ecdsa_do_verify = */ EcdsaMethodDoVerify,
+ /* .flags = */ 0,
+ /* .app_data = */ NULL,
+};
+
+#ifdef CONSCRYPT_UNBUNDLED
+/*
+ * This is a big hack; don't learn from this. Basically what happened is we do
+ * not have an API way to insert ourselves into the AsynchronousCloseMonitor
+ * that's compiled into the native libraries for libcore when we're unbundled.
+ * So we try to look up the symbol from the main library to find it.
+ */
+typedef void (*acm_ctor_func)(void*, int);
+typedef void (*acm_dtor_func)(void*);
+static acm_ctor_func async_close_monitor_ctor = NULL;
+static acm_dtor_func async_close_monitor_dtor = NULL;
+
+class CompatibilityCloseMonitor {
+public:
+ CompatibilityCloseMonitor(int fd) {
+ if (async_close_monitor_ctor != NULL) {
+ async_close_monitor_ctor(objBuffer, fd);
+ }
+ }
+
+ ~CompatibilityCloseMonitor() {
+ if (async_close_monitor_dtor != NULL) {
+ async_close_monitor_dtor(objBuffer);
+ }
+ }
+private:
+ char objBuffer[256];
+#if 0
+ static_assert(sizeof(objBuffer) > 2*sizeof(AsynchronousCloseMonitor),
+ "CompatibilityCloseMonitor must be larger than the actual object");
+#endif
+};
+
+static void findAsynchronousCloseMonitorFuncs() {
+ void *lib = dlopen("libjavacore.so", RTLD_NOW);
+ if (lib != NULL) {
+ async_close_monitor_ctor = (acm_ctor_func) dlsym(lib, "_ZN24AsynchronousCloseMonitorC1Ei");
+ async_close_monitor_dtor = (acm_dtor_func) dlsym(lib, "_ZN24AsynchronousCloseMonitorD1Ev");
+ }
+}
+#endif
+
/**
* Copied from libnativehelper NetworkUtilites.cpp
*/
@@ -1376,6 +1974,64 @@
return ret;
}
+static jlong NativeCrypto_EVP_PKEY_new_DH(JNIEnv* env, jclass,
+ jbyteArray p, jbyteArray g,
+ jbyteArray pub_key, jbyteArray priv_key) {
+ JNI_TRACE("EVP_PKEY_new_DH(p=%p, g=%p, pub_key=%p, priv_key=%p)",
+ p, g, pub_key, priv_key);
+
+ Unique_DH dh(DH_new());
+ if (dh.get() == NULL) {
+ jniThrowRuntimeException(env, "DH_new failed");
+ return 0;
+ }
+
+ if (!arrayToBignum(env, p, &dh->p)) {
+ return 0;
+ }
+
+ if (!arrayToBignum(env, g, &dh->g)) {
+ return 0;
+ }
+
+ if (pub_key != NULL && !arrayToBignum(env, pub_key, &dh->pub_key)) {
+ return 0;
+ }
+
+ if (priv_key != NULL && !arrayToBignum(env, priv_key, &dh->priv_key)) {
+ return 0;
+ }
+
+ if (dh->p == NULL || dh->g == NULL
+ || (pub_key != NULL && dh->pub_key == NULL)
+ || (priv_key != NULL && dh->priv_key == NULL)) {
+ jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+ return 0;
+ }
+
+ /* The public key can be recovered if the private key is available. */
+ if (dh->pub_key == NULL && dh->priv_key != NULL) {
+ if (!DH_generate_key(dh.get())) {
+ jniThrowRuntimeException(env, "EVP_PKEY_new_DH failed during pub_key generation");
+ return 0;
+ }
+ }
+
+ Unique_EVP_PKEY pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL) {
+ jniThrowRuntimeException(env, "EVP_PKEY_new failed");
+ return 0;
+ }
+ if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
+ jniThrowRuntimeException(env, "EVP_PKEY_assign_DH failed");
+ return 0;
+ }
+ OWNERSHIP_TRANSFERRED(dh);
+ JNI_TRACE("EVP_PKEY_new_DH(p=%p, g=%p, pub_key=%p, priv_key=%p) => %p",
+ p, g, pub_key, priv_key, pkey.get());
+ return reinterpret_cast<jlong>(pkey.release());
+}
+
/**
* public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g,
* byte[] pub_key, byte[] priv_key);
@@ -1836,6 +2492,112 @@
return reinterpret_cast<uintptr_t>(pkey.release());
}
+static jlong NativeCrypto_getRSAPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey,
+ jbyteArray modulusBytes) {
+ JNI_TRACE("getRSAPrivateKeyWrapper(%p, %p)", javaKey, modulus);
+
+ Unique_RSA rsa(RSA_new());
+ if (rsa.get() == NULL) {
+ jniThrowOutOfMemory(env, "Unable to allocate RSA key");
+ return 0;
+ }
+
+ RSA_set_method(rsa.get(), &android_rsa_method);
+
+ if (!arrayToBignum(env, modulusBytes, &rsa->n)) {
+ return 0;
+ }
+
+ RSA_set_app_data(rsa.get(), env->NewGlobalRef(javaKey));
+
+ Unique_EVP_PKEY pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL) {
+ JNI_TRACE("getRSAPrivateKeyWrapper failed");
+ jniThrowRuntimeException(env, "NativeCrypto_getRSAPrivateKeyWrapper failed");
+ freeOpenSslErrorState();
+ return 0;
+ }
+
+ if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
+ jniThrowRuntimeException(env, "getRSAPrivateKeyWrapper failed");
+ return 0;
+ }
+ OWNERSHIP_TRANSFERRED(rsa);
+ return reinterpret_cast<uintptr_t>(pkey.release());
+}
+
+static jlong NativeCrypto_getDSAPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey,
+ jbyteArray qBytes) {
+ JNI_TRACE("getDSAPrivateKeyWrapper(%p, %p)", javaKey, modulus);
+
+ Unique_DSA dsa(DSA_new());
+ if (dsa.get() == NULL) {
+ jniThrowOutOfMemory(env, "Unable to allocate DSA key");
+ return 0;
+ }
+
+ if (!arrayToBignum(env, qBytes, &dsa->q)) {
+ return 0;
+ }
+
+ DSA_set_method(dsa.get(), &android_dsa_method);
+ DSA_set_ex_data(dsa.get(), 0, env->NewGlobalRef(javaKey));
+
+ Unique_EVP_PKEY pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL) {
+ JNI_TRACE("getDSAPrivateKeyWrapper failed");
+ jniThrowRuntimeException(env, "NativeCrypto_getDSAPrivateKeyWrapper failed");
+ freeOpenSslErrorState();
+ return 0;
+ }
+
+ if (EVP_PKEY_assign_DSA(pkey.get(), dsa.get()) != 1) {
+ jniThrowRuntimeException(env, "getDSAPrivateKeyWrapper failed");
+ return 0;
+ }
+ OWNERSHIP_TRANSFERRED(dsa);
+ return reinterpret_cast<uintptr_t>(pkey.release());
+}
+
+static jlong NativeCrypto_getECPrivateKeyWrapper(JNIEnv* env, jclass, jobject javaKey, jlong groupRef) {
+ JNI_TRACE("getECPrivateKeyWrapper(%p, %p)", javaKey, modulus);
+
+ Unique_EC_KEY ecKey(EC_KEY_new());
+ if (ecKey.get() == NULL) {
+ jniThrowOutOfMemory(env, "Unable to allocate EC key");
+ return 0;
+ }
+
+ const EC_GROUP* group = reinterpret_cast<const EC_GROUP*>(groupRef);
+ JNI_TRACE("EC_GROUP_get_curve_name(%p)", group);
+
+ if (group == NULL) {
+ JNI_TRACE("EC_GROUP_get_curve_name => group == NULL");
+ jniThrowNullPointerException(env, "group == NULL");
+ return 0;
+ }
+
+ EC_KEY_set_group(ecKey.get(), group);
+
+ ECDSA_set_method(ecKey.get(), &android_ecdsa_method);
+ ECDSA_set_ex_data(ecKey.get(), EcdsaGetExDataIndex(), env->NewGlobalRef(javaKey));
+
+ Unique_EVP_PKEY pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL) {
+ JNI_TRACE("getDSAPrivateKeyWrapper failed");
+ jniThrowRuntimeException(env, "NativeCrypto_getDSAPrivateKeyWrapper failed");
+ freeOpenSslErrorState();
+ return 0;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+ jniThrowRuntimeException(env, "getECPrivateKeyWrapper failed");
+ return 0;
+ }
+ OWNERSHIP_TRANSFERRED(ecKey);
+ return reinterpret_cast<uintptr_t>(pkey.release());
+}
+
/*
* public static native int RSA_generate_key(int modulusBits, byte[] publicExponent);
*/
@@ -1880,6 +2642,11 @@
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("RSA_size(%p)", pkey);
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return 0;
+ }
+
Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == NULL) {
jniThrowRuntimeException(env, "RSA_size failed");
@@ -1898,6 +2665,11 @@
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("%s(%d, %p, %p, %p)", caller, flen, fromJavaBytes, toJavaBytes, pkey);
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return -1;
+ }
+
Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
if (rsa.get() == NULL) {
return -1;
@@ -2160,6 +2932,11 @@
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("get_DSA_params(%p)", pkey);
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return NULL;
+ }
+
Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
if (dsa.get() == NULL) {
throwExceptionIfNecessary(env, "get_DSA_params failed");
@@ -2214,6 +2991,139 @@
return joa;
}
+static void NativeCrypto_set_DSA_flag_nonce_from_hash(JNIEnv* env, jclass, jlong pkeyRef)
+{
+ EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+ JNI_TRACE("set_DSA_flag_nonce_from_hash(%p)", pkey);
+
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return;
+ }
+
+ Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
+ if (dsa.get() == NULL) {
+ throwExceptionIfNecessary(env, "set_DSA_flag_nonce_from_hash failed");
+ return;
+ }
+
+ dsa->flags |= DSA_FLAG_NONCE_FROM_HASH;
+}
+
+static jlong NativeCrypto_DH_generate_parameters_ex(JNIEnv* env, jclass, jint primeBits, jlong generator) {
+ JNI_TRACE("DH_generate_parameters_ex(%d, %d)", primeBits, generator);
+
+ Unique_DH dh(DH_new());
+ if (dh.get() == NULL) {
+ JNI_TRACE("DH_generate_parameters_ex failed");
+ jniThrowOutOfMemory(env, "Unable to allocate DH key");
+ freeOpenSslErrorState();
+ return 0;
+ }
+
+ JNI_TRACE("DH_generate_parameters_ex generating parameters");
+
+ if (!DH_generate_parameters_ex(dh.get(), primeBits, generator, NULL)) {
+ JNI_TRACE("DH_generate_parameters_ex => param generation failed");
+ throwExceptionIfNecessary(env, "NativeCrypto_DH_generate_parameters_ex failed");
+ return 0;
+ }
+
+ Unique_EVP_PKEY pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL) {
+ JNI_TRACE("DH_generate_parameters_ex failed");
+ jniThrowRuntimeException(env, "NativeCrypto_DH_generate_parameters_ex failed");
+ freeOpenSslErrorState();
+ return 0;
+ }
+
+ if (EVP_PKEY_assign_DH(pkey.get(), dh.get()) != 1) {
+ JNI_TRACE("DH_generate_parameters_ex failed");
+ throwExceptionIfNecessary(env, "NativeCrypto_DH_generate_parameters_ex failed");
+ return 0;
+ }
+
+ OWNERSHIP_TRANSFERRED(dh);
+ JNI_TRACE("DH_generate_parameters_ex(n=%d, g=%d) => %p", primeBits, generator, pkey.get());
+ return reinterpret_cast<uintptr_t>(pkey.release());
+}
+
+static void NativeCrypto_DH_generate_key(JNIEnv* env, jclass, jlong pkeyRef) {
+ JNI_TRACE("DH_generate_key(%p)", pkeyRef);
+ EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ }
+
+ Unique_DH dh(EVP_PKEY_get1_DH(pkey));
+ if (dh.get() == NULL) {
+ JNI_TRACE("DH_generate_key failed");
+ throwExceptionIfNecessary(env, "Unable to get DH key");
+ freeOpenSslErrorState();
+ }
+
+ if (!DH_generate_key(dh.get())) {
+ JNI_TRACE("DH_generate_key failed");
+ throwExceptionIfNecessary(env, "NativeCrypto_DH_generate_key failed");
+ }
+}
+
+static jobjectArray NativeCrypto_get_DH_params(JNIEnv* env, jclass, jlong pkeyRef) {
+ EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+ JNI_TRACE("get_DH_params(%p)", pkey);
+
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return NULL;
+ }
+
+ Unique_DH dh(EVP_PKEY_get1_DH(pkey));
+ if (dh.get() == NULL) {
+ throwExceptionIfNecessary(env, "get_DH_params failed");
+ return 0;
+ }
+
+ jobjectArray joa = env->NewObjectArray(4, byteArrayClass, NULL);
+ if (joa == NULL) {
+ return NULL;
+ }
+
+ if (dh->p != NULL) {
+ jbyteArray p = bignumToArray(env, dh->p, "p");
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ env->SetObjectArrayElement(joa, 0, p);
+ }
+
+ if (dh->g != NULL) {
+ jbyteArray g = bignumToArray(env, dh->g, "g");
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ env->SetObjectArrayElement(joa, 1, g);
+ }
+
+ if (dh->pub_key != NULL) {
+ jbyteArray pub_key = bignumToArray(env, dh->pub_key, "pub_key");
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ env->SetObjectArrayElement(joa, 2, pub_key);
+ }
+
+ if (dh->priv_key != NULL) {
+ jbyteArray priv_key = bignumToArray(env, dh->priv_key, "priv_key");
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ env->SetObjectArrayElement(joa, 3, priv_key);
+ }
+
+ return joa;
+}
+
#define EC_CURVE_GFP 1
#define EC_CURVE_GF2M 2
@@ -2821,6 +3731,11 @@
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EC_KEY_get_private_key(%p)", pkey);
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return NULL;
+ }
+
Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey));
if (eckey.get() == NULL) {
throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY");
@@ -2844,6 +3759,11 @@
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EC_KEY_get_public_key(%p)", pkey);
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return 0;
+ }
+
Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey));
if (eckey.get() == NULL) {
throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY");
@@ -2862,6 +3782,26 @@
return reinterpret_cast<uintptr_t>(dup.release());
}
+static void NativeCrypto_EC_KEY_set_nonce_from_hash(JNIEnv* env, jclass, jlong pkeyRef,
+ jboolean enabled)
+{
+ EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
+ JNI_TRACE("EC_KEY_set_nonce_from_hash(%p, %d)", pkey, enabled ? 1 : 0);
+
+ if (pkey == NULL) {
+ jniThrowNullPointerException(env, "pkey == null");
+ return;
+ }
+
+ Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey));
+ if (eckey.get() == NULL) {
+ throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY");
+ return;
+ }
+
+ EC_KEY_set_nonce_from_hash(eckey.get(), enabled ? 1 : 0);
+}
+
static jint NativeCrypto_ECDH_compute_key(JNIEnv* env, jclass,
jbyteArray outArray, jint outOffset, jlong pubkeyRef, jlong privkeyRef)
{
@@ -2881,6 +3821,11 @@
return -1;
}
+ if (pubPkey == NULL) {
+ jniThrowNullPointerException(env, "pubPkey == null");
+ return -1;
+ }
+
Unique_EC_KEY pubkey(EVP_PKEY_get1_EC_KEY(pubPkey));
if (pubkey.get() == NULL) {
JNI_TRACE("ECDH_compute_key(%p) => can't get public key", pubPkey);
@@ -2895,6 +3840,11 @@
return -1;
}
+ if (privPkey == NULL) {
+ jniThrowNullPointerException(env, "privPkey == null");
+ return -1;
+ }
+
Unique_EC_KEY privkey(EVP_PKEY_get1_EC_KEY(privPkey));
if (privkey.get() == NULL) {
throwExceptionIfNecessary(env, "EVP_PKEY_get1_EC_KEY private");
@@ -2917,7 +3867,7 @@
}
static jlong NativeCrypto_EVP_MD_CTX_create(JNIEnv* env, jclass) {
- JNI_TRACE("EVP_MD_CTX_create()");
+ JNI_TRACE_MD("EVP_MD_CTX_create()");
Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create());
if (ctx.get() == NULL) {
@@ -2925,13 +3875,13 @@
return 0;
}
- JNI_TRACE("EVP_MD_CTX_create() => %p", ctx.get());
+ JNI_TRACE_MD("EVP_MD_CTX_create() => %p", ctx.get());
return reinterpret_cast<uintptr_t>(ctx.release());
}
-static void NativeCrypto_EVP_MD_CTX_init(JNIEnv*, jclass, jlong ctxRef) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
- JNI_TRACE("NativeCrypto_EVP_MD_CTX_init(%p)", ctx);
+static void NativeCrypto_EVP_MD_CTX_init(JNIEnv* env, jclass, jobject ctxRef) {
+ EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
+ JNI_TRACE_MD("EVP_MD_CTX_init(%p)", ctx);
if (ctx != NULL) {
EVP_MD_CTX_init(ctx);
@@ -2940,50 +3890,45 @@
static void NativeCrypto_EVP_MD_CTX_destroy(JNIEnv*, jclass, jlong ctxRef) {
EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
- JNI_TRACE("NativeCrypto_EVP_MD_CTX_destroy(%p)", ctx);
+ JNI_TRACE_MD("EVP_MD_CTX_destroy(%p)", ctx);
if (ctx != NULL) {
EVP_MD_CTX_destroy(ctx);
}
}
-static jlong NativeCrypto_EVP_MD_CTX_copy(JNIEnv* env, jclass, jlong ctxRef) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
- JNI_TRACE("NativeCrypto_EVP_MD_CTX_copy(%p)", ctx);
+static jint NativeCrypto_EVP_MD_CTX_copy(JNIEnv* env, jclass, jobject dstCtxRef, jobject srcCtxRef) {
+ EVP_MD_CTX* dst_ctx = fromContextObject<EVP_MD_CTX>(env, dstCtxRef);
+ const EVP_MD_CTX* src_ctx = fromContextObject<EVP_MD_CTX>(env, srcCtxRef);
+ JNI_TRACE_MD("EVP_MD_CTX_copy(%p. %p)", dst_ctx, src_ctx);
- if (ctx == NULL) {
- jniThrowNullPointerException(env, "ctx == null");
+ if (src_ctx == NULL) {
+ return 0;
+ } else if (dst_ctx == NULL) {
return 0;
}
- EVP_MD_CTX* copy = EVP_MD_CTX_create();
- if (copy == NULL) {
- jniThrowOutOfMemory(env, "Unable to allocate copy of EVP_MD_CTX");
- return 0;
- }
-
- EVP_MD_CTX_init(copy);
- int result = EVP_MD_CTX_copy_ex(copy, ctx);
+ int result = EVP_MD_CTX_copy_ex(dst_ctx, src_ctx);
if (result == 0) {
- EVP_MD_CTX_destroy(copy);
jniThrowRuntimeException(env, "Unable to copy EVP_MD_CTX");
freeOpenSslErrorState();
- return 0;
}
- JNI_TRACE("NativeCrypto_EVP_MD_CTX_copy(%p) => %p", ctx, copy);
- return reinterpret_cast<uintptr_t>(copy);
+ JNI_TRACE_MD("EVP_MD_CTX_copy(%p, %p) => %d", dst_ctx, src_ctx, result);
+ return result;
}
/*
* public static native int EVP_DigestFinal(long, byte[], int)
*/
-static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass, jlong ctxRef,
- jbyteArray hash, jint offset) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
- JNI_TRACE("NativeCrypto_EVP_DigestFinal(%p, %p, %d)", ctx, hash, offset);
+static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass, jobject ctxRef, jbyteArray hash,
+ jint offset) {
+ EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
+ JNI_TRACE_MD("EVP_DigestFinal(%p, %p, %d)", ctx, hash, offset);
- if (ctx == NULL || hash == NULL) {
+ if (ctx == NULL) {
+ return -1;
+ } else if (hash == NULL) {
jniThrowNullPointerException(env, "ctx == null || hash == null");
return -1;
}
@@ -2993,45 +3938,52 @@
return -1;
}
unsigned int bytesWritten = -1;
- int ok = EVP_DigestFinal(ctx,
+ int ok = EVP_DigestFinal_ex(ctx,
reinterpret_cast<unsigned char*>(hashBytes.get() + offset),
&bytesWritten);
if (ok == 0) {
- throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestFinal");
+ throwExceptionIfNecessary(env, "EVP_DigestFinal");
}
- EVP_MD_CTX_destroy(ctx);
- JNI_TRACE("NativeCrypto_EVP_DigestFinal(%p, %p, %d) => %d", ctx, hash, offset, bytesWritten);
+ JNI_TRACE_MD("EVP_DigestFinal(%p, %p, %d) => %d", ctx, hash, offset, bytesWritten);
return bytesWritten;
}
-/*
- * public static native int EVP_DigestInit(long)
- */
-static jlong NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass, jlong evpMdRef) {
+static jint evpInit(JNIEnv* env, jobject evpMdCtxRef, jlong evpMdRef, const char* jniName,
+ int (*init_func)(EVP_MD_CTX*, const EVP_MD*, ENGINE*)) {
+ EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
const EVP_MD* evp_md = reinterpret_cast<const EVP_MD*>(evpMdRef);
- JNI_TRACE("NativeCrypto_EVP_DigestInit(%p)", evp_md);
+ JNI_TRACE_MD("%s(%p, %p)", jniName, ctx, evp_md);
- if (evp_md == NULL) {
- jniThrowNullPointerException(env, NULL);
+ if (ctx == NULL) {
+ return 0;
+ } else if (evp_md == NULL) {
+ jniThrowNullPointerException(env, "evp_md == null");
return 0;
}
- Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create());
- if (ctx.get() == NULL) {
- jniThrowOutOfMemory(env, "Unable to allocate EVP_MD_CTX");
- return 0;
- }
- JNI_TRACE("NativeCrypto_EVP_DigestInit ctx=%p", ctx.get());
-
- int ok = EVP_DigestInit(ctx.get(), evp_md);
+ int ok = init_func(ctx, evp_md, NULL);
if (ok == 0) {
- bool exception = throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestInit");
+ bool exception = throwExceptionIfNecessary(env, jniName);
if (exception) {
+ JNI_TRACE("%s(%p) => threw exception", jniName, evp_md);
return 0;
}
}
- return reinterpret_cast<uintptr_t>(ctx.release());
+ JNI_TRACE_MD("%s(%p, %p) => %d", jniName, ctx, evp_md, ok);
+ return ok;
+}
+
+static jint NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong evpMdRef) {
+ return evpInit(env, evpMdCtxRef, evpMdRef, "EVP_DigestInit", EVP_DigestInit_ex);
+}
+
+static jint NativeCrypto_EVP_SignInit(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong evpMdRef) {
+ return evpInit(env, evpMdCtxRef, evpMdRef, "EVP_SignInit", EVP_DigestInit_ex);
+}
+
+static jint NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass, jobject evpMdCtxRef, jlong evpMdRef) {
+ return evpInit(env, evpMdCtxRef, evpMdRef, "EVP_VerifyInit", EVP_DigestInit_ex);
}
/*
@@ -3095,45 +4047,14 @@
return result;
}
-/*
- * public static native void EVP_DigestUpdate(long, byte[], int, int)
- */
-static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass, jlong ctxRef,
- jbyteArray buffer, jint offset, jint length) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
- JNI_TRACE("NativeCrypto_EVP_DigestUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
-
- if (offset < 0 || length < 0) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
- return;
- }
-
- if (ctx == NULL || buffer == NULL) {
- jniThrowNullPointerException(env, NULL);
- return;
- }
-
- ScopedByteArrayRO bufferBytes(env, buffer);
- if (bufferBytes.get() == NULL) {
- return;
- }
- int ok = EVP_DigestUpdate(ctx,
- reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
- length);
- if (ok == 0) {
- throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestUpdate");
- }
-}
-
-static void NativeCrypto_EVP_DigestSignInit(JNIEnv* env, jclass, jlong evpMdCtxRef,
+static void NativeCrypto_EVP_DigestSignInit(JNIEnv* env, jclass, jobject evpMdCtxRef,
const jlong evpMdRef, jlong pkeyRef) {
- EVP_MD_CTX* mdCtx = reinterpret_cast<EVP_MD_CTX*>(evpMdCtxRef);
+ EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
const EVP_MD* md = reinterpret_cast<const EVP_MD*>(evpMdRef);
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("EVP_DigestSignInit(%p, %p, %p)", mdCtx, md, pkey);
if (mdCtx == NULL) {
- jniThrowNullPointerException(env, "mdCtx == null");
return;
}
@@ -3156,14 +4077,14 @@
JNI_TRACE("EVP_DigestSignInit(%p, %p, %p) => success", mdCtx, md, pkey);
}
-static void NativeCrypto_EVP_DigestSignUpdate(JNIEnv* env, jclass, jint evpMdCtxRef,
- jbyteArray inJavaBytes, jint inOffset, jint inLength)
+static void evpUpdate(JNIEnv* env, jobject evpMdCtxRef, jbyteArray inJavaBytes, jint inOffset,
+ jint inLength, const char *jniName, int (*update_func)(EVP_MD_CTX*, const void *,
+ size_t))
{
- EVP_MD_CTX* mdCtx = reinterpret_cast<EVP_MD_CTX*>(evpMdCtxRef);
- JNI_TRACE("EVP_DigestSignUpdate(%p, %p, %d, %d)", mdCtx, inJavaBytes, inOffset, inLength);
+ EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
+ JNI_TRACE_MD("%s(%p, %p, %d, %d)", jniName, mdCtx, inJavaBytes, inOffset, inLength);
if (mdCtx == NULL) {
- jniThrowNullPointerException(env, "mdCtx == null");
return;
}
@@ -3173,33 +4094,49 @@
}
if (inOffset < 0 || size_t(inOffset) > inBytes.size()) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "inOffset");
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inOffset");
return;
}
const ssize_t inEnd = inOffset + inLength;
- if (inEnd < 0 || size_t(inEnd) >= inBytes.size()) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "inLength");
+ if (inLength < 0 || inEnd < 0 || size_t(inEnd) > inBytes.size()) {
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "inLength");
return;
}
const unsigned char *tmp = reinterpret_cast<const unsigned char *>(inBytes.get());
- if (!EVP_DigestSignUpdate(mdCtx, tmp + inOffset, inLength)) {
- JNI_TRACE("ctx=%p EVP_DigestSignUpdate => threw exception", mdCtx);
- throwExceptionIfNecessary(env, "EVP_DigestSignUpdate");
+ if (!update_func(mdCtx, tmp + inOffset, inLength)) {
+ JNI_TRACE("ctx=%p %s => threw exception", mdCtx, jniName);
+ throwExceptionIfNecessary(env, jniName);
}
- JNI_TRACE("EVP_DigestSignUpdate(%p, %p, %d, %d) => success", mdCtx, inJavaBytes, inOffset,
- inLength);
+ JNI_TRACE_MD("%s(%p, %p, %d, %d) => success", jniName, mdCtx, inJavaBytes, inOffset, inLength);
}
-static jbyteArray NativeCrypto_EVP_DigestSignFinal(JNIEnv* env, jclass, jlong evpMdCtxRef)
+static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef,
+ jbyteArray inJavaBytes, jint inOffset, jint inLength) {
+ evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestUpdate",
+ EVP_DigestUpdate);
+}
+
+static void NativeCrypto_EVP_DigestSignUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef,
+ jbyteArray inJavaBytes, jint inOffset, jint inLength) {
+ evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_DigestSignUpdate",
+ EVP_DigestUpdate);
+}
+
+static void NativeCrypto_EVP_SignUpdate(JNIEnv* env, jclass, jobject evpMdCtxRef,
+ jbyteArray inJavaBytes, jint inOffset, jint inLength) {
+ evpUpdate(env, evpMdCtxRef, inJavaBytes, inOffset, inLength, "EVP_SignUpdate",
+ EVP_DigestUpdate);
+}
+
+static jbyteArray NativeCrypto_EVP_DigestSignFinal(JNIEnv* env, jclass, jobject evpMdCtxRef)
{
- EVP_MD_CTX* mdCtx = reinterpret_cast<EVP_MD_CTX*>(evpMdCtxRef);
+ EVP_MD_CTX* mdCtx = fromContextObject<EVP_MD_CTX>(env, evpMdCtxRef);
JNI_TRACE("EVP_DigestSignFinal(%p)", mdCtx);
if (mdCtx == NULL) {
- jniThrowNullPointerException(env, "mdCtx == null");
return NULL;
}
@@ -3229,82 +4166,18 @@
return outJavaBytes.release();
}
-static jlong NativeCrypto_EVP_SignInit(JNIEnv* env, jclass, jstring algorithm) {
- JNI_TRACE("NativeCrypto_EVP_SignInit(%p)", algorithm);
-
- if (algorithm == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
- }
-
- Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create());
- if (ctx.get() == NULL) {
- jniThrowOutOfMemory(env, "Unable to allocate EVP_MD_CTX");
- return 0;
- }
- JNI_TRACE("NativeCrypto_EVP_SignInit ctx=%p", ctx.get());
-
- ScopedUtfChars algorithmChars(env, algorithm);
- if (algorithmChars.c_str() == NULL) {
- return 0;
- }
- JNI_TRACE("NativeCrypto_EVP_SignInit algorithmChars=%s", algorithmChars.c_str());
-
- const EVP_MD* digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str()));
- if (digest == NULL) {
- JNI_TRACE("NativeCrypto_EVP_SignInit(%s) => hash not found", algorithmChars.c_str());
- throwExceptionIfNecessary(env, "Hash algorithm not found");
- return 0;
- }
-
- int ok = EVP_SignInit(ctx.get(), digest);
- if (ok == 0) {
- bool exception = throwExceptionIfNecessary(env, "NativeCrypto_EVP_SignInit");
- if (exception) {
- JNI_TRACE("NativeCrypto_EVP_SignInit(%s) => threw exception", algorithmChars.c_str());
- return 0;
- }
- }
-
- JNI_TRACE("NativeCrypto_EVP_SignInit(%s) => %p", algorithmChars.c_str(), ctx.get());
- return reinterpret_cast<uintptr_t>(ctx.release());
-}
-
-/*
- * public static native void EVP_SignUpdate(long, byte[], int, int)
- */
-static void NativeCrypto_EVP_SignUpdate(JNIEnv* env, jclass, jlong ctxRef,
- jbyteArray buffer, jint offset, jint length) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
- JNI_TRACE("NativeCrypto_EVP_SignUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
-
- if (ctx == NULL || buffer == NULL) {
- jniThrowNullPointerException(env, NULL);
- return;
- }
-
- ScopedByteArrayRO bufferBytes(env, buffer);
- if (bufferBytes.get() == NULL) {
- return;
- }
- int ok = EVP_SignUpdate(ctx,
- reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
- length);
- if (ok == 0) {
- throwExceptionIfNecessary(env, "NativeCrypto_EVP_SignUpdate");
- }
-}
-
/*
* public static native int EVP_SignFinal(long, byte[], int, long)
*/
-static jint NativeCrypto_EVP_SignFinal(JNIEnv* env, jclass, jlong ctxRef, jbyteArray signature,
+static jint NativeCrypto_EVP_SignFinal(JNIEnv* env, jclass, jobject ctxRef, jbyteArray signature,
jint offset, jlong pkeyRef) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
+ EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("NativeCrypto_EVP_SignFinal(%p, %p, %d, %p)", ctx, signature, offset, pkey);
- if (ctx == NULL || pkey == NULL) {
+ if (ctx == NULL) {
+ return -1;
+ } else if (pkey == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
@@ -3328,62 +4201,34 @@
}
/*
- * public static native int EVP_VerifyInit(java.lang.String)
- */
-static jlong NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass, jstring algorithm) {
- JNI_TRACE("NativeCrypto_EVP_VerifyInit(%p)", algorithm);
-
- if (algorithm == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
- }
-
- Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create());
- if (ctx.get() == NULL) {
- jniThrowOutOfMemory(env, "Unable to allocate EVP_MD_CTX");
- return 0;
- }
- JNI_TRACE("NativeCrypto_EVP_VerifyInit ctx=%p", ctx.get());
-
- ScopedUtfChars algorithmChars(env, algorithm);
- if (algorithmChars.c_str() == NULL) {
- return 0;
- }
- JNI_TRACE("NativeCrypto_EVP_VerifyInit algorithmChars=%s", algorithmChars.c_str());
-
- const EVP_MD* digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str()));
- if (digest == NULL) {
- jniThrowRuntimeException(env, "Hash algorithm not found");
- return 0;
- }
-
- int ok = EVP_VerifyInit(ctx.get(), digest);
- if (ok == 0) {
- bool exception = throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyInit");
- if (exception) {
- return 0;
- }
- }
- return reinterpret_cast<uintptr_t>(ctx.release());
-}
-
-/*
* public static native void EVP_VerifyUpdate(long, byte[], int, int)
*/
-static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass, jlong ctxRef,
+static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass, jobject ctxRef,
jbyteArray buffer, jint offset, jint length) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
+ EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
JNI_TRACE("NativeCrypto_EVP_VerifyUpdate(%p, %p, %d, %d)", ctx, buffer, offset, length);
- if (ctx == NULL || buffer == NULL) {
+ if (ctx == NULL) {
+ return;
+ } else if (buffer == NULL) {
jniThrowNullPointerException(env, NULL);
return;
}
+ if (offset < 0 || length < 0) {
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
+ return;
+ }
+
ScopedByteArrayRO bufferBytes(env, buffer);
if (bufferBytes.get() == NULL) {
return;
}
+ if (bufferBytes.size() < static_cast<size_t>(offset + length)) {
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
+ return;
+ }
+
int ok = EVP_VerifyUpdate(ctx,
reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset),
length);
@@ -3395,14 +4240,16 @@
/*
* public static native int EVP_VerifyFinal(long, byte[], int, int, long)
*/
-static jint NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, jlong ctxRef, jbyteArray buffer,
+static jint NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, jobject ctxRef, jbyteArray buffer,
jint offset, jint length, jlong pkeyRef) {
- EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
+ EVP_MD_CTX* ctx = fromContextObject<EVP_MD_CTX>(env, ctxRef);
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p)",
ctx, buffer, offset, length, pkey);
- if (ctx == NULL || buffer == NULL || pkey == NULL) {
+ if (ctx == NULL) {
+ return -1;
+ } else if (buffer == NULL || pkey == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
@@ -3522,7 +4369,7 @@
}
const size_t inSize = inBytes.size();
if (size_t(inOffset + inLength) > inSize) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException",
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException",
"in.length < (inSize + inOffset)");
return 0;
}
@@ -3533,7 +4380,7 @@
}
const size_t outSize = outBytes.size();
if (size_t(outOffset + inLength) > outSize) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException",
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException",
"out.length < inSize + outOffset + blockSize - 1");
return 0;
}
@@ -3867,14 +4714,14 @@
}
if (offset < 0 || length < 0) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "offset < 0 || length < 0");
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", "offset < 0 || length < 0");
JNI_TRACE("BIO_write(%p, %p, %d, %d) => IOOB", bio, inputJavaBytes, offset, length);
return;
}
int inputSize = env->GetArrayLength(inputJavaBytes);
if (inputSize < offset + length) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException",
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException",
"input.length < offset + length");
JNI_TRACE("BIO_write(%p, %p, %d, %d) => IOOB", bio, inputJavaBytes, offset, length);
return;
@@ -3897,16 +4744,16 @@
JNI_TRACE("BIO_write(%p, %p, %d, %d) => success", bio, inputJavaBytes, offset, length);
}
-static void NativeCrypto_BIO_free(JNIEnv* env, jclass, jlong bioRef) {
+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(%p)", bio);
+ JNI_TRACE("BIO_free_all(%p)", bio);
if (bio == NULL) {
jniThrowNullPointerException(env, "bio == null");
return;
}
- BIO_free(bio);
+ BIO_free_all(bio);
}
static jstring X509_NAME_to_jstring(JNIEnv* env, X509_NAME* name, unsigned long flags) {
@@ -5150,6 +5997,7 @@
return 0;
}
+ JNI_TRACE("X509_get_pubkey(%p) => %p", x509, pkey.get());
return reinterpret_cast<uintptr_t>(pkey.release());
}
@@ -5674,9 +6522,13 @@
*/
bool setCallbackState(JNIEnv* e, jobject shc, jobject fd, jbyteArray npnProtocols,
jbyteArray alpnProtocols) {
- NetFd netFd(e, fd);
- if (netFd.isClosed()) {
- return false;
+ UniquePtr<NetFd> netFd;
+ if (fd != NULL) {
+ netFd.reset(new NetFd(e, fd));
+ if (netFd->isClosed()) {
+ JNI_TRACE("appData=%p setCallbackState => netFd->isClosed() == true", this);
+ return false;
+ }
}
env = e;
sslHandshakeCallbacks = shc;
@@ -5684,6 +6536,7 @@
npnProtocolsData = e->GetByteArrayElements(npnProtocols, NULL);
if (npnProtocolsData == NULL) {
clearCallbackState();
+ JNI_TRACE("appData=%p setCallbackState => npnProtocolsData == NULL", this);
return false;
}
npnProtocolsArray = npnProtocols;
@@ -5693,6 +6546,7 @@
alpnProtocolsData = e->GetByteArrayElements(alpnProtocols, NULL);
if (alpnProtocolsData == NULL) {
clearCallbackState();
+ JNI_TRACE("appData=%p setCallbackState => alpnProtocolsData == NULL", this);
return false;
}
alpnProtocolsArray = alpnProtocols;
@@ -5784,7 +6638,11 @@
ptv = NULL;
}
- AsynchronousSocketCloseMonitor monitor(intFd);
+#ifndef CONSCRYPT_UNBUNDLED
+ AsynchronousCloseMonitor monitor(intFd);
+#else
+ CompatibilityCloseMonitor monitor(intFd);
+#endif
result = select(maxFd + 1, &rfds, &wfds, NULL, ptv);
JNI_TRACE("sslSelect %s fd=%d appData=%p timeout_millis=%d => %d",
(type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE",
@@ -5874,7 +6732,7 @@
jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
jmethodID methodID
- = env->GetMethodID(cls, "verifyCertificateChain", "([JLjava/lang/String;)V");
+ = env->GetMethodID(cls, "verifyCertificateChain", "(J[JLjava/lang/String;)V");
jlongArray refArray = getCertificateRefs(env, x509_store_ctx->untrusted);
@@ -5882,7 +6740,9 @@
JNI_TRACE("ssl=%p cert_verify_callback calling verifyCertificateChain authMethod=%s",
ssl, authMethod);
jstring authMethodString = env->NewStringUTF(authMethod);
- env->CallVoidMethod(sslHandshakeCallbacks, methodID, refArray, authMethodString);
+ env->CallVoidMethod(sslHandshakeCallbacks, methodID,
+ static_cast<jlong>(reinterpret_cast<uintptr_t>(SSL_get1_session(ssl))), refArray,
+ authMethodString);
int result = (env->ExceptionCheck()) ? 0 : 1;
JNI_TRACE("ssl=%p cert_verify_callback => %d", ssl, result);
@@ -5894,12 +6754,12 @@
* for SSL_MODE_HANDSHAKE_CUTTHROUGH support, since SSL_do_handshake
* returns before the handshake is completed in this case.
*/
-static void info_callback(const SSL* ssl, int where, int ret __attribute__ ((unused))) {
+static void info_callback(const SSL* ssl, int where, int ret) {
JNI_TRACE("ssl=%p info_callback where=0x%x ret=%d", ssl, where, ret);
#ifdef WITH_JNI_TRACE
info_callback_LOG(ssl, where, ret);
#endif
- if (!(where & SSL_CB_HANDSHAKE_DONE)) {
+ if (!(where & SSL_CB_HANDSHAKE_DONE) && !(where & SSL_CB_HANDSHAKE_START)) {
JNI_TRACE("ssl=%p info_callback ignored", ssl);
return;
}
@@ -5919,10 +6779,10 @@
jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
- jmethodID methodID = env->GetMethodID(cls, "handshakeCompleted", "()V");
+ jmethodID methodID = env->GetMethodID(cls, "onSSLStateChange", "(JII)V");
- JNI_TRACE("ssl=%p info_callback calling handshakeCompleted", ssl);
- env->CallVoidMethod(sslHandshakeCallbacks, methodID);
+ JNI_TRACE("ssl=%p info_callback calling onSSLStateChange", ssl);
+ env->CallVoidMethod(sslHandshakeCallbacks, methodID, reinterpret_cast<jlong>(ssl), where, ret);
if (env->ExceptionCheck()) {
JNI_TRACE("ssl=%p info_callback exception", ssl);
@@ -6019,6 +6879,137 @@
return result;
}
+/**
+ * Pre-Shared Key (PSK) client callback.
+ */
+static unsigned int psk_client_callback(SSL* ssl, const char *hint,
+ char *identity, unsigned int max_identity_len,
+ unsigned char *psk, unsigned int max_psk_len) {
+ JNI_TRACE("ssl=%p psk_client_callback", ssl);
+
+ AppData* appData = toAppData(ssl);
+ JNIEnv* env = appData->env;
+ if (env == NULL) {
+ ALOGE("AppData->env missing in psk_client_callback");
+ JNI_TRACE("ssl=%p psk_client_callback env error", ssl);
+ return 0;
+ }
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p psk_client_callback already pending exception", ssl);
+ return 0;
+ }
+
+ jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
+ jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
+ jmethodID methodID =
+ env->GetMethodID(cls, "clientPSKKeyRequested", "(Ljava/lang/String;[B[B)I");
+ JNI_TRACE("ssl=%p psk_client_callback calling clientPSKKeyRequested", ssl);
+ ScopedLocalRef<jstring> identityHintJava(
+ env,
+ (hint != NULL) ? env->NewStringUTF(hint) : NULL);
+ ScopedLocalRef<jbyteArray> identityJava(env, env->NewByteArray(max_identity_len));
+ if (identityJava.get() == NULL) {
+ JNI_TRACE("ssl=%p psk_client_callback failed to allocate identity bufffer", ssl);
+ return 0;
+ }
+ ScopedLocalRef<jbyteArray> keyJava(env, env->NewByteArray(max_psk_len));
+ if (keyJava.get() == NULL) {
+ JNI_TRACE("ssl=%p psk_client_callback failed to allocate key bufffer", ssl);
+ return 0;
+ }
+ jint keyLen = env->CallIntMethod(sslHandshakeCallbacks, methodID,
+ identityHintJava.get(), identityJava.get(), keyJava.get());
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p psk_client_callback exception", ssl);
+ return 0;
+ }
+ if (keyLen <= 0) {
+ JNI_TRACE("ssl=%p psk_client_callback failed to get key", ssl);
+ return 0;
+ } else if ((unsigned int) keyLen > max_psk_len) {
+ JNI_TRACE("ssl=%p psk_client_callback got key which is too long", ssl);
+ return 0;
+ }
+ ScopedByteArrayRO keyJavaRo(env, keyJava.get());
+ if (keyJavaRo.get() == NULL) {
+ JNI_TRACE("ssl=%p psk_client_callback failed to get key bytes", ssl);
+ return 0;
+ }
+ memcpy(psk, keyJavaRo.get(), keyLen);
+
+ ScopedByteArrayRO identityJavaRo(env, identityJava.get());
+ if (identityJavaRo.get() == NULL) {
+ JNI_TRACE("ssl=%p psk_client_callback failed to get identity bytes", ssl);
+ return 0;
+ }
+ memcpy(identity, identityJavaRo.get(), max_identity_len);
+
+ JNI_TRACE("ssl=%p psk_client_callback completed", ssl);
+ return keyLen;
+}
+
+/**
+ * Pre-Shared Key (PSK) server callback.
+ */
+static unsigned int psk_server_callback(SSL* ssl, const char *identity,
+ unsigned char *psk, unsigned int max_psk_len) {
+ JNI_TRACE("ssl=%p psk_server_callback", ssl);
+
+ AppData* appData = toAppData(ssl);
+ JNIEnv* env = appData->env;
+ if (env == NULL) {
+ ALOGE("AppData->env missing in psk_server_callback");
+ JNI_TRACE("ssl=%p psk_server_callback env error", ssl);
+ return 0;
+ }
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p psk_server_callback already pending exception", ssl);
+ return 0;
+ }
+
+ jobject sslHandshakeCallbacks = appData->sslHandshakeCallbacks;
+ jclass cls = env->GetObjectClass(sslHandshakeCallbacks);
+ jmethodID methodID = env->GetMethodID(
+ cls, "serverPSKKeyRequested", "(Ljava/lang/String;Ljava/lang/String;[B)I");
+ JNI_TRACE("ssl=%p psk_server_callback calling serverPSKKeyRequested", ssl);
+ const char* identityHint = SSL_get_psk_identity_hint(ssl);
+ // identityHint = NULL;
+ // identity = NULL;
+ ScopedLocalRef<jstring> identityHintJava(
+ env,
+ (identityHint != NULL) ? env->NewStringUTF(identityHint) : NULL);
+ ScopedLocalRef<jstring> identityJava(
+ env,
+ (identity != NULL) ? env->NewStringUTF(identity) : NULL);
+ ScopedLocalRef<jbyteArray> keyJava(env, env->NewByteArray(max_psk_len));
+ if (keyJava.get() == NULL) {
+ JNI_TRACE("ssl=%p psk_server_callback failed to allocate key bufffer", ssl);
+ return 0;
+ }
+ jint keyLen = env->CallIntMethod(sslHandshakeCallbacks, methodID,
+ identityHintJava.get(), identityJava.get(), keyJava.get());
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p psk_server_callback exception", ssl);
+ return 0;
+ }
+ if (keyLen <= 0) {
+ JNI_TRACE("ssl=%p psk_server_callback failed to get key", ssl);
+ return 0;
+ } else if ((unsigned int) keyLen > max_psk_len) {
+ JNI_TRACE("ssl=%p psk_server_callback got key which is too long", ssl);
+ return 0;
+ }
+ ScopedByteArrayRO keyJavaRo(env, keyJava.get());
+ if (keyJavaRo.get() == NULL) {
+ JNI_TRACE("ssl=%p psk_server_callback failed to get key bytes", ssl);
+ return 0;
+ }
+ memcpy(psk, keyJavaRo.get(), keyLen);
+
+ JNI_TRACE("ssl=%p psk_server_callback completed", ssl);
+ return keyLen;
+}
+
static RSA* rsaGenerateKey(int keylength) {
Unique_BIGNUM bn(BN_new());
if (bn.get() == NULL) {
@@ -6171,6 +7162,9 @@
SSL_CTX_set_tmp_dh_callback(sslCtx.get(), tmp_dh_callback);
SSL_CTX_set_tmp_ecdh_callback(sslCtx.get(), tmp_ecdh_callback);
+ // When TLS Channel ID extension is used, use the new version of it.
+ sslCtx.get()->tlsext_channel_id_enabled_new = 1;
+
JNI_TRACE("NativeCrypto_SSL_CTX_new => %p", sslCtx.get());
return (jlong) sslCtx.release();
}
@@ -6238,17 +7232,28 @@
return 0;
}
- /* Java code in class OpenSSLSocketImpl does the verification. Meaning of
- * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
- * (by default disabled), the server will send a certificate which will
- * be checked. The result of the certificate verification process can be
- * checked after the TLS/SSL handshake using the SSL_get_verify_result(3)
- * function. The handshake will be continued regardless of the
- * verification result.
+ /*
+ * Create our special application data.
*/
- SSL_set_verify(ssl.get(), SSL_VERIFY_NONE, NULL);
+ AppData* appData = AppData::create();
+ if (appData == NULL) {
+ throwSSLExceptionStr(env, "Unable to create application data");
+ freeOpenSslErrorState();
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new appData => 0", ssl_ctx);
+ return 0;
+ }
+ SSL_set_app_data(ssl.get(), reinterpret_cast<char*>(appData));
- JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => ssl=%p", ssl_ctx, ssl.get());
+ /*
+ * Java code in class OpenSSLSocketImpl does the verification. Since
+ * the callbacks do all the verification of the chain, this flag
+ * simply controls whether to send protocol-level alerts or not.
+ * SSL_VERIFY_NONE means don't send alerts and anything else means send
+ * alerts.
+ */
+ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL);
+
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => ssl=%p appData=%p", ssl_ctx, ssl.get(), appData);
return (jlong) ssl.release();
}
@@ -6627,6 +7632,62 @@
return result;
}
+
+static void NativeCrypto_SSL_use_psk_identity_hint(JNIEnv* env, jclass,
+ jlong ssl_address, jstring identityHintJava)
+{
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_use_psk_identity_hint identityHint=%p",
+ ssl, identityHintJava);
+ if (ssl == NULL) {
+ return;
+ }
+
+ int ret;
+ if (identityHintJava == NULL) {
+ ret = SSL_use_psk_identity_hint(ssl, NULL);
+ } else {
+ ScopedUtfChars identityHint(env, identityHintJava);
+ if (identityHint.c_str() == NULL) {
+ throwSSLExceptionStr(env, "Failed to obtain identityHint bytes");
+ return;
+ }
+ ret = SSL_use_psk_identity_hint(ssl, identityHint.c_str());
+ }
+
+ if (ret != 1) {
+ int sslErrorCode = SSL_get_error(ssl, ret);
+ throwSSLExceptionWithSslErrors(env, ssl, sslErrorCode, "Failed to set PSK identity hint");
+ SSL_clear(ssl);
+ }
+}
+
+static void NativeCrypto_set_SSL_psk_client_callback_enabled(JNIEnv* env, jclass,
+ jlong ssl_address, jboolean enabled)
+{
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_set_SSL_psk_client_callback_enabled(%d)",
+ ssl, enabled);
+ if (ssl == NULL) {
+ return;
+ }
+
+ SSL_set_psk_client_callback(ssl, (enabled) ? psk_client_callback : NULL);
+}
+
+static void NativeCrypto_set_SSL_psk_server_callback_enabled(JNIEnv* env, jclass,
+ jlong ssl_address, jboolean enabled)
+{
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_set_SSL_psk_server_callback_enabled(%d)",
+ ssl, enabled);
+ if (ssl == NULL) {
+ return;
+ }
+
+ SSL_set_psk_server_callback(ssl, (enabled) ? psk_server_callback : NULL);
+}
+
static jlongArray NativeCrypto_SSL_get_ciphers(JNIEnv* env, jclass, jlong ssl_address)
{
SSL* ssl = to_SSL(env, ssl_address, true);
@@ -6725,6 +7786,24 @@
}
}
+static void NativeCrypto_SSL_set_accept_state(JNIEnv* env, jclass, jlong sslRef) {
+ SSL* ssl = to_SSL(env, sslRef, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_accept_state", ssl);
+ if (ssl == NULL) {
+ return;
+ }
+ SSL_set_accept_state(ssl);
+}
+
+static void NativeCrypto_SSL_set_connect_state(JNIEnv* env, jclass, jlong sslRef) {
+ SSL* ssl = to_SSL(env, sslRef, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_connect_state", ssl);
+ if (ssl == NULL) {
+ return;
+ }
+ SSL_set_connect_state(ssl);
+}
+
/**
* Sets certificate expectations, especially for server to request client auth
*/
@@ -6828,7 +7907,7 @@
unsigned char **out, unsigned char *outLength,
const unsigned char *primary, const unsigned int primaryLength,
const unsigned char *secondary, const unsigned int secondaryLength) {
- if (primary != NULL) {
+ if (primary != NULL && secondary != NULL) {
JNI_TRACE("primary=%p, length=%d", primary, primaryLength);
int status = SSL_select_next_proto(out, outLength, primary, primaryLength, secondary,
@@ -6951,30 +8030,30 @@
return result;
}
-static int NativeCrypto_SSL_CTX_set_alpn_protos(JNIEnv* env, jclass, jlong ssl_ctx_address,
+static int NativeCrypto_SSL_set_alpn_protos(JNIEnv* env, jclass, jlong ssl_address,
jbyteArray protos) {
- SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
- if (ssl_ctx == NULL) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ if (ssl == NULL) {
return 0;
}
- JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=%p", ssl_ctx, protos);
+ JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=%p", ssl, protos);
if (protos == NULL) {
- JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=NULL", ssl_ctx);
+ JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=NULL", ssl);
return 1;
}
ScopedByteArrayRO protosBytes(env, protos);
if (protosBytes.get() == NULL) {
- JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=%p => protosBytes == NULL", ssl_ctx,
+ JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=%p => protosBytes == NULL", ssl,
protos);
return 0;
}
const unsigned char *tmp = reinterpret_cast<const unsigned char*>(protosBytes.get());
- int ret = SSL_CTX_set_alpn_protos(ssl_ctx, tmp, protosBytes.size());
- JNI_TRACE("ssl_ctx=%p SSL_CTX_set_alpn_protos protos=%p => ret=%d", ssl_ctx, protos, ret);
+ int ret = SSL_set_alpn_protos(ssl, tmp, protosBytes.size());
+ JNI_TRACE("ssl=%p SSL_set_alpn_protos protos=%p => ret=%d", ssl, protos, ret);
return ret;
}
@@ -7062,6 +8141,99 @@
/**
* Perform SSL handshake
*/
+static jlong NativeCrypto_SSL_do_handshake_bio(JNIEnv* env, jclass, jlong ssl_address,
+ jlong rbioRef, jlong wbioRef, jobject shc, jboolean client_mode, jbyteArray npnProtocols,
+ jbyteArray alpnProtocols) {
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ BIO* rbio = reinterpret_cast<BIO*>(rbioRef);
+ BIO* wbio = reinterpret_cast<BIO*>(wbioRef);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio rbio=%p wbio=%p shc=%p client_mode=%d npn=%p",
+ ssl, rbio, wbio, shc, client_mode, npnProtocols);
+ if (ssl == NULL) {
+ return 0;
+ }
+ if (shc == NULL) {
+ jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio sslHandshakeCallbacks == null => 0", ssl);
+ return 0;
+ }
+
+ if (rbio == NULL || wbio == NULL) {
+ jniThrowNullPointerException(env, "rbio == null || wbio == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio => rbio == null || wbio == NULL", ssl);
+ return 0;
+ }
+
+ ScopedSslBio sslBio(ssl, rbio, wbio);
+
+ AppData* appData = toAppData(ssl);
+ if (appData == NULL) {
+ throwSSLExceptionStr(env, "Unable to retrieve application data");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake appData => 0", ssl);
+ return 0;
+ }
+
+ if (!client_mode && alpnProtocols != NULL) {
+ SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(ssl), alpn_select_callback, NULL);
+ }
+
+ int ret = 0;
+ errno = 0;
+
+ if (!appData->setCallbackState(env, shc, NULL, npnProtocols, alpnProtocols)) {
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio setCallbackState => 0", ssl);
+ return 0;
+ }
+ ret = SSL_do_handshake(ssl);
+ appData->clearCallbackState();
+ // cert_verify_callback threw exception
+ if (env->ExceptionCheck()) {
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio exception => 0", ssl);
+ return 0;
+ }
+
+ if (ret <= 0) { // error. See SSL_do_handshake(3SSL) man page.
+ // error case
+ int sslError = SSL_get_error(ssl, ret);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio ret=%d errno=%d sslError=%d",
+ ssl, ret, errno, sslError);
+
+ /*
+ * If SSL_do_handshake doesn't succeed due to the socket being
+ * either unreadable or unwritable, we need to exit to allow
+ * the SSLEngine code to wrap or unwrap.
+ */
+ if (sslError == SSL_ERROR_NONE || (sslError == SSL_ERROR_SYSCALL && errno == 0)) {
+ throwSSLHandshakeExceptionStr(env, "Connection closed by peer");
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ } else if (sslError != SSL_ERROR_WANT_READ && sslError != SSL_ERROR_WANT_WRITE) {
+ throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake terminated",
+ throwSSLHandshakeExceptionStr);
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio error => 0", ssl);
+ return 0;
+ }
+
+ // success. handshake completed
+ SSL_SESSION* ssl_session = SSL_get1_session(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake_bio => ssl_session=%p", ssl, ssl_session);
+#ifdef WITH_JNI_TRACE_KEYS
+ debug_print_session_key(ssl_session);
+#endif
+ return reinterpret_cast<uintptr_t>(ssl_session);
+}
+
+/**
+ * Perform SSL handshake
+ */
static jlong NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject,
jobject shc, jint timeout_millis, jboolean client_mode, jbyteArray npnProtocols,
jbyteArray alpnProtocols) {
@@ -7112,20 +8284,14 @@
return 0;
}
- /*
- * Create our special application data.
- */
- AppData* appData = AppData::create();
+ AppData* appData = toAppData(ssl);
if (appData == NULL) {
- throwSSLExceptionStr(env, "Unable to create application data");
+ throwSSLExceptionStr(env, "Unable to retrieve application data");
SSL_clear(ssl);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake appData => 0", ssl);
return 0;
}
- SSL_set_app_data(ssl, reinterpret_cast<char*>(appData));
- JNI_TRACE("ssl=%p AppData::create => %p", ssl, appData);
-
if (client_mode) {
SSL_set_connect_state(ssl);
} else {
@@ -7185,7 +8351,8 @@
return 0;
}
if (selectResult == -1) {
- throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_SYSCALL, "handshake error");
+ throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_SYSCALL, "handshake error",
+ throwSSLHandshakeExceptionStr);
SSL_clear(ssl);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == -1 => 0", ssl);
return 0;
@@ -7212,9 +8379,10 @@
*/
int sslError = SSL_get_error(ssl, ret);
if (sslError == SSL_ERROR_NONE || (sslError == SSL_ERROR_SYSCALL && errno == 0)) {
- throwSSLExceptionStr(env, "Connection closed by peer");
+ throwSSLHandshakeExceptionStr(env, "Connection closed by peer");
} else {
- throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake terminated");
+ throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake terminated",
+ throwSSLHandshakeExceptionStr);
}
SSL_clear(ssl);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake clean error => 0", ssl);
@@ -7228,7 +8396,8 @@
* at this point.
*/
int sslError = SSL_get_error(ssl, ret);
- throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake aborted");
+ throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake aborted",
+ throwSSLHandshakeExceptionStr);
SSL_clear(ssl);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake unclean error => 0", ssl);
return 0;
@@ -7355,18 +8524,6 @@
return refArray;
}
-/**
- * Helper function which does the actual reading. The Java layer guarantees that
- * at most one thread will enter this function at any given time.
- *
- * @param ssl non-null; the SSL context
- * @param buf non-null; buffer to read into
- * @param len length of the buffer, in bytes
- * @param sslReturnCode original SSL return code
- * @param sslErrorCode filled in with the SSL error code in case of error
- * @return number of bytes read on success, -1 if the connection was
- * cleanly shut down, or THROW_SSLEXCEPTION if an exception should be thrown.
- */
static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* buf, jint len,
int* sslReturnCode, int* sslErrorCode, int read_timeout_millis) {
JNI_TRACE("ssl=%p sslRead buf=%p len=%d", ssl, buf, len);
@@ -7376,9 +8533,11 @@
return 0;
}
- BIO* bio = SSL_get_rbio(ssl);
+ BIO* rbio = SSL_get_rbio(ssl);
+ BIO* wbio = SSL_get_wbio(ssl);
AppData* appData = toAppData(ssl);
+ JNI_TRACE("ssl=%p sslRead appData=%p", ssl, appData);
if (appData == NULL) {
return THROW_SSLEXCEPTION;
}
@@ -7390,7 +8549,15 @@
return -1;
}
- unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
+ if (!SSL_is_init_finished(ssl) && !SSL_cutthrough_complete(ssl) &&
+ !SSL_renegotiate_pending(ssl)) {
+ JNI_TRACE("ssl=%p sslRead => init is not finished (state=0x%x)", ssl,
+ SSL_get_state(ssl));
+ MUTEX_UNLOCK(appData->mutex);
+ return THROW_SSLEXCEPTION;
+ }
+
+ unsigned int bytesMoved = BIO_number_read(rbio) + BIO_number_written(wbio);
if (!appData->setCallbackState(env, shc, fdObject, NULL, NULL)) {
MUTEX_UNLOCK(appData->mutex);
@@ -7423,7 +8590,7 @@
// If we have been successful in moving data around, check whether it
// might make sense to wake up other blocked threads, so they can give
// it a try, too.
- if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved
+ if (BIO_number_read(rbio) + BIO_number_written(wbio) != bytesMoved
&& appData->waitingThreads > 0) {
sslNotify(appData);
}
@@ -7496,6 +8663,133 @@
return -1;
}
+static jint NativeCrypto_SSL_read_BIO(JNIEnv* env, jclass, jlong sslRef, jbyteArray destJava,
+ jint destOffset, jint destLength, jlong sourceBioRef, jlong sinkBioRef, jobject shc) {
+ SSL* ssl = to_SSL(env, sslRef, true);
+ BIO* rbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(sourceBioRef));
+ BIO* wbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(sinkBioRef));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO dest=%p sourceBio=%p sinkBio=%p shc=%p",
+ ssl, destJava, rbio, wbio, shc);
+ if (ssl == NULL) {
+ return 0;
+ }
+ if (rbio == NULL || wbio == NULL) {
+ jniThrowNullPointerException(env, "rbio == null || wbio == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => rbio == null || wbio == null", ssl);
+ return -1;
+ }
+ if (shc == NULL) {
+ jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => sslHandshakeCallbacks == null", ssl);
+ return -1;
+ }
+
+ ScopedByteArrayRW dest(env, destJava);
+ if (dest.get() == NULL) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => threw exception", ssl);
+ return -1;
+ }
+ if (destOffset < 0 || destOffset > ssize_t(dest.size()) || destLength < 0
+ || destLength > (ssize_t) dest.size() - destOffset) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => destOffset=%d, destLength=%d, size=%zd",
+ destOffset, destLength, dest.size());
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
+ return -1;
+ }
+
+ AppData* appData = toAppData(ssl);
+ if (appData == NULL) {
+ throwSSLExceptionStr(env, "Unable to retrieve application data");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => appData == NULL", ssl);
+ return -1;
+ }
+
+ errno = 0;
+
+ if (MUTEX_LOCK(appData->mutex) == -1) {
+ return -1;
+ }
+
+ if (!appData->setCallbackState(env, shc, NULL, NULL, NULL)) {
+ MUTEX_UNLOCK(appData->mutex);
+ throwSSLExceptionStr(env, "Unable to set callback state");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => set callback state failed", ssl);
+ return -1;
+ }
+
+ ScopedSslBio sslBio(ssl, rbio, wbio);
+
+ int result = SSL_read(ssl, dest.get() + destOffset, destLength);
+ appData->clearCallbackState();
+ // callbacks can happen if server requests renegotiation
+ if (env->ExceptionCheck()) {
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => threw exception", ssl);
+ return THROWN_EXCEPTION;
+ }
+ int sslError = SSL_ERROR_NONE;
+ if (result <= 0) {
+ sslError = SSL_get_error(ssl, result);
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO SSL_read result=%d sslError=%d", ssl, result, sslError);
+#ifdef WITH_JNI_TRACE_DATA
+ for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) {
+ int n = result - i;
+ if (n > WITH_JNI_TRACE_DATA_CHUNK_SIZE) {
+ n = WITH_JNI_TRACE_DATA_CHUNK_SIZE;
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO data: %d:\n%.*s", ssl, n, n, buf+i);
+ }
+#endif
+
+ MUTEX_UNLOCK(appData->mutex);
+
+ switch (sslError) {
+ // Successfully read at least one byte.
+ case SSL_ERROR_NONE:
+ break;
+
+ // Read zero bytes. End of stream reached.
+ case SSL_ERROR_ZERO_RETURN:
+ result = -1;
+ break;
+
+ // Need to wait for availability of underlying layer, then retry.
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ result = 0;
+ break;
+
+ // A problem occurred during a system call, but this is not
+ // necessarily an error.
+ case SSL_ERROR_SYSCALL: {
+ // Connection closed without proper shutdown. Tell caller we
+ // have reached end-of-stream.
+ if (result == 0) {
+ result = -1;
+ break;
+ } else if (errno == EINTR) {
+ // System call has been interrupted. Simply retry.
+ result = 0;
+ break;
+ }
+
+ // Note that for all other system call errors we fall through
+ // to the default case, which results in an Exception.
+ }
+
+ // Everything else is basically an error.
+ default: {
+ throwSSLExceptionWithSslErrors(env, ssl, sslError, "Read error");
+ return -1;
+ }
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_read_BIO => %d", ssl, result);
+ return result;
+}
+
/**
* OpenSSL read function (2): read into buffer at offset n chunks.
* Returns 1 (success) or value <= 0 (failure).
@@ -7557,18 +8851,6 @@
return result;
}
-/**
- * Helper function which does the actual writing. The Java layer guarantees that
- * at most one thread will enter this function at any given time.
- *
- * @param ssl non-null; the SSL context
- * @param buf non-null; buffer to write
- * @param len length of the buffer, in bytes
- * @param sslReturnCode original SSL return code
- * @param sslErrorCode filled in with the SSL error code in case of error
- * @return number of bytes read on success, -1 if the connection was
- * cleanly shut down, or THROW_SSLEXCEPTION if an exception should be thrown.
- */
static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const char* buf, jint len,
int* sslReturnCode, int* sslErrorCode, int write_timeout_millis) {
JNI_TRACE("ssl=%p sslWrite buf=%p len=%d write_timeout_millis=%d",
@@ -7579,9 +8861,11 @@
return 0;
}
- BIO* bio = SSL_get_wbio(ssl);
+ BIO* rbio = SSL_get_rbio(ssl);
+ BIO* wbio = SSL_get_wbio(ssl);
AppData* appData = toAppData(ssl);
+ JNI_TRACE("ssl=%p sslWrite appData=%p", ssl, appData);
if (appData == NULL) {
return THROW_SSLEXCEPTION;
}
@@ -7595,7 +8879,15 @@
return -1;
}
- unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
+ if (!SSL_is_init_finished(ssl) && !SSL_cutthrough_complete(ssl) &&
+ !SSL_renegotiate_pending(ssl)) {
+ JNI_TRACE("ssl=%p sslWrite => init is not finished (state=0x%x)", ssl,
+ SSL_get_state(ssl));
+ MUTEX_UNLOCK(appData->mutex);
+ return THROW_SSLEXCEPTION;
+ }
+
+ unsigned int bytesMoved = BIO_number_read(rbio) + BIO_number_written(wbio);
if (!appData->setCallbackState(env, shc, fdObject, NULL, NULL)) {
MUTEX_UNLOCK(appData->mutex);
@@ -7630,7 +8922,7 @@
// If we have been successful in moving data around, check whether it
// might make sense to wake up other blocked threads, so they can give
// it a try, too.
- if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved
+ if (BIO_number_read(rbio) + BIO_number_written(wbio) != bytesMoved
&& appData->waitingThreads > 0) {
sslNotify(appData);
}
@@ -7711,6 +9003,127 @@
/**
* OpenSSL write function (2): write into buffer at offset n chunks.
*/
+static int NativeCrypto_SSL_write_BIO(JNIEnv* env, jclass, jlong sslRef, jbyteArray sourceJava, jint len,
+ jlong sinkBioRef, jobject shc) {
+ SSL* ssl = to_SSL(env, sslRef, true);
+ BIO* wbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(sinkBioRef));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO source=%p len=%d wbio=%p shc=%p",
+ ssl, sourceJava, len, wbio, shc);
+ if (ssl == NULL) {
+ return -1;
+ }
+ if (wbio == NULL) {
+ jniThrowNullPointerException(env, "wbio == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => wbio == null", ssl);
+ return -1;
+ }
+ if (shc == NULL) {
+ jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => sslHandshakeCallbacks == null", ssl);
+ return -1;
+ }
+
+ AppData* appData = toAppData(ssl);
+ if (appData == NULL) {
+ throwSSLExceptionStr(env, "Unable to retrieve application data");
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO appData => NULL", ssl);
+ return -1;
+ }
+
+ errno = 0;
+
+ if (MUTEX_LOCK(appData->mutex) == -1) {
+ return 0;
+ }
+
+ if (!appData->setCallbackState(env, shc, NULL, NULL, NULL)) {
+ MUTEX_UNLOCK(appData->mutex);
+ throwSSLExceptionStr(env, "Unable to set appdata callback");
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => appData can't set callback", ssl);
+ return -1;
+ }
+
+ ScopedByteArrayRO source(env, sourceJava);
+ if (source.get() == NULL) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO => threw exception", ssl);
+ return -1;
+ }
+
+ Unique_BIO nullBio(BIO_new(BIO_s_null()));
+ ScopedSslBio sslBio(ssl, nullBio.get(), wbio);
+
+ int result = SSL_write(ssl, reinterpret_cast<const char*>(source.get()), len);
+ appData->clearCallbackState();
+ // callbacks can happen if server requests renegotiation
+ if (env->ExceptionCheck()) {
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO exception => exception pending (reneg)", ssl);
+ return -1;
+ }
+ int sslError = SSL_ERROR_NONE;
+ if (result <= 0) {
+ sslError = SSL_get_error(ssl, result);
+ freeOpenSslErrorState();
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO SSL_write result=%d sslError=%d left=%d",
+ ssl, result, sslError, ssl->s3->wbuf.left);
+#ifdef WITH_JNI_TRACE_DATA
+ for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) {
+ int n = result - i;
+ if (n > WITH_JNI_TRACE_DATA_CHUNK_SIZE) {
+ n = WITH_JNI_TRACE_DATA_CHUNK_SIZE;
+ }
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_write_BIO data: %d:\n%.*s", ssl, n, n, buf+i);
+ }
+#endif
+
+ MUTEX_UNLOCK(appData->mutex);
+
+ switch (sslError) {
+ case SSL_ERROR_NONE:
+ return result;
+
+ // Wrote zero bytes. End of stream reached.
+ case SSL_ERROR_ZERO_RETURN:
+ return -1;
+
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+
+ case SSL_ERROR_SYSCALL: {
+ // Connection closed without proper shutdown. Tell caller we
+ // have reached end-of-stream.
+ if (result == 0) {
+ return -1;
+ }
+
+ // System call has been interrupted. Simply retry.
+ if (errno == EINTR) {
+ return 0;
+ }
+
+ // Note that for all other system call errors we fall through
+ // to the default case, which results in an Exception.
+ }
+
+ // Everything else is basically an error.
+ default: {
+ throwSSLExceptionWithSslErrors(env, ssl, sslError, "Write error");
+ break;
+ }
+ }
+ return -1;
+}
+
+/**
+ * OpenSSL write function (2): write into buffer at offset n chunks.
+ */
static void NativeCrypto_SSL_write(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject,
jobject shc, jbyteArray b, jint offset, jint len, jint write_timeout_millis)
{
@@ -7863,6 +9276,94 @@
}
/**
+ * OpenSSL close SSL socket function.
+ */
+static void NativeCrypto_SSL_shutdown_BIO(JNIEnv* env, jclass, jlong ssl_address, jlong rbioRef,
+ jlong wbioRef, jobject shc) {
+ SSL* ssl = to_SSL(env, ssl_address, false);
+ BIO* rbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(rbioRef));
+ BIO* wbio = reinterpret_cast<BIO*>(static_cast<uintptr_t>(wbioRef));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown rbio=%p wbio=%p shc=%p", ssl, rbio, wbio, shc);
+ if (ssl == NULL) {
+ return;
+ }
+ if (rbio == NULL || wbio == NULL) {
+ jniThrowNullPointerException(env, "rbio == null || wbio == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => rbio == null || wbio == null", ssl);
+ return;
+ }
+ if (shc == NULL) {
+ jniThrowNullPointerException(env, "sslHandshakeCallbacks == null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => sslHandshakeCallbacks == null", ssl);
+ return;
+ }
+
+ AppData* appData = toAppData(ssl);
+ if (appData != NULL) {
+ if (!appData->setCallbackState(env, shc, NULL, NULL, NULL)) {
+ // SocketException thrown by NetFd.isClosed
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+ return;
+ }
+
+ ScopedSslBio scopedBio(ssl, rbio, wbio);
+
+ int ret = SSL_shutdown(ssl);
+ appData->clearCallbackState();
+ // callbacks can happen if server requests renegotiation
+ if (env->ExceptionCheck()) {
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown => exception", ssl);
+ return;
+ }
+ switch (ret) {
+ case 0:
+ /*
+ * Shutdown was not successful (yet), but there also
+ * is no error. Since we can't know whether the remote
+ * server is actually still there, and we don't want to
+ * get stuck forever in a second SSL_shutdown() call, we
+ * simply return. This is not security a problem as long
+ * as we close the underlying socket, which we actually
+ * do, because that's where we are just coming from.
+ */
+ break;
+ case 1:
+ /*
+ * Shutdown was successful. We can safely return. Hooray!
+ */
+ break;
+ default:
+ /*
+ * Everything else is a real error condition. We should
+ * let the Java layer know about this by throwing an
+ * exception.
+ */
+ int sslError = SSL_get_error(ssl, ret);
+ throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL shutdown failed");
+ break;
+ }
+ }
+
+ SSL_clear(ssl);
+ freeOpenSslErrorState();
+}
+
+static jint NativeCrypto_SSL_get_shutdown(JNIEnv* env, jclass, jlong ssl_address) {
+ const SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_shutdown", ssl);
+ if (ssl == NULL) {
+ jniThrowNullPointerException(env, "ssl == null");
+ return 0;
+ }
+
+ int status = SSL_get_shutdown(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_shutdown => %d", ssl, status);
+ return static_cast<jint>(status);
+}
+
+/**
* public static native void SSL_free(long ssl);
*/
static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jlong ssl_address)
@@ -8027,6 +9528,7 @@
NATIVE_METHOD(NativeCrypto, ENGINE_load_private_key, "(JLjava/lang/String;)J"),
NATIVE_METHOD(NativeCrypto, ENGINE_get_id, "(J)Ljava/lang/String;"),
NATIVE_METHOD(NativeCrypto, ENGINE_ctrl_cmd_string, "(JLjava/lang/String;Ljava/lang/String;I)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_DH, "([B[B[B[B)J"),
NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_DSA, "([B[B[B[B[B)J"),
NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_RSA, "([B[B[B[B[B[B[B[B)J"),
NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_EC_KEY, "(JJ[B)J"),
@@ -8041,6 +9543,9 @@
NATIVE_METHOD(NativeCrypto, d2i_PKCS8_PRIV_KEY_INFO, "([B)J"),
NATIVE_METHOD(NativeCrypto, i2d_PUBKEY, "(J)[B"),
NATIVE_METHOD(NativeCrypto, d2i_PUBKEY, "([B)J"),
+ NATIVE_METHOD(NativeCrypto, getRSAPrivateKeyWrapper, "(Ljava/security/interfaces/RSAPrivateKey;[B)J"),
+ NATIVE_METHOD(NativeCrypto, getDSAPrivateKeyWrapper, "(Ljava/security/interfaces/DSAPrivateKey;)J"),
+ NATIVE_METHOD(NativeCrypto, getECPrivateKeyWrapper, "(Ljava/security/interfaces/ECPrivateKey;J)J"),
NATIVE_METHOD(NativeCrypto, RSA_generate_key_ex, "(I[B)J"),
NATIVE_METHOD(NativeCrypto, RSA_size, "(J)I"),
NATIVE_METHOD(NativeCrypto, RSA_private_encrypt, "(I[B[BJI)I"),
@@ -8051,6 +9556,10 @@
NATIVE_METHOD(NativeCrypto, get_RSA_public_params, "(J)[[B"),
NATIVE_METHOD(NativeCrypto, DSA_generate_key, "(I[B[B[B[B)J"),
NATIVE_METHOD(NativeCrypto, get_DSA_params, "(J)[[B"),
+ NATIVE_METHOD(NativeCrypto, set_DSA_flag_nonce_from_hash, "(J)V"),
+ NATIVE_METHOD(NativeCrypto, DH_generate_parameters_ex, "(IJ)J"),
+ NATIVE_METHOD(NativeCrypto, DH_generate_key, "(J)V"),
+ NATIVE_METHOD(NativeCrypto, get_DH_params, "(J)[[B"),
NATIVE_METHOD(NativeCrypto, EC_GROUP_new_by_curve_name, "(Ljava/lang/String;)J"),
NATIVE_METHOD(NativeCrypto, EC_GROUP_new_curve, "(I[B[B[B)J"),
NATIVE_METHOD(NativeCrypto, EC_GROUP_dup, "(J)J"),
@@ -8075,26 +9584,27 @@
NATIVE_METHOD(NativeCrypto, EC_KEY_get0_group, "(J)J"),
NATIVE_METHOD(NativeCrypto, EC_KEY_get_private_key, "(J)[B"),
NATIVE_METHOD(NativeCrypto, EC_KEY_get_public_key, "(J)J"),
+ NATIVE_METHOD(NativeCrypto, EC_KEY_set_nonce_from_hash, "(JZ)V"),
NATIVE_METHOD(NativeCrypto, ECDH_compute_key, "([BIJJ)I"),
NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_create, "()J"),
- NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_init, "(J)V"),
+ NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_init, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;)V"),
NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_destroy, "(J)V"),
- NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_copy, "(J)J"),
- NATIVE_METHOD(NativeCrypto, EVP_DigestFinal, "(J[BI)I"),
- NATIVE_METHOD(NativeCrypto, EVP_DigestInit, "(J)J"),
+ NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_copy, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_DigestInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;J)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_DigestUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[BII)V"),
+ NATIVE_METHOD(NativeCrypto, EVP_DigestFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[BI)I"),
NATIVE_METHOD(NativeCrypto, EVP_get_digestbyname, "(Ljava/lang/String;)J"),
NATIVE_METHOD(NativeCrypto, EVP_MD_block_size, "(J)I"),
NATIVE_METHOD(NativeCrypto, EVP_MD_size, "(J)I"),
- NATIVE_METHOD(NativeCrypto, EVP_DigestUpdate, "(J[BII)V"),
- NATIVE_METHOD(NativeCrypto, EVP_SignInit, "(Ljava/lang/String;)J"),
- NATIVE_METHOD(NativeCrypto, EVP_SignUpdate, "(J[BII)V"),
- NATIVE_METHOD(NativeCrypto, EVP_SignFinal, "(J[BIJ)I"),
- NATIVE_METHOD(NativeCrypto, EVP_VerifyInit, "(Ljava/lang/String;)J"),
- NATIVE_METHOD(NativeCrypto, EVP_VerifyUpdate, "(J[BII)V"),
- NATIVE_METHOD(NativeCrypto, EVP_VerifyFinal, "(J[BIIJ)I"),
- NATIVE_METHOD(NativeCrypto, EVP_DigestSignInit, "(JJJ)V"),
- NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdate, "(J[B)V"),
- NATIVE_METHOD(NativeCrypto, EVP_DigestSignFinal, "(J)[B"),
+ NATIVE_METHOD(NativeCrypto, EVP_SignInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;J)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_SignUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[BII)V"),
+ NATIVE_METHOD(NativeCrypto, EVP_SignFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[BIJ)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_VerifyInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;J)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_VerifyUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[BII)V"),
+ NATIVE_METHOD(NativeCrypto, EVP_VerifyFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[BIIJ)I"),
+ NATIVE_METHOD(NativeCrypto, EVP_DigestSignInit, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;JJ)V"),
+ NATIVE_METHOD(NativeCrypto, EVP_DigestSignUpdate, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;[B)V"),
+ NATIVE_METHOD(NativeCrypto, EVP_DigestSignFinal, "(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLDigestContext;)[B"),
NATIVE_METHOD(NativeCrypto, EVP_get_cipherbyname, "(Ljava/lang/String;)J"),
NATIVE_METHOD(NativeCrypto, EVP_CipherInit_ex, "(JJ[B[BZ)V"),
NATIVE_METHOD(NativeCrypto, EVP_CipherUpdate, "(J[BI[BII)I"),
@@ -8116,7 +9626,7 @@
NATIVE_METHOD(NativeCrypto, create_BIO_OutputStream, "(Ljava/io/OutputStream;)J"),
NATIVE_METHOD(NativeCrypto, BIO_read, "(J[B)I"),
NATIVE_METHOD(NativeCrypto, BIO_write, "(J[BII)V"),
- NATIVE_METHOD(NativeCrypto, BIO_free, "(J)V"),
+ NATIVE_METHOD(NativeCrypto, BIO_free_all, "(J)V"),
NATIVE_METHOD(NativeCrypto, X509_NAME_print_ex, "(JJ)Ljava/lang/String;"),
NATIVE_METHOD(NativeCrypto, d2i_X509_bio, "(J)J"),
NATIVE_METHOD(NativeCrypto, d2i_X509, "([B)J"),
@@ -8202,23 +9712,33 @@
NATIVE_METHOD(NativeCrypto, SSL_get_options, "(J)J"),
NATIVE_METHOD(NativeCrypto, SSL_set_options, "(JJ)J"),
NATIVE_METHOD(NativeCrypto, SSL_clear_options, "(JJ)J"),
+ NATIVE_METHOD(NativeCrypto, SSL_use_psk_identity_hint, "(JLjava/lang/String;)V"),
+ NATIVE_METHOD(NativeCrypto, set_SSL_psk_client_callback_enabled, "(JZ)V"),
+ NATIVE_METHOD(NativeCrypto, set_SSL_psk_server_callback_enabled, "(JZ)V"),
NATIVE_METHOD(NativeCrypto, SSL_set_cipher_lists, "(J[Ljava/lang/String;)V"),
NATIVE_METHOD(NativeCrypto, SSL_get_ciphers, "(J)[J"),
NATIVE_METHOD(NativeCrypto, get_SSL_CIPHER_algorithm_auth, "(J)I"),
NATIVE_METHOD(NativeCrypto, get_SSL_CIPHER_algorithm_mkey, "(J)I"),
+ NATIVE_METHOD(NativeCrypto, SSL_set_accept_state, "(J)V"),
+ NATIVE_METHOD(NativeCrypto, SSL_set_connect_state, "(J)V"),
NATIVE_METHOD(NativeCrypto, SSL_set_verify, "(JI)V"),
NATIVE_METHOD(NativeCrypto, SSL_set_session, "(JJ)V"),
NATIVE_METHOD(NativeCrypto, SSL_set_session_creation_enabled, "(JZ)V"),
NATIVE_METHOD(NativeCrypto, SSL_set_tlsext_host_name, "(JLjava/lang/String;)V"),
NATIVE_METHOD(NativeCrypto, SSL_get_servername, "(J)Ljava/lang/String;"),
NATIVE_METHOD(NativeCrypto, SSL_do_handshake, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "IZ[B[B)J"),
+ NATIVE_METHOD(NativeCrypto, SSL_do_handshake_bio, "(JJJ" SSL_CALLBACKS "Z[B[B)J"),
NATIVE_METHOD(NativeCrypto, SSL_renegotiate, "(J)V"),
NATIVE_METHOD(NativeCrypto, SSL_get_certificate, "(J)[J"),
NATIVE_METHOD(NativeCrypto, SSL_get_peer_cert_chain, "(J)[J"),
NATIVE_METHOD(NativeCrypto, SSL_read, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)I"),
+ NATIVE_METHOD(NativeCrypto, SSL_read_BIO, "(J[BIIJJ" SSL_CALLBACKS ")I"),
NATIVE_METHOD(NativeCrypto, SSL_write, "(J" FILE_DESCRIPTOR SSL_CALLBACKS "[BIII)V"),
+ NATIVE_METHOD(NativeCrypto, SSL_write_BIO, "(J[BIJ" SSL_CALLBACKS ")I"),
NATIVE_METHOD(NativeCrypto, SSL_interrupt, "(J)V"),
NATIVE_METHOD(NativeCrypto, SSL_shutdown, "(J" FILE_DESCRIPTOR SSL_CALLBACKS ")V"),
+ NATIVE_METHOD(NativeCrypto, SSL_shutdown_BIO, "(JJJ" SSL_CALLBACKS ")V"),
+ NATIVE_METHOD(NativeCrypto, SSL_get_shutdown, "(J)I"),
NATIVE_METHOD(NativeCrypto, SSL_free, "(J)V"),
NATIVE_METHOD(NativeCrypto, SSL_SESSION_session_id, "(J)[B"),
NATIVE_METHOD(NativeCrypto, SSL_SESSION_get_time, "(J)J"),
@@ -8230,31 +9750,73 @@
NATIVE_METHOD(NativeCrypto, SSL_CTX_enable_npn, "(J)V"),
NATIVE_METHOD(NativeCrypto, SSL_CTX_disable_npn, "(J)V"),
NATIVE_METHOD(NativeCrypto, SSL_get_npn_negotiated_protocol, "(J)[B"),
- NATIVE_METHOD(NativeCrypto, SSL_CTX_set_alpn_protos, "(J[B)I"),
+ NATIVE_METHOD(NativeCrypto, SSL_set_alpn_protos, "(J[B)I"),
NATIVE_METHOD(NativeCrypto, SSL_get0_alpn_selected, "(J)[B"),
NATIVE_METHOD(NativeCrypto, ERR_peek_last_error, "()J"),
};
+static jclass getGlobalRefToClass(JNIEnv* env, const char* className) {
+ ScopedLocalRef<jclass> localClass(env, env->FindClass(className));
+ jclass globalRef = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
+ if (globalRef == NULL) {
+ ALOGE("failed to find class %s", className);
+ abort();
+ }
+ return globalRef;
+}
+
+static jmethodID getStaticMethodRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+ jmethodID localMethod = env->GetStaticMethodID(clazz, name, sig);
+ if (localMethod == NULL) {
+ ALOGE("could not find static method %s", name);
+ abort();
+ }
+ return localMethod;
+}
+
+static jmethodID getMethodRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+ jmethodID localMethod = env->GetMethodID(clazz, name, sig);
+ if (localMethod == NULL) {
+ ALOGE("could not find method %s", name);
+ abort();
+ }
+ return localMethod;
+}
+
+static jfieldID getFieldRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+ jfieldID localField = env->GetFieldID(clazz, name, sig);
+ if (localField == NULL) {
+ ALOGE("could not find field %s", name);
+ abort();
+ }
+ return localField;
+}
+
static void initialize_conscrypt(JNIEnv* env) {
jniRegisterNativeMethods(env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeCrypto",
sNativeCryptoMethods, NELEM(sNativeCryptoMethods));
- ScopedLocalRef<jclass> localClass(env,
- env->FindClass(TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream"));
- openSslOutputStreamClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
- if (openSslOutputStreamClass == NULL) {
- ALOGE("failed to find class OpenSSLBIOInputStream");
- abort();
- }
+ cryptoUpcallsClass = getGlobalRefToClass(env,
+ TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/CryptoUpcalls");
+ openSslNativeReferenceClass = getGlobalRefToClass(env,
+ TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLNativeReference");
+ openSslInputStreamClass = getGlobalRefToClass(env,
+ TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream");
- calendar_setMethod = env->GetMethodID(calendarClass, "set", "(IIIIII)V");
- inputStream_readMethod = env->GetMethodID(inputStreamClass, "read", "([B)I");
+ openSslNativeReference_context = getFieldRef(env, openSslNativeReferenceClass, "context", "J");
+
+ calendar_setMethod = getMethodRef(env, calendarClass, "set", "(IIIIII)V");
+ inputStream_readMethod = getMethodRef(env, inputStreamClass, "read", "([B)I");
integer_valueOfMethod = env->GetStaticMethodID(integerClass, "valueOf",
"(I)Ljava/lang/Integer;");
- openSslInputStream_readLineMethod = env->GetMethodID(openSslOutputStreamClass, "gets",
+ openSslInputStream_readLineMethod = getMethodRef(env, openSslInputStreamClass, "gets",
"([B)I");
- outputStream_writeMethod = env->GetMethodID(outputStreamClass, "write", "([B)V");
- outputStream_flushMethod = env->GetMethodID(outputStreamClass, "flush", "()V");
+ outputStream_writeMethod = getMethodRef(env, outputStreamClass, "write", "([B)V");
+ outputStream_flushMethod = getMethodRef(env, outputStreamClass, "flush", "()V");
+
+#ifdef CONSCRYPT_UNBUNDLED
+ findAsynchronousCloseMonitorFuncs();
+#endif
}
static jclass findClass(JNIEnv* env, const char* name) {
@@ -8267,9 +9829,14 @@
return result;
}
+#ifdef STATIC_LIB
+// Give client libs everything they need to initialize our JNI
+int libconscrypt_JNI_OnLoad(JavaVM *vm, void*) {
+#else
// Use JNI_OnLoad for when we're standalone
int JNI_OnLoad(JavaVM *vm, void*) {
JNI_TRACE("JNI_OnLoad NativeCrypto");
+#endif
gJavaVM = vm;
JNIEnv *env;
diff --git a/src/main/java/org/conscrypt/CertPinManager.java b/src/platform/java/org/conscrypt/CertPinManager.java
similarity index 100%
rename from src/main/java/org/conscrypt/CertPinManager.java
rename to src/platform/java/org/conscrypt/CertPinManager.java
diff --git a/src/main/java/org/conscrypt/JSSEProvider.java b/src/platform/java/org/conscrypt/JSSEProvider.java
similarity index 100%
rename from src/main/java/org/conscrypt/JSSEProvider.java
rename to src/platform/java/org/conscrypt/JSSEProvider.java
diff --git a/src/main/java/org/conscrypt/PinEntryException.java b/src/platform/java/org/conscrypt/PinEntryException.java
similarity index 100%
rename from src/main/java/org/conscrypt/PinEntryException.java
rename to src/platform/java/org/conscrypt/PinEntryException.java
diff --git a/src/main/java/org/conscrypt/PinFailureLogger.java b/src/platform/java/org/conscrypt/PinFailureLogger.java
similarity index 100%
rename from src/main/java/org/conscrypt/PinFailureLogger.java
rename to src/platform/java/org/conscrypt/PinFailureLogger.java
diff --git a/src/main/java/org/conscrypt/PinListEntry.java b/src/platform/java/org/conscrypt/PinListEntry.java
similarity index 85%
rename from src/main/java/org/conscrypt/PinListEntry.java
rename to src/platform/java/org/conscrypt/PinListEntry.java
index 0b24dc7..d908dcf 100644
--- a/src/main/java/org/conscrypt/PinListEntry.java
+++ b/src/platform/java/org/conscrypt/PinListEntry.java
@@ -89,18 +89,22 @@
*
* <p>If enforcing is on and the given {@code chain} does not include the
* expected pinned certificate, this will return {@code false} indicating
- * the chain is not valid. Otherwise this will return {@code true}
- * indicating the {@code chain} is valid.
+ * the chain is not valid unless the {@code chain} chains up to an user-installed
+ * CA cert. Otherwise this will return {@code true} indicating the {@code chain}
+ * is valid.
*/
public boolean isChainValid(List<X509Certificate> chain) {
- for (X509Certificate cert : chain) {
- String fingerprint = getFingerprint(cert);
- if (pinnedFingerprints.contains(fingerprint)) {
- return true;
+ boolean containsUserCert = chainContainsUserCert(chain);
+ if (!containsUserCert) {
+ for (X509Certificate cert : chain) {
+ String fingerprint = getFingerprint(cert);
+ if (pinnedFingerprints.contains(fingerprint)) {
+ return true;
+ }
}
}
- logPinFailure(chain);
- return !enforcing;
+ logPinFailure(chain, containsUserCert);
+ return !enforcing || containsUserCert;
}
private static String getFingerprint(X509Certificate cert) {
@@ -146,8 +150,8 @@
return false;
}
- private void logPinFailure(List<X509Certificate> chain) {
- PinFailureLogger.log(cn, chainContainsUserCert(chain), enforcing, chain);
+ private void logPinFailure(List<X509Certificate> chain, boolean containsUserCert) {
+ PinFailureLogger.log(cn, containsUserCert, enforcing, chain);
}
}
diff --git a/src/main/java/org/conscrypt/PinManagerException.java b/src/platform/java/org/conscrypt/PinManagerException.java
similarity index 100%
rename from src/main/java/org/conscrypt/PinManagerException.java
rename to src/platform/java/org/conscrypt/PinManagerException.java
diff --git a/src/platform/java/org/conscrypt/Platform.java b/src/platform/java/org/conscrypt/Platform.java
new file mode 100644
index 0000000..668914b
--- /dev/null
+++ b/src/platform/java/org/conscrypt/Platform.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 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 static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_SNDTIMEO;
+
+import org.apache.harmony.security.utils.AlgNameMapper;
+import org.apache.harmony.security.utils.AlgNameMapperSource;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+import java.io.FileDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketImpl;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.ECParameterSpec;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.X509TrustManager;
+
+class 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.
+ */
+ public static void setup() {
+ NoPreloadHolder.MAPPER.ping();
+ }
+
+ /**
+ * Just a placeholder to make sure the class is initialized.
+ */
+ private void ping() {
+ }
+
+ private Platform() {
+ AlgNameMapper.setSource(new OpenSSLMapper());
+ }
+
+ private static class OpenSSLMapper implements AlgNameMapperSource {
+ @Override
+ public String mapNameToOid(String algName) {
+ return NativeCrypto.OBJ_txt2nid_oid(algName);
+ }
+
+ @Override
+ public String mapOidToName(String oid) {
+ return NativeCrypto.OBJ_txt2nid_longName(oid);
+ }
+ }
+
+ public static FileDescriptor getFileDescriptor(Socket s) {
+ return s.getFileDescriptor$();
+ }
+
+ public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
+ try {
+ Field f_impl = Socket.class.getDeclaredField("impl");
+ f_impl.setAccessible(true);
+ Object socketImpl = f_impl.get(openSSLSocketImpl);
+ Field f_fd = SocketImpl.class.getDeclaredField("fd");
+ f_fd.setAccessible(true);
+ return (FileDescriptor) f_fd.get(socketImpl);
+ } catch (Exception e) {
+ throw new RuntimeException("Can't get FileDescriptor from socket", e);
+ }
+ }
+
+ public static String getCurveName(ECParameterSpec spec) {
+ return spec.getCurveName();
+ }
+
+ public static void setCurveName(ECParameterSpec spec, String curveName) {
+ spec.setCurveName(curveName);
+ }
+
+ public static void setSocketTimeout(Socket s, long timeoutMillis) throws SocketException {
+ StructTimeval tv = StructTimeval.fromMillis(timeoutMillis);
+ try {
+ Os.setsockoptTimeval(s.getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ public static void checkServerTrusted(X509TrustManager x509tm, X509Certificate[] chain,
+ String authType, String host) throws CertificateException {
+ if (x509tm instanceof TrustManagerImpl) {
+ TrustManagerImpl tm = (TrustManagerImpl) x509tm;
+ tm.checkServerTrusted(chain, authType, host);
+ } else {
+ x509tm.checkServerTrusted(chain, authType);
+ }
+ }
+
+ /**
+ * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
+ * builds since we didn't backport, so return null.
+ */
+ public static OpenSSLKey wrapRsaKey(PrivateKey key) {
+ return null;
+ }
+
+ /**
+ * Logs to the system EventLog system.
+ */
+ public static void logEvent(String message) {
+ try {
+ Class processClass = Class.forName("android.os.Process");
+ Object processInstance = processClass.newInstance();
+ Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
+ int uid = (Integer) myUidMethod.invoke(processInstance);
+
+ Class eventLogClass = Class.forName("android.util.EventLog");
+ Object eventLogInstance = eventLogClass.newInstance();
+ Method writeEventMethod = eventLogClass.getMethod("writeEvent",
+ new Class[] { Integer.TYPE, Object[].class });
+ writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
+ new Object[] { "conscrypt", uid, message });
+ } catch (Exception e) {
+ // Do not log and fail silently
+ }
+ }
+}
diff --git a/src/main/java/org/conscrypt/TrustManagerFactoryImpl.java b/src/platform/java/org/conscrypt/TrustManagerFactoryImpl.java
similarity index 100%
rename from src/main/java/org/conscrypt/TrustManagerFactoryImpl.java
rename to src/platform/java/org/conscrypt/TrustManagerFactoryImpl.java
diff --git a/src/main/java/org/conscrypt/TrustManagerImpl.java b/src/platform/java/org/conscrypt/TrustManagerImpl.java
similarity index 92%
rename from src/main/java/org/conscrypt/TrustManagerImpl.java
rename to src/platform/java/org/conscrypt/TrustManagerImpl.java
index 2537d1a..2af84d6 100644
--- a/src/main/java/org/conscrypt/TrustManagerImpl.java
+++ b/src/platform/java/org/conscrypt/TrustManagerImpl.java
@@ -17,6 +17,7 @@
package org.conscrypt;
+import java.net.Socket;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -39,6 +40,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509TrustManager;
/**
@@ -90,7 +97,7 @@
/**
* Creates X509TrustManager based on a keystore
*
- * @param ks
+ * @param keyStore
*/
public TrustManagerImpl(KeyStore keyStore) {
this(keyStore, null);
@@ -100,6 +107,14 @@
* For testing only
*/
public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) {
+ this(keyStore, manager, null);
+ }
+
+ /**
+ * For testing only.
+ */
+ public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
+ TrustedCertificateStore certStore) {
CertPathValidator validatorLocal = null;
CertificateFactory factoryLocal = null;
KeyStore rootKeyStoreLocal = null;
@@ -114,12 +129,13 @@
// if we have an AndroidCAStore, we will lazily load CAs
if ("AndroidCAStore".equals(keyStore.getType())) {
rootKeyStoreLocal = keyStore;
- trustedCertificateStoreLocal = new TrustedCertificateStore();
+ trustedCertificateStoreLocal =
+ (certStore != null) ? certStore : new TrustedCertificateStore();
acceptedIssuersLocal = null;
trustedCertificateIndexLocal = new TrustedCertificateIndex();
} else {
rootKeyStoreLocal = null;
- trustedCertificateStoreLocal = null;
+ trustedCertificateStoreLocal = certStore;
acceptedIssuersLocal = acceptedIssuers(keyStore);
trustedCertificateIndexLocal
= new TrustedCertificateIndex(trustAnchors(acceptedIssuersLocal));
@@ -203,6 +219,25 @@
return checkTrusted(chain, authType, host, false);
}
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ if (trustedCertificateStore == null) {
+ return false;
+ } else {
+ return trustedCertificateStore.isUserAddedCertificate(cert);
+ }
+ }
+
+ /**
+ * Validates whether a server is trusted. If session is given and non-null
+ * it also checks if chain is pinned appropriately for that peer host. If
+ * null, it does not check for pinned certs. The return value is a list of
+ * the certificates used for making the trust decision.
+ */
+ public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
+ SSLSession session) throws CertificateException {
+ return checkTrusted(chain, authType, session.getPeerHost(), false);
+ }
+
public void handleTrustStorageUpdate() {
if (acceptedIssuers == null) {
trustedCertificateIndex.reset();
@@ -527,10 +562,11 @@
// probe KeyStore for a cert. AndroidCAStore stores its
// contents hashed by cert subject on the filesystem to make
// this faster than scanning all key store entries.
- if (trustedCertificateStore.isTrustAnchor(cert)) {
+ X509Certificate systemCert = trustedCertificateStore.getTrustAnchor(cert);
+ if (systemCert != null) {
// add new TrustAnchor to params index to avoid
// checking filesystem next time around.
- return trustedCertificateIndex.index(cert);
+ return trustedCertificateIndex.index(systemCert);
}
return null;
}
diff --git a/src/main/java/org/conscrypt/TrustedCertificateIndex.java b/src/platform/java/org/conscrypt/TrustedCertificateIndex.java
similarity index 100%
rename from src/main/java/org/conscrypt/TrustedCertificateIndex.java
rename to src/platform/java/org/conscrypt/TrustedCertificateIndex.java
diff --git a/src/main/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java b/src/platform/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java
similarity index 100%
rename from src/main/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java
rename to src/platform/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java
diff --git a/src/main/java/org/conscrypt/TrustedCertificateStore.java b/src/platform/java/org/conscrypt/TrustedCertificateStore.java
similarity index 93%
rename from src/main/java/org/conscrypt/TrustedCertificateStore.java
rename to src/platform/java/org/conscrypt/TrustedCertificateStore.java
index c8ba5cd..7287b75 100644
--- a/src/main/java/org/conscrypt/TrustedCertificateStore.java
+++ b/src/platform/java/org/conscrypt/TrustedCertificateStore.java
@@ -90,16 +90,15 @@
return alias.startsWith(PREFIX_USER);
}
- private static final File CA_CERTS_DIR_SYSTEM;
- private static final File CA_CERTS_DIR_ADDED;
- private static final File CA_CERTS_DIR_DELETED;
+ private static File defaultCaCertsSystemDir;
+ private static File defaultCaCertsAddedDir;
+ private static File defaultCaCertsDeletedDir;
private static final CertificateFactory CERT_FACTORY;
static {
String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
String ANDROID_DATA = System.getenv("ANDROID_DATA");
- CA_CERTS_DIR_SYSTEM = new File(ANDROID_ROOT + "/etc/security/cacerts");
- CA_CERTS_DIR_ADDED = new File(ANDROID_DATA + "/misc/keychain/cacerts-added");
- CA_CERTS_DIR_DELETED = new File(ANDROID_DATA + "/misc/keychain/cacerts-removed");
+ defaultCaCertsSystemDir = new File(ANDROID_ROOT + "/etc/security/cacerts");
+ setDefaultUserDirectory(new File(ANDROID_DATA + "/misc/keychain"));
try {
CERT_FACTORY = CertificateFactory.getInstance("X509");
@@ -108,12 +107,17 @@
}
}
+ public static void setDefaultUserDirectory(File root) {
+ defaultCaCertsAddedDir = new File(root, "cacerts-added");
+ defaultCaCertsDeletedDir = new File(root, "cacerts-removed");
+ }
+
private final File systemDir;
private final File addedDir;
private final File deletedDir;
public TrustedCertificateStore() {
- this(CA_CERTS_DIR_SYSTEM, CA_CERTS_DIR_ADDED, CA_CERTS_DIR_DELETED);
+ this(defaultCaCertsSystemDir, defaultCaCertsAddedDir, defaultCaCertsDeletedDir);
}
public TrustedCertificateStore(File systemDir, File addedDir, File deletedDir) {
@@ -271,6 +275,10 @@
}
public String getCertificateAlias(Certificate c) {
+ return getCertificateAlias(c, false);
+ }
+
+ public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
if (c == null || !(c instanceof X509Certificate)) {
return null;
}
@@ -279,7 +287,7 @@
if (user.exists()) {
return PREFIX_USER + user.getName();
}
- if (isDeletedSystemCertificate(x)) {
+ if (!includeDeletedSystem && isDeletedSystemCertificate(x)) {
return null;
}
File system = getCertificateFile(systemDir, x);
@@ -323,7 +331,7 @@
* with other differences (for example when switching signature
* from md2WithRSAEncryption to SHA1withRSA)
*/
- public boolean isTrustAnchor(final X509Certificate c) {
+ public X509Certificate getTrustAnchor(final X509Certificate c) {
// compare X509Certificate.getPublicKey values
CertSelector selector = new CertSelector() {
@Override
@@ -331,18 +339,21 @@
return ca.getPublicKey().equals(c.getPublicKey());
}
};
- boolean user = findCert(addedDir,
- c.getSubjectX500Principal(),
- selector,
- Boolean.class);
- if (user) {
- return true;
+ X509Certificate user = findCert(addedDir,
+ c.getSubjectX500Principal(),
+ selector,
+ X509Certificate.class);
+ if (user != null) {
+ return user;
}
X509Certificate system = findCert(systemDir,
c.getSubjectX500Principal(),
selector,
X509Certificate.class);
- return system != null && !isDeletedSystemCertificate(system);
+ if (system != null && !isDeletedSystemCertificate(system)) {
+ return system;
+ }
+ return null;
}
/**
diff --git a/src/test/java/org/conscrypt/CipherSuiteTest.java b/src/test/java/org/conscrypt/CipherSuiteTest.java
deleted file mode 100644
index a6338f9..0000000
--- a/src/test/java/org/conscrypt/CipherSuiteTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2010 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.MessageDigest;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import junit.framework.TestCase;
-import libcore.java.security.StandardNames;
-
-public class CipherSuiteTest extends TestCase {
- public void test_getByName() throws Exception {
- for (String name : StandardNames.CIPHER_SUITES) {
- if (name.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)) {
- assertNull(CipherSuite.getByName(name));
- } else if ((name.endsWith("_SHA256")) || (name.endsWith("_SHA384"))) {
- // TLSv1.2 cipher suites not supported by the SSLEngine implementation which is the
- // only user of the CipherSuite class.
- assertNull(CipherSuite.getByName(name));
- } else {
- test_CipherSuite(name);
- }
- }
-
- assertNull(CipherSuite.getByName("bogus"));
- try {
- CipherSuite.getByName(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- private void test_CipherSuite(String name) throws Exception {
- CipherSuite cs = CipherSuite.getByName(name);
- assertNotNull(name, cs);
- assertEquals(name, cs.getName());
- test_CipherSuite(cs);
- }
-
- private void test_CipherSuite(CipherSuite cs) throws Exception {
- assertNotNull(cs);
-
- String name = cs.getName();
- assertNotNull(name);
- assertSame(name, cs, CipherSuite.getByName(name));
- assertTrue(name, StandardNames.CIPHER_SUITES.contains(name));
- assertTrue(name, name.startsWith("SSL_") || name.startsWith("TLS_"));
-
- assertEquals(cs.isAnonymous(), name.contains("_anon_"));
-
- byte[] bytes = cs.toBytes();
- assertNotNull(name, bytes);
- assertEquals(name, 2, bytes.length);
- assertTrue(name + bytes[0], bytes[0] == (byte) 0x00 || bytes[0] == (byte) 0xc0);
- assertSame(name, cs, CipherSuite.getByCode(bytes[0], bytes[1]));
- assertSame(name, cs, CipherSuite.getByCode((byte) 0, bytes[0], bytes[1]));
-
- assertTrue(name, cs.toString().contains(name));
-
- String bulkEncryptionAlgorithm = cs.getBulkEncryptionAlgorithm();
- int blockSize = cs.getBlockSize();
- if (bulkEncryptionAlgorithm == null) {
- assertTrue(name, name.contains("_NULL_"));
- assertEquals(name, 0, blockSize);
- } else {
- assertNotNull(name, Cipher.getInstance(cs.getBulkEncryptionAlgorithm()));
- assertTrue(name, blockSize == 0 || blockSize == 8 || blockSize == 16);
- }
-
- String hmacName = cs.getHmacName();
- assertNotNull(name, hmacName);
- assertNotNull(name, Mac.getInstance(hmacName));
-
- String hashName = cs.getHashName();
- assertNotNull(name, hashName);
- assertNotNull(name, MessageDigest.getInstance(hashName));
-
- int macLength = cs.getMACLength();
- assertTrue(name, macLength == 0 || macLength == 16 || macLength == 20);
-
- assertTrue(name,
- cs.isExportable() == name.contains("_EXPORT_")
- || cs.isExportable() == name.contains("_NULL_"));
-
- String keyType = cs.getServerKeyType();
- assertEquals(name, cs.isAnonymous(), keyType == null);
- assertTrue(name, keyType == null || StandardNames.KEY_TYPES.contains(keyType));
- }
-
- public void test_getByCode() {
- // CipherSuite.getByCode is also covered by test_CipherSuite
- assertUnknown(CipherSuite.getByCode((byte) 0x12, (byte) 0x34));
- assertUnknown(CipherSuite.getByCode((byte) 0x12, (byte) 0x34, (byte) 0x56));
- assertUnknown(CipherSuite.getByCode((byte) -1, (byte) -1));
- assertUnknown(CipherSuite.getByCode((byte) -1, (byte) -1, (byte) -1));
- }
- private void assertUnknown(CipherSuite cs) {
- assertNotNull(cs);
- assertNotNull(cs.getName().contains("UNKNOWN"));
- }
-
- public void test_getSupported() throws Exception {
- CipherSuite[] suites = CipherSuite.getSupported();
- List<String> names = new ArrayList<String>(suites.length);
- for (CipherSuite cs : suites) {
- test_CipherSuite(cs);
- names.add(cs.getName());
- }
- assertEquals(Arrays.asList(CipherSuite.getSupportedCipherSuiteNames()), names);
- }
-
- public void test_getSupportedCipherSuiteNames() throws Exception {
- String[] names = CipherSuite.getSupportedCipherSuiteNames();
- StandardNames.assertSSLEngineSupportedCipherSuites(names);
- for (String name : names) {
- test_CipherSuite(name);
- }
- }
-
- public void test_getClientKeyType() throws Exception {
- byte b = Byte.MIN_VALUE;
- do {
- String byteString = Byte.toString(b);
- String keyType = CipherSuite.getClientKeyType(b);
- switch (b) {
- case 1:
- assertEquals(byteString, "RSA", keyType);
- break;
- case 2:
- assertEquals(byteString, "DSA", keyType);
- break;
- case 3:
- assertEquals(byteString, "DH_RSA", keyType);
- break;
- case 4:
- assertEquals(byteString, "DH_DSA", 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);
- }
-}
diff --git a/src/test/java/org/conscrypt/NativeCryptoTest.java b/src/test/java/org/conscrypt/NativeCryptoTest.java
index c561b06..b0fc7dd 100644
--- a/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -21,9 +21,11 @@
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
+import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -335,6 +337,8 @@
assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_TLSv1_1) == 0);
assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_TLSv1_2) == 0);
+ assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_TLSEXT_PADDING) != 0);
+
long s2 = NativeCrypto.SSL_new(c);
assertTrue(s != s2);
NativeCrypto.SSL_free(s2);
@@ -635,8 +639,15 @@
private static final boolean DEBUG = false;
public static class Hooks {
+ protected String negotiatedCipherSuite;
private OpenSSLKey channelIdPrivateKey;
+ protected boolean pskEnabled;
+ protected byte[] pskKey;
+ protected List<String> enabledCipherSuites;
+ /**
+ * @throws SSLException
+ */
public long getContext() throws SSLException {
return NativeCrypto.SSL_CTX_new();
}
@@ -645,19 +656,34 @@
// without this SSL_set_cipher_lists call the tests were
// negotiating DHE-RSA-AES256-SHA by default which had
// very slow ephemeral RSA key generation
- NativeCrypto.SSL_set_cipher_lists(s, new String[] { "RC4-MD5" });
+ List<String> cipherSuites = new ArrayList<String>();
+ if (enabledCipherSuites == null) {
+ cipherSuites.add("RC4-MD5");
+ if (pskEnabled) {
+ // In TLS-PSK the client indicates that PSK key exchange is desired by offering
+ // at least one PSK cipher suite.
+ cipherSuites.add(0, "PSK-AES128-CBC-SHA");
+ }
+ } else {
+ cipherSuites.addAll(enabledCipherSuites);
+ }
+ NativeCrypto.SSL_set_cipher_lists(
+ s, cipherSuites.toArray(new String[cipherSuites.size()]));
if (channelIdPrivateKey != null) {
NativeCrypto.SSL_set1_tls_channel_id(s, channelIdPrivateKey.getPkeyContext());
}
return s;
}
- public void clientCertificateRequested(long s) {}
+ public void configureCallbacks(
+ @SuppressWarnings("unused") TestSSLHandshakeCallbacks callbacks) {}
+ public void clientCertificateRequested(@SuppressWarnings("unused") long s) {}
public void afterHandshake(long session, long ssl, long context,
Socket socket, FileDescriptor fd,
SSLHandshakeCallbacks callback)
throws Exception {
if (session != NULL) {
+ negotiatedCipherSuite = NativeCrypto.SSL_SESSION_cipher(session);
NativeCrypto.SSL_SESSION_free(session);
}
if (ssl != NULL) {
@@ -693,11 +719,13 @@
public String authMethod;
public boolean verifyCertificateChainCalled;
- public void verifyCertificateChain(long[] certChainRefs, String authMethod)
- throws CertificateException {
+ @Override
+ public void verifyCertificateChain(long sslSessionNativePtr, long[] certChainRefs,
+ String authMethod) throws CertificateException {
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ " verifyCertificateChain"
+ + " sessionPtr=0x" + Long.toString(sslSessionNativePtr, 16)
+ " asn1DerEncodedCertificateChain="
+ Arrays.toString(certChainRefs)
+ " authMethod=" + authMethod);
@@ -710,6 +738,8 @@
public byte[] keyTypes;
public byte[][] asn1DerEncodedX500Principals;
public boolean clientCertificateRequestedCalled;
+
+ @Override
public void clientCertificateRequested(byte[] keyTypes,
byte[][] asn1DerEncodedX500Principals) {
if (DEBUG) {
@@ -728,10 +758,12 @@
}
public boolean handshakeCompletedCalled;
- public void handshakeCompleted() {
+
+ @Override
+ public void onSSLStateChange(long sslSessionNativePtr, int type, int val) {
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
- + " handshakeCompleted");
+ + " onSSLStateChange");
}
this.handshakeCompletedCalled = true;
}
@@ -739,6 +771,97 @@
public Socket getSocket() {
return socket;
}
+
+ private boolean clientPSKKeyRequestedInvoked;
+ private String clientPSKKeyRequestedIdentityHint;
+ private int clientPSKKeyRequestedResult;
+ private byte[] clientPSKKeyRequestedResultKey;
+ private byte[] clientPSKKeyRequestedResultIdentity;
+
+ @Override
+ public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+ if (DEBUG) {
+ System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ + " clientPSKKeyRequested"
+ + " identityHint=" + identityHint
+ + " identity capacity=" + identity.length
+ + " key capacity=" + key.length);
+ }
+ clientPSKKeyRequestedInvoked = true;
+ clientPSKKeyRequestedIdentityHint = identityHint;
+ if (clientPSKKeyRequestedResultKey != null) {
+ System.arraycopy(
+ clientPSKKeyRequestedResultKey, 0,
+ key, 0,
+ clientPSKKeyRequestedResultKey.length);
+ }
+ if (clientPSKKeyRequestedResultIdentity != null) {
+ System.arraycopy(
+ clientPSKKeyRequestedResultIdentity, 0,
+ identity, 0,
+ Math.min(clientPSKKeyRequestedResultIdentity.length, identity.length));
+ }
+ return clientPSKKeyRequestedResult;
+ }
+
+ private boolean serverPSKKeyRequestedInvoked;
+ private int serverPSKKeyRequestedResult;
+ private byte[] serverPSKKeyRequestedResultKey;
+ private String serverPSKKeyRequestedIdentityHint;
+ private String serverPSKKeyRequestedIdentity;
+ @Override
+ public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+ if (DEBUG) {
+ System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ + " serverPSKKeyRequested"
+ + " identityHint=" + identityHint
+ + " identity=" + identity
+ + " key capacity=" + key.length);
+ }
+ serverPSKKeyRequestedInvoked = true;
+ serverPSKKeyRequestedIdentityHint = identityHint;
+ serverPSKKeyRequestedIdentity = identity;
+ if (serverPSKKeyRequestedResultKey != null) {
+ System.arraycopy(
+ serverPSKKeyRequestedResultKey, 0,
+ key, 0,
+ serverPSKKeyRequestedResultKey.length);
+ }
+ return serverPSKKeyRequestedResult;
+ }
+ }
+
+ public static class ClientHooks extends Hooks {
+ protected String pskIdentity;
+
+ @Override
+ public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+ super.configureCallbacks(callbacks);
+ if (pskEnabled) {
+ if (pskIdentity != null) {
+ // Create a NULL-terminated modified UTF-8 representation of pskIdentity.
+ byte[] b;
+ try {
+ b = pskIdentity.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF-8 encoding not supported", e);
+ }
+ callbacks.clientPSKKeyRequestedResultIdentity =
+ Arrays.copyOf(b, b.length + 1);
+ }
+ callbacks.clientPSKKeyRequestedResultKey = pskKey;
+ callbacks.clientPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
+ }
+ }
+
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long s = super.beforeHandshake(c);
+ if (pskEnabled) {
+ NativeCrypto.set_SSL_psk_client_callback_enabled(s, true);
+ }
+ return s;
+ }
}
public static class ServerHooks extends Hooks {
@@ -748,6 +871,12 @@
private byte[] channelIdAfterHandshake;
private Throwable channelIdAfterHandshakeException;
+ protected String pskIdentityHint;
+
+ public ServerHooks() {
+ this(null, null);
+ }
+
public ServerHooks(OpenSSLKey privateKey, long[] certificates) {
this.privateKey = privateKey;
this.certificates = certificates;
@@ -765,10 +894,24 @@
if (channelIdEnabled) {
NativeCrypto.SSL_enable_tls_channel_id(s);
}
+ if (pskEnabled) {
+ NativeCrypto.set_SSL_psk_server_callback_enabled(s, true);
+ NativeCrypto.SSL_use_psk_identity_hint(s, pskIdentityHint);
+ }
+ NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_NONE);
return s;
}
@Override
+ public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+ super.configureCallbacks(callbacks);
+ if (pskEnabled) {
+ callbacks.serverPSKKeyRequestedResultKey = pskKey;
+ callbacks.serverPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
+ }
+ }
+
+ @Override
public void afterHandshake(long session, long ssl, long context,
Socket socket, FileDescriptor fd,
SSLHandshakeCallbacks callback)
@@ -783,6 +926,7 @@
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
+ @Override
public void clientCertificateRequested(long s) {
fail("Server asked for client certificates");
}
@@ -795,6 +939,7 @@
Future<TestSSLHandshakeCallbacks> future = 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())
@@ -807,6 +952,7 @@
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"
@@ -880,6 +1026,10 @@
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@@ -923,13 +1073,18 @@
// this depends on the SSL_set_cipher_lists call in beforeHandshake
// the three returned are the non-ephemeral cases.
assertEquals(3, clientCallback.keyTypes.length);
- assertEquals("RSA", OpenSSLSocketImpl.getClientKeyType(clientCallback.keyTypes[0]));
- assertEquals("DSA", OpenSSLSocketImpl.getClientKeyType(clientCallback.keyTypes[1]));
- assertEquals("EC", OpenSSLSocketImpl.getClientKeyType(clientCallback.keyTypes[2]));
+ assertEquals("RSA", SSLParametersImpl.getClientKeyType(clientCallback.keyTypes[0]));
+ assertEquals("DSA", SSLParametersImpl.getClientKeyType(clientCallback.keyTypes[1]));
+ assertEquals("EC", SSLParametersImpl.getClientKeyType(clientCallback.keyTypes[2]));
assertEqualPrincipals(getCaPrincipals(),
clientCallback.asn1DerEncodedX500Principals);
assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@@ -950,6 +1105,7 @@
return s;
}
};
+ @SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
null);
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
@@ -1079,8 +1235,11 @@
final ServerSocket listener = new ServerSocket(0);
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"});
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
sHooks.channelIdEnabled = true;
+ sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -1088,10 +1247,14 @@
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(getServerCertificates(),
clientCallback.certificateChainRefs);
- assertEquals("RSA", clientCallback.authMethod);
+ assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
@@ -1105,8 +1268,11 @@
final ServerSocket listener = new ServerSocket(0);
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"});
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
sHooks.channelIdEnabled = false;
+ sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -1114,10 +1280,14 @@
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(getServerCertificates(),
clientCallback.certificateChainRefs);
- assertEquals("RSA", clientCallback.authMethod);
+ assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
@@ -1131,8 +1301,11 @@
final ServerSocket listener = new ServerSocket(0);
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"});
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
sHooks.channelIdEnabled = true;
+ sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -1140,14 +1313,232 @@
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(getServerCertificates(),
clientCallback.certificateChainRefs);
- assertEquals("RSA", clientCallback.authMethod);
+ assertEquals("ECDHE_RSA", clientCallback.authMethod);
+ assertFalse(serverCallback.verifyCertificateChainCalled);
+ assertFalse(clientCallback.clientCertificateRequestedCalled);
+ assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.handshakeCompletedCalled);
+ assertTrue(serverCallback.handshakeCompletedCalled);
+ assertNull(sHooks.channelIdAfterHandshakeException);
+ assertNull(sHooks.channelIdAfterHandshake);
+ }
+
+ public void test_SSL_do_handshake_with_psk_normal() throws Exception {
+ // normal TLS-PSK client and server case
+ final ServerSocket listener = new ServerSocket(0);
+ Hooks cHooks = new ClientHooks();
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ sHooks.pskKey = cHooks.pskKey;
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertFalse(clientCallback.verifyCertificateChainCalled);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
- assertNull(sHooks.channelIdAfterHandshakeException);
- assertNull(sHooks.channelIdAfterHandshake);
+ assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+ assertContains(cHooks.negotiatedCipherSuite, "PSK");
+ assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+ assertNull(clientCallback.clientPSKKeyRequestedIdentityHint);
+ assertNull(serverCallback.serverPSKKeyRequestedIdentityHint);
+ assertEquals("", serverCallback.serverPSKKeyRequestedIdentity);
+ }
+
+ 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);
+ ClientHooks cHooks = new ClientHooks();
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ sHooks.pskKey = cHooks.pskKey;
+ sHooks.pskIdentityHint = "Some non-ASCII characters: \u00c4\u0332";
+ cHooks.pskIdentity = "More non-ASCII characters: \u00f5\u044b";
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertFalse(clientCallback.verifyCertificateChainCalled);
+ assertFalse(serverCallback.verifyCertificateChainCalled);
+ assertFalse(clientCallback.clientCertificateRequestedCalled);
+ assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertTrue(clientCallback.handshakeCompletedCalled);
+ assertTrue(serverCallback.handshakeCompletedCalled);
+ assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+ assertContains(cHooks.negotiatedCipherSuite, "PSK");
+ assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+ assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
+ assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
+ assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
+ }
+
+ public void test_SSL_do_handshake_with_psk_with_identity_and_hint_of_max_length()
+ 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);
+ ClientHooks cHooks = new ClientHooks();
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ sHooks.pskKey = cHooks.pskKey;
+ sHooks.pskIdentityHint = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx";
+ cHooks.pskIdentity = "123456789012345678901234567890123456789012345678901234567890"
+ + "12345678901234567890123456789012345678901234567890123456789012345678";
+ assertEquals(PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES, sHooks.pskIdentityHint.length());
+ assertEquals(PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES, cHooks.pskIdentity.length());
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertFalse(clientCallback.verifyCertificateChainCalled);
+ assertFalse(serverCallback.verifyCertificateChainCalled);
+ assertFalse(clientCallback.clientCertificateRequestedCalled);
+ assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertTrue(clientCallback.handshakeCompletedCalled);
+ assertTrue(serverCallback.handshakeCompletedCalled);
+ assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+ assertContains(cHooks.negotiatedCipherSuite, "PSK");
+ assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+ assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
+ assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
+ assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
+ }
+
+ public void test_SSL_do_handshake_with_psk_key_mismatch() throws Exception {
+ final ServerSocket listener = new ServerSocket(0);
+ ClientHooks cHooks = new ClientHooks();
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ sHooks.pskKey = "1, 2, 3, 3, Testing...".getBytes("UTF-8");
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ try {
+ client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ fail();
+ } catch (ExecutionException expected) {
+ assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+ }
+ }
+
+ public void test_SSL_do_handshake_with_psk_with_no_client_key() throws Exception {
+ final ServerSocket listener = new ServerSocket(0);
+ ClientHooks cHooks = new ClientHooks();
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = null;
+ sHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ try {
+ client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ fail();
+ } catch (ExecutionException expected) {
+ assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+ }
+ }
+
+ public void test_SSL_do_handshake_with_psk_with_no_server_key() throws Exception {
+ final ServerSocket listener = new ServerSocket(0);
+ ClientHooks cHooks = new ClientHooks();
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ sHooks.pskKey = null;
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ try {
+ client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ fail();
+ } catch (ExecutionException expected) {
+ assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+ }
+ }
+
+ public void test_SSL_do_handshake_with_psk_key_too_long() throws Exception {
+ final ServerSocket listener = new ServerSocket(0);
+ ClientHooks cHooks = new ClientHooks() {
+ @Override
+ public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+ super.configureCallbacks(callbacks);
+ callbacks.clientPSKKeyRequestedResult = PSKKeyManager.MAX_KEY_LENGTH_BYTES + 1;
+ }
+ };
+ ServerHooks sHooks = new ServerHooks();
+ cHooks.pskEnabled = true;
+ sHooks.pskEnabled = true;
+ cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+ sHooks.pskKey = cHooks.pskKey;
+ Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+ Future<TestSSLHandshakeCallbacks> server =
+ handshake(listener, 0, false, sHooks, null, null);
+ try {
+ client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ fail();
+ } catch (ExecutionException expected) {
+ assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+ }
+ }
+
+ public void test_SSL_use_psk_identity_hint() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ NativeCrypto.SSL_use_psk_identity_hint(s, null);
+ NativeCrypto.SSL_use_psk_identity_hint(s, "test");
+
+ try {
+ // 800 characters is much longer than the permitted maximum.
+ StringBuilder pskIdentityHint = new StringBuilder();
+ for (int i = 0; i < 160; i++) {
+ pskIdentityHint.append(" long");
+ }
+ assertTrue(pskIdentityHint.length() > PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES);
+ NativeCrypto.SSL_use_psk_identity_hint(s, pskIdentityHint.toString());
+ fail();
+ } catch (SSLException expected) {
+ }
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
}
public void test_SSL_set_session() throws Exception {
@@ -1288,6 +1679,7 @@
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
null);
+ @SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -1309,6 +1701,7 @@
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
null);
+ @SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -1406,6 +1799,8 @@
assertEquals("spdy/2", new String(negotiated));
assertTrue("NPN should enable cutthrough on the client",
0 != (NativeCrypto.SSL_get_mode(ssl) & SSL_MODE_HANDSHAKE_CUTTHROUGH));
+ NativeCrypto.SSL_write(ssl, fd, callback, new byte[] { 42 }, 0, 1,
+ (int) ((TIMEOUT_SECONDS * 1000) / 2));
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
@@ -1420,6 +1815,9 @@
assertEquals("spdy/2", new String(negotiated));
assertEquals("NPN should not enable cutthrough on the server",
0, NativeCrypto.SSL_get_mode(ssl) & SSL_MODE_HANDSHAKE_CUTTHROUGH);
+ byte[] buffer = new byte[1];
+ NativeCrypto.SSL_read(ssl, fd, callback, buffer, 0, 1, 0);
+ assertEquals(42, buffer[0]);
super.afterHandshake(session, ssl, c, sock, fd, callback);
}
};
@@ -1447,8 +1845,9 @@
Hooks cHooks = new Hooks() {
@Override public long beforeHandshake(long context) throws SSLException {
- NativeCrypto.SSL_CTX_set_alpn_protos(context, clientAlpnProtocols);
- return super.beforeHandshake(context);
+ long sslContext = super.beforeHandshake(context);
+ NativeCrypto.SSL_set_alpn_protos(sslContext, clientAlpnProtocols);
+ return sslContext;
}
@Override public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
@@ -1733,6 +2132,7 @@
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null,
null);
+ @SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null,
null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -1836,6 +2236,7 @@
SSLHandshakeCallbacks callback)
throws Exception {
new Thread() {
+ @Override
public void run() {
try {
Thread.sleep(1*1000);
@@ -1854,32 +2255,61 @@
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
+ private static abstract class SSLSessionWrappedTask {
+ public abstract void run(long sslSession) throws Exception;
+ }
+
+ private void wrapWithSSLSession(SSLSessionWrappedTask task) throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c);
+ try {
+ task.run(s);
+ } finally {
+ NativeCrypto.SSL_free(s);
+ NativeCrypto.SSL_CTX_free(c);
+ }
+ }
+
public void test_SSL_shutdown() throws Exception {
// null FileDescriptor
- try {
- NativeCrypto.SSL_shutdown(NULL, null, DUMMY_CB);
- } catch (NullPointerException expected) {
- }
+ wrapWithSSLSession(new SSLSessionWrappedTask() {
+ @Override
+ public void run(long sslSession) throws Exception {
+ try {
+ NativeCrypto.SSL_shutdown(sslSession, null, DUMMY_CB);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+ });
// null SSLHandshakeCallbacks
- try {
- NativeCrypto.SSL_shutdown(NULL, INVALID_FD, null);
- } catch (NullPointerException expected) {
- }
+ wrapWithSSLSession(new SSLSessionWrappedTask() {
+ @Override
+ public void run(long sslSession) throws Exception {
+ try {
+ NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+ });
// SSL_shutdown is a rare case that tolerates a null SSL argument
NativeCrypto.SSL_shutdown(NULL, INVALID_FD, DUMMY_CB);
// handshaking not yet performed
- long c = NativeCrypto.SSL_CTX_new();
- long s = NativeCrypto.SSL_new(c);
- try {
- NativeCrypto.SSL_shutdown(s, INVALID_FD, DUMMY_CB);
- } catch (SSLProtocolException expected) {
- }
- NativeCrypto.SSL_free(s);
- NativeCrypto.SSL_CTX_free(c);
+ wrapWithSSLSession(new SSLSessionWrappedTask() {
+ @Override
+ public void run(long sslSession) throws Exception {
+ try {
+ NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, DUMMY_CB);
+ fail();
+ } catch (SocketException expected) {
+ }
+ }
+ });
// positively tested elsewhere because handshake uses use
// SSL_shutdown to ensure SSL_SESSIONs are reused.
@@ -2218,12 +2648,12 @@
}
public void test_EVP_SignInit() throws Exception {
- final long ctx = NativeCrypto.EVP_SignInit("RSA-SHA256");
- assertTrue(ctx != NULL);
- NativeCrypto.EVP_MD_CTX_destroy(ctx);
+ final OpenSSLDigestContext ctx = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
+ assertEquals(1,
+ NativeCrypto.EVP_SignInit(ctx, NativeCrypto.EVP_get_digestbyname("sha256")));
try {
- NativeCrypto.EVP_SignInit("foobar");
+ NativeCrypto.EVP_SignInit(ctx, 0);
fail();
} catch (RuntimeException expected) {
}
@@ -2287,6 +2717,42 @@
}
}
+ public void test_RSA_size_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.RSA_size(0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_RSA_private_encrypt_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.RSA_private_encrypt(0, new byte[0], new byte[0], 0, 0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_RSA_private_decrypt_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.RSA_private_decrypt(0, new byte[0], new byte[0], 0, 0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_RSA_public_encrypt_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.RSA_public_encrypt(0, new byte[0], new byte[0], 0, 0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_RSA_public_decrypt_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.RSA_public_decrypt(0, new byte[0], new byte[0], 0, 0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+
final byte[] dsa2048_p = {
(byte) 0x00, (byte) 0xC3, (byte) 0x16, (byte) 0xD4, (byte) 0xBA, (byte) 0xDC,
(byte) 0x0E, (byte) 0xB8, (byte) 0xFC, (byte) 0x40, (byte) 0xDB, (byte) 0xB0,
@@ -2433,6 +2899,20 @@
}
}
+ public void test_get_DSA_params_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.get_DSA_params(0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_set_DSA_flag_nonce_from_hash_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.set_DSA_flag_nonce_from_hash(0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
/*
* Test vector generation:
* openssl rand -hex 16
@@ -2443,12 +2923,6 @@
(byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f,
};
- private static final byte[] AES_IV_ZEROES = new byte[] {
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- };
-
public void testEC_GROUP() throws Exception {
/* Test using NIST's P-256 curve */
check_EC_GROUP(NativeCrypto.EC_CURVE_GFP, "prime256v1",
@@ -2566,6 +3040,68 @@
}
}
+ public void test_EC_KEY_get_private_key_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.EC_KEY_get_private_key(0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_EC_KEY_get_public_key_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.EC_KEY_get_public_key(0);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_EC_KEY_set_nonce_from_hash_null_key_Failure() throws Exception {
+ try {
+ NativeCrypto.EC_KEY_set_nonce_from_hash(0, true);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void test_ECDH_compute_key_null_key_Failure() throws Exception {
+ long groupRef = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+ if (groupRef == 0) {
+ fail();
+ }
+ try {
+ long pkey1Ref = NativeCrypto.EC_KEY_generate_key(groupRef);
+ long pkey2Ref = NativeCrypto.EC_KEY_generate_key(groupRef);
+ try {
+ if (pkey1Ref == 0) {
+ fail();
+ }
+ if (pkey2Ref == 0) {
+ fail();
+ }
+
+ byte[] out = new byte[128];
+ int outOffset = 0;
+ // Assert that the method under test works fine with the two non-null keys
+ NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, pkey2Ref);
+
+ // Assert that it fails when only the first key is null
+ try {
+ NativeCrypto.ECDH_compute_key(out, outOffset, 0, pkey2Ref);
+ fail();
+ } catch (NullPointerException expected) {}
+
+ // Assert that it fails when only the second key is null
+ try {
+ NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, 0);
+ fail();
+ } catch (NullPointerException expected) {}
+ } finally {
+ NativeCrypto.EVP_PKEY_free(pkey1Ref);
+ NativeCrypto.EVP_PKEY_free(pkey2Ref);
+ }
+ } finally {
+ NativeCrypto.EC_GROUP_clear_free(groupRef);
+ }
+ }
+
public void test_EVP_CipherInit_ex_Null_Failure() throws Exception {
final long ctx = NativeCrypto.EVP_CIPHER_CTX_new();
try {
@@ -2634,15 +3170,16 @@
byte[] actual = "Test".getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(actual);
- long ctx = NativeCrypto.create_BIO_InputStream(new OpenSSLBIOInputStream(is));
+ @SuppressWarnings("resource")
+ OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
try {
byte[] buffer = new byte[1024];
- int numRead = NativeCrypto.BIO_read(ctx, buffer);
+ int numRead = NativeCrypto.BIO_read(bis.getBioContext(), buffer);
assertEquals(actual.length, numRead);
assertEquals(Arrays.toString(actual),
Arrays.toString(Arrays.copyOfRange(buffer, 0, numRead)));
} finally {
- NativeCrypto.BIO_free(ctx);
+ bis.release();
}
}
@@ -2657,7 +3194,17 @@
assertEquals(actual.length, os.size());
assertEquals(Arrays.toString(actual), Arrays.toString(os.toByteArray()));
} finally {
- NativeCrypto.BIO_free(ctx);
+ NativeCrypto.BIO_free_all(ctx);
}
}
+
+ private static void assertContains(String actualValue, String expectedSubstring) {
+ if (actualValue == null) {
+ return;
+ }
+ if (actualValue.contains(expectedSubstring)) {
+ return;
+ }
+ fail("\"" + actualValue + "\" does not contain \"" + expectedSubstring + "\"");
+ }
}
diff --git a/src/test/java/org/conscrypt/OpenSSLSignatureTest.java b/src/test/java/org/conscrypt/OpenSSLSignatureTest.java
deleted file mode 100644
index 045c509..0000000
--- a/src/test/java/org/conscrypt/OpenSSLSignatureTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2010 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.NoSuchAlgorithmException;
-import junit.framework.TestCase;
-
-public class OpenSSLSignatureTest extends TestCase {
-
- public void test_getInstance() throws Exception {
- try {
- OpenSSLSignature.getInstance("SHA1WITHDSA");
- OpenSSLSignature.getInstance("MD5WITHRSAENCRYPTION");
- OpenSSLSignature.getInstance("SHA1WITHRSAENCRYPTION");
- OpenSSLSignature.getInstance("SHA256WITHRSAENCRYPTION");
- OpenSSLSignature.getInstance("SHA384WITHRSAENCRYPTION");
- OpenSSLSignature.getInstance("SHA512WITHRSAENCRYPTION");
- } catch (NoSuchAlgorithmException e) {
- fail("getInstance is not case insensitive");
- }
- }
-}
diff --git a/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java b/src/test/java/org/conscrypt/SSLParametersImplTest.java
similarity index 93%
rename from src/test/java/org/conscrypt/OpenSSLSocketImplTest.java
rename to src/test/java/org/conscrypt/SSLParametersImplTest.java
index 1d03694..6371648 100644
--- a/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java
+++ b/src/test/java/org/conscrypt/SSLParametersImplTest.java
@@ -18,14 +18,14 @@
import junit.framework.TestCase;
-public class OpenSSLSocketImplTest extends 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 = OpenSSLSocketImpl.getClientKeyType(b);
+ String keyType = SSLParametersImpl.getClientKeyType(b);
switch (b) {
case 1:
assertEquals(byteString, "RSA", keyType);
diff --git a/src/test/java/org/conscrypt/TrustManagerImplTest.java b/src/test/java/org/conscrypt/TrustManagerImplTest.java
index 9f757cf..035d4f2 100644
--- a/src/test/java/org/conscrypt/TrustManagerImplTest.java
+++ b/src/test/java/org/conscrypt/TrustManagerImplTest.java
@@ -18,13 +18,18 @@
import java.io.File;
import java.io.FileWriter;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.security.MessageDigest;
+import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import junit.framework.TestCase;
@@ -128,9 +133,10 @@
assertTrue(tm instanceof TrustManagerImpl);
TrustManagerImpl tmi = (TrustManagerImpl) tm;
- List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", "purple.com");
+ List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", new MySSLSession(
+ "purple.com"));
assertEquals(Arrays.asList(chain3), certs);
- certs = tmi.checkServerTrusted(chain1, "RSA", "purple.com");
+ certs = tmi.checkServerTrusted(chain1, "RSA", new MySSLSession("purple.com"));
assertEquals(Arrays.asList(chain3), certs);
}
@@ -156,7 +162,12 @@
assertInvalidPinned(chain1, trustManager(intermediate, "gugle.com", root), "gugle.com");
// test a pinned hostname that should succeed
assertValidPinned(chain2, trustManager(intermediate, "gugle.com", server), "gugle.com",
- chain2);
+ chain2);
+ // test a pinned hostname that chains to user installed that should succeed
+ assertValidPinned(chain2, trustManagerUserInstalled(
+ (X509Certificate)TestKeyStore.getIntermediateCa2().getPrivateKey("RSA", "RSA")
+ .getCertificateChain()[1], intermediate, "gugle.com", server), "gugle.com",
+ chain2, true);
}
private X509TrustManager trustManager(X509Certificate ca) throws Exception {
@@ -179,6 +190,29 @@
return new TrustManagerImpl(keyStore, cm);
}
+ private TrustManagerImpl trustManagerUserInstalled(
+ X509Certificate caKeyStore, X509Certificate caUserStore, String hostname,
+ X509Certificate pin) throws Exception {
+ // build the cert pin manager
+ CertPinManager cm = certManager(hostname, pin);
+
+ // install at least one cert in the store (requirement)
+ KeyStore keyStore = TestKeyStore.createKeyStore();
+ keyStore.setCertificateEntry("alias", caKeyStore);
+
+ // install a cert into the user installed store
+ final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
+ final File DIR_TEST = new File(DIR_TEMP, "test");
+ final File system = new File(DIR_TEST, "system-test");
+ final File added = new File(DIR_TEST, "added-test");
+ final File deleted = new File(DIR_TEST, "deleted-test");
+
+ TrustedCertificateStore tcs = new TrustedCertificateStore(system, added, deleted);
+ added.mkdirs();
+ tcs.installCertificate(caUserStore);
+ return new TrustManagerImpl(keyStore, cm, tcs);
+ }
+
private CertPinManager certManager(String hostname, X509Certificate pin) throws Exception {
String pinString = "";
if (pin != null) {
@@ -200,10 +234,24 @@
private void assertValidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname,
X509Certificate[] fullChain) throws Exception {
+ assertValidPinned(chain, tm, hostname, fullChain, false);
+ }
+
+ private void assertValidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname,
+ X509Certificate[] fullChain, boolean expectUserInstalled)
+ throws Exception {
if (tm instanceof TrustManagerImpl) {
TrustManagerImpl tmi = (TrustManagerImpl) tm;
- List<X509Certificate> checkedChain = tmi.checkServerTrusted(chain, "RSA", hostname);
+ List<X509Certificate> checkedChain = tmi.checkServerTrusted(chain, "RSA",
+ new MySSLSession(hostname));
assertEquals(checkedChain, Arrays.asList(fullChain));
+ boolean chainContainsUserInstalled = false;
+ for (X509Certificate cert : checkedChain) {
+ if (tmi.isUserAddedCertificate(cert)) {
+ chainContainsUserInstalled = true;
+ }
+ }
+ assertEquals(expectUserInstalled, chainContainsUserInstalled);
}
tm.checkServerTrusted(chain, "RSA");
}
@@ -226,9 +274,123 @@
assertTrue(tm.getClass().getName(), tm instanceof TrustManagerImpl);
try {
TrustManagerImpl tmi = (TrustManagerImpl) tm;
- tmi.checkServerTrusted(chain, "RSA", hostname);
+ tmi.checkServerTrusted(chain, "RSA", new MySSLSession(hostname));
fail();
} catch (CertificateException expected) {
}
}
+
+ private class MySSLSession implements SSLSession {
+ private final String hostname;
+
+ public MySSLSession(String hostname) {
+ this.hostname = hostname;
+ }
+
+ @Override
+ public int getApplicationBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getCreationTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPeerHost() {
+ return hostname;
+ }
+
+ @Override
+ public int getPeerPort() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getProtocol() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java b/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java
index c75f540..2bb9efd 100644
--- a/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java
+++ b/src/test/java/org/conscrypt/TrustedCertificateStoreTest.java
@@ -22,26 +22,24 @@
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
-import java.util.NoSuchElementException;
+import java.util.Random;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import junit.framework.TestCase;
import libcore.java.security.TestKeyStore;
public class TrustedCertificateStoreTest extends TestCase {
+ private static final Random tempFileRandom = new Random();
- private static final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
- private static final File DIR_TEST = new File(DIR_TEMP, "test");
- private static final File DIR_SYSTEM = new File(DIR_TEST, "system");
- private static final File DIR_ADDED = new File(DIR_TEST, "added");
- private static final File DIR_DELETED = new File(DIR_TEST, "removed");
+ private final File dirTest = new File(System.getProperty("java.io.tmpdir", "."),
+ "cert-store-test" + tempFileRandom.nextInt());
+ private final File dirSystem = new File(dirTest, "system");
+ private final File dirAdded = new File(dirTest, "added");
+ private final File dirDeleted = new File(dirTest, "removed");
private static X509Certificate CA1;
private static X509Certificate CA2;
@@ -196,12 +194,13 @@
}
private void setupStore() {
- DIR_SYSTEM.mkdirs();
+ dirSystem.mkdirs();
+ cleanStore();
createStore();
}
private void createStore() {
- store = new TrustedCertificateStore(DIR_SYSTEM, DIR_ADDED, DIR_DELETED);
+ store = new TrustedCertificateStore(dirSystem, dirAdded, dirDeleted);
}
@Override protected void tearDown() {
@@ -209,7 +208,7 @@
}
private void cleanStore() {
- for (File dir : new File[] { DIR_SYSTEM, DIR_ADDED, DIR_DELETED, DIR_TEST }) {
+ for (File dir : new File[] { dirSystem, dirAdded, dirDeleted, dirTest }) {
File[] files = dir.listFiles();
if (files == null) {
continue;
@@ -249,6 +248,7 @@
public void testPartialFileIsIgnored() throws Exception {
File file = file(getAliasSystemCa1());
+ file.getParentFile().mkdirs();
OutputStream os = new FileOutputStream(file);
os.write(0);
os.close();
@@ -292,11 +292,11 @@
assertNull(store.getCertificateAlias(getCa1()));
try {
- store.isTrustAnchor(null);
+ store.getTrustAnchor(null);
fail();
} catch (NullPointerException expected) {
}
- assertFalse(store.isTrustAnchor(getCa1()));
+ assertNull(store.getTrustAnchor(getCa1()));
try {
store.findIssuer(null);
@@ -314,7 +314,7 @@
store.deleteCertificateEntry(null);
store.deleteCertificateEntry("");
- String[] userFiles = DIR_ADDED.list();
+ String[] userFiles = dirAdded.list();
assertTrue(userFiles == null || userFiles.length == 0);
}
@@ -432,8 +432,8 @@
}
public void testWithExistingUserDirectories() throws Exception {
- DIR_ADDED.mkdirs();
- DIR_DELETED.mkdirs();
+ dirAdded.mkdirs();
+ dirDeleted.mkdirs();
install(getCa1(), getAliasSystemCa1());
assertRootCa(getCa1(), getAliasSystemCa1());
assertAliases(getAliasSystemCa1());
@@ -451,14 +451,14 @@
String systemAlias = alias(false, ca1, 0);
install(ca1, systemAlias);
assertRootCa(ca1, systemAlias);
- assertTrue(store.isTrustAnchor(ca2));
+ assertEquals(ca1, store.getTrustAnchor(ca2));
assertEquals(ca1, store.findIssuer(ca2));
resetStore();
String userAlias = alias(true, ca1, 0);
store.installCertificate(ca1);
assertRootCa(ca1, userAlias);
- assertTrue(store.isTrustAnchor(ca2));
+ assertNotNull(store.getTrustAnchor(ca2));
assertEquals(ca1, store.findIssuer(ca2));
resetStore();
}
@@ -559,7 +559,7 @@
assertEquals(x, store.getCertificate(alias));
assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime());
assertTrue(store.containsAlias(alias));
- assertTrue(store.isTrustAnchor(x));
+ assertEquals(x, store.getTrustAnchor(x));
}
private void assertIntermediateCa(X509Certificate x, String alias) {
@@ -576,7 +576,7 @@
assertNull(store.getCertificate(alias));
assertFalse(store.containsAlias(alias));
assertNull(store.getCertificateAlias(x));
- assertFalse(store.isTrustAnchor(x));
+ assertNull(store.getTrustAnchor(x));
assertEquals(store.allSystemAliases().contains(alias),
store.getCertificate(alias, true) != null);
}
@@ -626,7 +626,7 @@
/**
* Install certificate under specified alias
*/
- private static void install(X509Certificate x, String alias) {
+ private void install(X509Certificate x, String alias) {
try {
File file = file(alias);
file.getParentFile().mkdirs();
@@ -641,12 +641,12 @@
/**
* Compute file for an alias
*/
- private static File file(String alias) {
+ private File file(String alias) {
File dir;
if (TrustedCertificateStore.isSystem(alias)) {
- dir = DIR_SYSTEM;
+ dir = dirSystem;
} else if (TrustedCertificateStore.isUser(alias)) {
- dir = DIR_ADDED;
+ dir = dirAdded;
} else {
throw new IllegalArgumentException(alias);
}
diff --git a/update_prebuilts.sh b/update_prebuilts.sh
new file mode 100755
index 0000000..c765e5d
--- /dev/null
+++ b/update_prebuilts.sh
@@ -0,0 +1,118 @@
+#!/usr/bin/env bash
+
+if (( BASH_VERSINFO[0] < 3 )); then
+ echo "Must be running BASH version 3 or newer!"
+ exit 1
+fi
+
+if [[ -z $TOP ]]; then \
+ echo "You must do envsetup beforehand."
+ exit 1
+fi
+
+# We are currently in frameworks/rs, so compute our top-level directory.
+MY_ANDROID_DIR="$TOP"
+cd "$MY_ANDROID_DIR"
+
+if [[ $OSTYPE != *linux* ]]; then \
+ echo "Only works on Linux."
+ exit 1
+fi
+
+SHORT_OSNAME=linux
+SONAME=so
+# Target architectures and their system library names.
+declare -a TARGETS=(generic_armv5 aosp_arm aosp_mips aosp_x86)
+declare -a ABI_NAMES=(armeabi armeabi-v7a mips x86)
+declare -a SYS_NAMES=(generic_armv5 generic generic_mips generic_x86)
+declare -i NUM_CORES="$(awk '/^processor/ { i++ } END { print i }' /proc/cpuinfo)"
+
+echo "Using $NUM_CORES cores"
+
+# Turn off the build cache and make sure we build all of LLVM from scratch.
+#export ANDROID_USE_BUILDCACHE=false
+
+# PREBUILTS_DIR is where we want to copy our new files to.
+PREBUILTS_DIR="$MY_ANDROID_DIR/prebuilts/conscrypt/"
+
+print_usage() {
+ echo "USAGE: $0 [-h|--help] [-n|--no-build] [-x]"
+ echo "OPTIONS:"
+ echo " -h, --help : Display this help message."
+ echo " -n, --no-build : Skip the build step and just copy files."
+ echo " -x : Display commands before they are executed."
+}
+
+build_libs() {
+ local t="$1"
+ echo Building for target $t
+ cd $MY_ANDROID_DIR
+ WITH_HOST_DALVIK=false make -j32 PRODUCT-$t-userdebug APP-conscrypt_unbundled-libconscrypt_jni || exit 1
+}
+
+# Build everything by default
+build_me=1
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -h|--help)
+ print_usage
+ exit 0
+ ;;
+ -n|--no-build)
+ build_me=0
+ ;;
+ -x)
+ # set lets us enable bash -x mode.
+ set -x
+ ;;
+ *)
+ echo Unknown argument: "$1"
+ print_usage
+ exit 99
+ break
+ ;;
+ esac
+ shift
+done
+
+declare -i i
+
+if [ $build_me -eq 1 ]; then
+
+ echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ echo !!! BUILDING CONSCRYPT PREBUILTS !!!
+ echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ source build/envsetup.sh
+
+ for (( i=0; i < ${#TARGETS[@]}; i++ )); do
+ build_libs "${TARGETS[$i]}"
+ done
+
+ echo DONE BUILDING CONSCRYPT PREBUILTS
+
+else
+
+ echo SKIPPING BUILD OF CONSCRYPT PREBUILTS
+
+fi
+
+DATE="$(date +"%Y%m%d")"
+
+cd "$PREBUILTS_DIR" || exit 3
+repo start "pb_$DATE" .
+
+# Don't copy device prebuilts on Darwin. We don't need/use them.
+for (( i=0; i < ${#TARGETS[@]}; i++ )); do
+ sys="${SYS_NAMES[$i]}"
+ abi="${ABI_NAMES[$i]}"
+ sys_lib_dir="$MY_ANDROID_DIR/out/target/product/$sys/system/lib"
+ if [[ ! -d "jni/$abi" ]]; then
+ mkdir -p "jni/$abi"
+ fi
+ cp "$sys_lib_dir/libconscrypt_jni.so" "jni/$abi/" || exit 4
+done
+
+# javalib.jar
+cp "$MY_ANDROID_DIR/out/target/common/obj/JAVA_LIBRARIES/conscrypt_unbundled_intermediates/classes.jar" .