summaryrefslogtreecommitdiffstats
path: root/luni/src/main/native/NativeCrypto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/main/native/NativeCrypto.cpp')
-rw-r--r--luni/src/main/native/NativeCrypto.cpp295
1 files changed, 103 insertions, 192 deletions
diff --git a/luni/src/main/native/NativeCrypto.cpp b/luni/src/main/native/NativeCrypto.cpp
index f94b7e7..0f9ae1d 100644
--- a/luni/src/main/native/NativeCrypto.cpp
+++ b/luni/src/main/native/NativeCrypto.cpp
@@ -20,6 +20,7 @@
#define LOG_TAG "NativeCrypto"
+#include <algorithm>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
@@ -37,7 +38,6 @@
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
-#include "LocalArray.h"
#include "NetFd.h"
#include "NetworkUtilities.h"
#include "ScopedLocalRef.h"
@@ -46,6 +46,8 @@
#include "UniquePtr.h"
#undef WITH_JNI_TRACE
+#undef WITH_JNI_TRACE_DATA
+
#ifdef WITH_JNI_TRACE
#define JNI_TRACE(...) \
((void)LOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__)); \
@@ -57,6 +59,8 @@
#else
#define JNI_TRACE(...) ((void)0)
#endif
+// don't overwhelm logcat
+#define WITH_JNI_TRACE_DATA_CHUNK_SIZE 512
struct BIO_Delete {
void operator()(BIO* p) const {
@@ -86,6 +90,13 @@ struct DSA_Delete {
};
typedef UniquePtr<DSA, DSA_Delete> Unique_DSA;
+struct EC_KEY_Delete {
+ void operator()(EC_KEY* p) const {
+ EC_KEY_free(p);
+ }
+};
+typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY;
+
struct EVP_PKEY_Delete {
void operator()(EVP_PKEY* p) const {
EVP_PKEY_free(p);
@@ -619,19 +630,19 @@ static jint NativeCrypto_EVP_MD_CTX_copy(JNIEnv* env, jclass, EVP_MD_CTX* ctx) {
if (ctx == NULL) {
jniThrowNullPointerException(env, NULL);
- return NULL;
+ return 0;
}
EVP_MD_CTX* copy = EVP_MD_CTX_create();
if (copy == NULL) {
jniThrowOutOfMemoryError(env, "Unable to allocate copy of EVP_MD_CTX");
- return NULL;
+ return 0;
}
EVP_MD_CTX_init(copy);
int result = EVP_MD_CTX_copy_ex(copy, ctx);
if (result == 0) {
EVP_MD_CTX_destroy(copy);
jniThrowRuntimeException(env, "Unable to copy EVP_MD_CTX");
- return NULL;
+ return 0;
}
JNI_TRACE("NativeCrypto_EVP_MD_CTX_copy(%p) => %p", ctx, copy);
return (jint) copy;
@@ -987,72 +998,6 @@ static int NativeCrypto_RAND_load_file(JNIEnv* env, jclass, jstring filename, jl
return result;
}
-/**
- * Convert ssl version constant to string. Based on SSL_get_version
- */
-// TODO move to jsse.patch
-static const char* get_ssl_version(int ssl_version) {
- switch (ssl_version) {
- // newest to oldest
- case TLS1_VERSION: {
- return SSL_TXT_TLSV1;
- }
- case SSL3_VERSION: {
- return SSL_TXT_SSLV3;
- }
- case SSL2_VERSION: {
- return SSL_TXT_SSLV2;
- }
- default: {
- return "unknown";
- }
- }
-}
-
-#ifdef WITH_JNI_TRACE
-/**
- * Convert content type constant to string.
- */
-// TODO move to jsse.patch
-static const char* get_content_type(int content_type) {
- switch (content_type) {
- case SSL3_RT_CHANGE_CIPHER_SPEC: {
- return "SSL3_RT_CHANGE_CIPHER_SPEC";
- }
- case SSL3_RT_ALERT: {
- return "SSL3_RT_ALERT";
- }
- case SSL3_RT_HANDSHAKE: {
- return "SSL3_RT_HANDSHAKE";
- }
- case SSL3_RT_APPLICATION_DATA: {
- return "SSL3_RT_APPLICATION_DATA";
- }
- default: {
- LOGD("Unknown TLS/SSL content type %d", content_type);
- return "<unknown>";
- }
- }
-}
-#endif
-
-#ifdef WITH_JNI_TRACE
-/**
- * Simple logging call back to show hand shake messages
- */
-static void ssl_msg_callback_LOG(int write_p, int ssl_version, int content_type,
- const void* buf, size_t len, SSL* ssl, void* arg) {
- JNI_TRACE("ssl=%p SSL msg %s %s %s %p %d %p",
- ssl,
- (write_p) ? "send" : "recv",
- get_ssl_version(ssl_version),
- get_content_type(content_type),
- buf,
- len,
- arg);
-}
-#endif
-
#ifdef WITH_JNI_TRACE
/**
* Based on example logging call back from SSL_CTX_set_info_callback man page
@@ -1259,11 +1204,14 @@ static jobjectArray getPrincipalBytes(JNIEnv* env, const STACK_OF(X509_NAME)* na
* the JNIEnv on calls that can read and write to the SSL such as
* SSL_do_handshake, SSL_read, SSL_write, and SSL_shutdown.
*
- * Finally, we have one other piece of state setup by OpenSSL callbacks:
+ * Finally, we have two emphemeral keys setup by OpenSSL callbacks:
*
* (8) a set of ephemeral RSA keys that is lazily generated if a peer
* wants to use an exportable RSA cipher suite.
*
+ * (9) a set of ephemeral EC keys that is lazily generated if a peer
+ * wants to use an TLS_ECDHE_* cipher suite.
+ *
*/
class AppData {
public:
@@ -1275,6 +1223,7 @@ class AppData {
jobject sslHandshakeCallbacks;
jobject fileDescriptor;
Unique_RSA ephemeralRsa;
+ Unique_EC_KEY ephemeralEc;
/**
* Creates the application data context for the SSL*.
@@ -1285,6 +1234,9 @@ class AppData {
if (pipe(appData.get()->fdsEmergency) == -1) {
return NULL;
}
+ if (!setBlocking(appData.get()->fdsEmergency[0], false)) {
+ return NULL;
+ }
if (MUTEX_SETUP(appData.get()->mutex) == -1) {
return NULL;
}
@@ -1308,7 +1260,8 @@ class AppData {
waitingThreads(0),
env(NULL),
sslHandshakeCallbacks(NULL),
- ephemeralRsa(NULL) {
+ ephemeralRsa(NULL),
+ ephemeralEc(NULL) {
fdsEmergency[0] = -1;
fdsEmergency[1] = -1;
}
@@ -1345,7 +1298,7 @@ class AppData {
/**
* Dark magic helper function that checks, for a given SSL session, whether it
* can SSL_read() or SSL_write() without blocking. Takes into account any
- * concurrent attempts to close the SSL session from the Java side. This is
+ * concurrent attempts to close the SSLSocket from the Java side. This is
* needed to get rid of the hangs that occur when thread #1 closes the SSLSocket
* while thread #2 is sitting in a blocking read or write. The type argument
* specifies whether we are waiting for readability or writability. It expects
@@ -1393,7 +1346,7 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData,
FD_SET(appData->fdsEmergency[0], &rfds);
- int max = intFd > appData->fdsEmergency[0] ? intFd : appData->fdsEmergency[0];
+ int maxFd = (intFd > appData->fdsEmergency[0]) ? intFd : appData->fdsEmergency[0];
// Build a struct for the timeout data if we actually want a timeout.
timeval tv;
@@ -1406,32 +1359,34 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData,
ptv = NULL;
}
- {
- AsynchronousSocketCloseMonitor monitor(intFd);
- result = select(max + 1, &rfds, &wfds, NULL, ptv);
- JNI_TRACE("sslSelect %s fd=%d appData=%p timeout=%d => %d",
- (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE",
- fd.get(), appData, timeout, result);
- if (result == -1) {
- if (fd.isClosed()) {
- result = THROWN_SOCKETEXCEPTION;
- break;
- }
- if (errno != EINTR) {
- break;
- }
+ AsynchronousSocketCloseMonitor monitor(intFd);
+ result = select(maxFd + 1, &rfds, &wfds, NULL, ptv);
+ JNI_TRACE("sslSelect %s fd=%d appData=%p timeout=%d => %d",
+ (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE",
+ fd.get(), appData, timeout, result);
+ if (result == -1) {
+ if (fd.isClosed()) {
+ result = THROWN_SOCKETEXCEPTION;
+ break;
+ }
+ if (errno != EINTR) {
+ break;
}
}
} while (result == -1);
- // Lock
if (MUTEX_LOCK(appData->mutex) == -1) {
return -1;
}
if (result > 0) {
- // If we have been woken up by the emergency pipe, there must be a token in
- // it. Thus we can safely read it (even in a blocking way).
+ // We have been woken up by a token in the emergency pipe. We
+ // can't be sure the token is still in the pipe at this point
+ // because it could have already been read by the thread that
+ // originally wrote it if it entered sslSelect and acquired
+ // the mutex before we did. Thus we cannot safely read from
+ // the pipe in a blocking way (so we make the pipe
+ // non-blocking at creation).
if (FD_ISSET(appData->fdsEmergency[0], &rfds)) {
char token;
do {
@@ -1444,7 +1399,6 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData,
// underlying network.
appData->waitingThreads--;
- // Unlock
MUTEX_UNLOCK(appData->mutex);
return result;
@@ -1470,76 +1424,6 @@ static void sslNotify(AppData* appData) {
errno = errnoBackup;
}
-// From private header file external/openssl/ssl_locl.h
-// TODO move dependent code to jsse.patch to avoid dependency
-#define SSL_aRSA 0x00000001L
-#define SSL_aDSS 0x00000002L
-#define SSL_aNULL 0x00000004L
-#define SSL_aDH 0x00000008L
-#define SSL_aECDH 0x00000010L
-#define SSL_aKRB5 0x00000020L
-#define SSL_aECDSA 0x00000040L
-#define SSL_aPSK 0x00000080L
-
-/**
- * Converts an SSL_CIPHER's algorithms field to a TrustManager auth argument
- */
-// TODO move to jsse.patch
-static const char* SSL_CIPHER_authentication_method(const SSL_CIPHER* cipher)
-{
- unsigned long alg_auth = cipher->algorithm_auth;
-
- const char* au;
- switch (alg_auth) {
- case SSL_aRSA:
- au="RSA";
- break;
- case SSL_aDSS:
- au="DSS";
- break;
- case SSL_aDH:
- au="DH";
- break;
- case SSL_aKRB5:
- au="KRB5";
- break;
- case SSL_aECDH:
- au = "ECDH";
- break;
- case SSL_aNULL:
- au="None";
- break;
- case SSL_aECDSA:
- au="ECDSA";
- break;
- case SSL_aPSK:
- au="PSK";
- break;
- default:
- au="unknown";
- break;
- }
- return au;
-}
-
-/**
- * Converts an SSL_CIPHER's algorithms field to a TrustManager auth argument
- */
-// TODO move to jsse.patch
-static const char* SSL_authentication_method(SSL* ssl)
-{
- switch (ssl->version) {
- case SSL2_VERSION:
- return "RSA";
- case SSL3_VERSION:
- case TLS1_VERSION:
- case DTLS1_VERSION:
- return SSL_CIPHER_authentication_method(ssl->s3->tmp.new_cipher);
- default:
- return "unknown";
- }
-}
-
static AppData* toAppData(const SSL* ssl) {
return reinterpret_cast<AppData*>(SSL_get_app_data(ssl));
}
@@ -1782,6 +1666,31 @@ static DH* tmp_dh_callback(SSL* ssl __attribute__ ((unused)),
return tmp_dh;
}
+static EC_KEY* ecGenerateKey(int keylength __attribute__ ((unused))) {
+ // TODO selected curve based on keylength
+ Unique_EC_KEY ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (ec.get() == NULL) {
+ return NULL;
+ }
+ return ec.release();
+}
+
+/**
+ * Call back to ask for an ephemeral EC key for TLS_ECDHE_* cipher suites
+ */
+static EC_KEY* tmp_ecdh_callback(SSL* ssl __attribute__ ((unused)),
+ int is_export __attribute__ ((unused)),
+ int keylength) {
+ JNI_TRACE("ssl=%p tmp_ecdh_callback is_export=%d keylength=%d", ssl, is_export, keylength);
+ AppData* appData = toAppData(ssl);
+ if (appData->ephemeralEc.get() == NULL) {
+ JNI_TRACE("ssl=%p tmp_ecdh_callback generating ephemeral EC key", ssl);
+ appData->ephemeralEc.reset(ecGenerateKey(keylength));
+ }
+ JNI_TRACE("ssl=%p tmp_ecdh_callback => %p", ssl, appData->ephemeralEc.get());
+ return appData->ephemeralEc.get();
+}
+
/*
* public static native int SSL_CTX_new();
*/
@@ -1789,7 +1698,7 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) {
Unique_SSL_CTX sslCtx(SSL_CTX_new(SSLv23_method()));
if (sslCtx.get() == NULL) {
jniThrowRuntimeException(env, "SSL_CTX_new");
- return NULL;
+ return 0;
}
SSL_CTX_set_options(sslCtx.get(),
SSL_OP_ALL
@@ -1800,7 +1709,9 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) {
// We also disable compression for better compatibility b/2710492 b/2710497
| SSL_OP_NO_COMPRESSION
// Because dhGenerateParameters uses DSA_generate_parameters_ex
- | SSL_OP_SINGLE_DH_USE);
+ | SSL_OP_SINGLE_DH_USE
+ // Because ecGenerateParameters uses a fixed named curve
+ | SSL_OP_SINGLE_ECDH_USE);
int mode = SSL_CTX_get_mode(sslCtx.get());
/*
@@ -1828,10 +1739,8 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) {
SSL_CTX_set_client_cert_cb(sslCtx.get(), client_cert_cb);
SSL_CTX_set_tmp_rsa_callback(sslCtx.get(), tmp_rsa_callback);
SSL_CTX_set_tmp_dh_callback(sslCtx.get(), tmp_dh_callback);
+ SSL_CTX_set_tmp_ecdh_callback(sslCtx.get(), tmp_ecdh_callback);
-#ifdef WITH_JNI_TRACE
- SSL_CTX_set_msg_callback(sslCtx.get(), ssl_msg_callback_LOG); /* enable for message debug */
-#endif
JNI_TRACE("NativeCrypto_SSL_CTX_new => %p", sslCtx.get());
return (jint) sslCtx.release();
}
@@ -1858,14 +1767,14 @@ static jint NativeCrypto_SSL_new(JNIEnv* env, jclass, jint ssl_ctx_address)
SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new", ssl_ctx);
if (ssl_ctx == NULL) {
- return NULL;
+ return 0;
}
Unique_SSL ssl(SSL_new(ssl_ctx));
if (ssl.get() == NULL) {
throwSSLExceptionWithSslErrors(env, NULL, SSL_ERROR_NONE,
"Unable to create SSL structure");
JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
- return NULL;
+ return 0;
}
/* Java code in class OpenSSLSocketImpl does the verification. Meaning of
@@ -1917,6 +1826,8 @@ static void NativeCrypto_SSL_use_PrivateKey(JNIEnv* env, jclass,
return;
}
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_use_PrivateKey EVP_PKEY_type=%d",
+ ssl, EVP_PKEY_type(privatekeyevp.get()->type));
int ret = SSL_use_PrivateKey(ssl, privatekeyevp.get());
if (ret == 1) {
privatekeyevp.release();
@@ -2323,7 +2234,8 @@ static void NativeCrypto_SSL_set_tlsext_host_name(JNIEnv* env, jclass,
if (hostnameChars.c_str() == NULL) {
return;
}
- JNI_TRACE("NativeCrypto_SSL_set_tlsext_host_name hostnameChars=%s", hostnameChars.c_str());
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_tlsext_host_name hostnameChars=%s",
+ ssl, hostnameChars.c_str());
int ret = SSL_set_tlsext_host_name(ssl, hostnameChars.c_str());
if (ret != 1) {
@@ -2421,6 +2333,7 @@ static jint NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass,
ret = 0;
while (appData->aliveAndKicking) {
errno = 0;
+
if (!appData->setCallbackState(env, shc, fdObject)) {
// SocketException thrown by NetFd.isClosed
SSL_clear(ssl);
@@ -2635,8 +2548,7 @@ static jobjectArray NativeCrypto_SSL_get_peer_cert_chain(JNIEnv* env, jclass, ji
*/
static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* buf, jint len,
int* sslReturnCode, int* sslErrorCode, int timeout) {
-
- // LOGD("Entering sslRead, caller requests to read %d bytes...", len);
+ JNI_TRACE("ssl=%p sslRead buf=%p len=%d", ssl, buf, len);
if (len == 0) {
// Don't bother doing anything in this case.
@@ -2653,14 +2565,12 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b
while (appData->aliveAndKicking) {
errno = 0;
- // Lock
if (MUTEX_LOCK(appData->mutex) == -1) {
return -1;
}
unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
- // LOGD("Doing SSL_Read()");
if (!appData->setCallbackState(env, shc, fdObject)) {
MUTEX_UNLOCK(appData->mutex);
return THROWN_SOCKETEXCEPTION;
@@ -2672,7 +2582,13 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b
sslError = SSL_get_error(ssl, result);
freeSslErrorState();
}
- // LOGD("Returned from SSL_Read() with result %d, error code %d", result, sslError);
+ JNI_TRACE("ssl=%p sslRead 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 = std::min(result - i, WITH_JNI_TRACE_DATA_CHUNK_SIZE);
+ JNI_TRACE("ssl=%p sslRead data: %d:\n%*s", ssl, n, n, buf+i);
+ }
+#endif
// 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
@@ -2688,7 +2604,6 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b
appData->waitingThreads++;
}
- // Unlock
MUTEX_UNLOCK(appData->mutex);
switch (sslError) {
@@ -2882,8 +2797,7 @@ static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jobject
*/
static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const char* buf, jint len,
int* sslReturnCode, int* sslErrorCode) {
-
- // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
+ JNI_TRACE("ssl=%p sslWrite buf=%p len=%d", ssl, buf, len);
if (len == 0) {
// Don't bother doing anything in this case.
@@ -2901,6 +2815,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const
while (appData->aliveAndKicking && len > 0) {
errno = 0;
+
if (MUTEX_LOCK(appData->mutex) == -1) {
return -1;
}
@@ -2919,7 +2834,13 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const
sslError = SSL_get_error(ssl, result);
freeSslErrorState();
}
- // LOGD("Returned from SSL_write() with result %d, error code %d", result, error);
+ JNI_TRACE("ssl=%p sslWrite SSL_write 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 = std::min(result - i, WITH_JNI_TRACE_DATA_CHUNK_SIZE);
+ JNI_TRACE("ssl=%p sslWrite data: %d:\n%*s", ssl, n, n, buf+i);
+ }
+#endif
// 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
@@ -2938,7 +2859,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const
MUTEX_UNLOCK(appData->mutex);
switch (sslError) {
- // Successfully write at least one byte.
+ // Successfully wrote at least one byte.
case SSL_ERROR_NONE: {
buf += result;
len -= result;
@@ -2971,7 +2892,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const
break;
}
- // An problem occurred during a system call, but this is not
+ // 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
@@ -2997,7 +2918,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const
}
}
}
- // LOGD("Successfully wrote %d bytes", count);
+ JNI_TRACE("ssl=%p sslWrite => count=%d", ssl, count);
return count;
}
@@ -3248,16 +3169,6 @@ static jlong NativeCrypto_SSL_SESSION_get_time(JNIEnv* env, jclass, jint ssl_ses
}
/**
- * Our implementation of what might be considered
- * SSL_SESSION_get_version, based on SSL_get_version.
- * See get_ssl_version above.
- */
-// TODO move to jsse.patch
-static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) {
- return get_ssl_version(ssl_session->ssl_version);
-}
-
-/**
* Gets and returns in a string the version of the SSL protocol. If it
* returns the string "unknown" it means that no connection is established.
*/