From 372ef97746c5ef0fff19caa414485ba34d03401f Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Mon, 21 Oct 2024 13:21:12 -0600 Subject: [PATCH] JSSE: add initial SSLEngine support for DTLSv1.3 --- README.md | 7 +- native/com_wolfssl_WolfSSL.c | 108 ++++---- native/com_wolfssl_WolfSSL.h | 12 + native/com_wolfssl_WolfSSLSession.c | 131 +++++---- native/com_wolfssl_WolfSSLSession.h | 32 +++ src/java/com/wolfssl/WolfSSL.java | 11 + src/java/com/wolfssl/WolfSSLSession.java | 129 ++++++++- .../wolfssl/provider/jsse/WolfSSLContext.java | 24 +- .../wolfssl/provider/jsse/WolfSSLEngine.java | 249 ++++++++++++++---- .../provider/jsse/WolfSSLEngineHelper.java | 52 +++- .../provider/jsse/WolfSSLParameters.java | 11 + .../jsse/WolfSSLParametersHelper.java | 70 ++++- .../provider/jsse/WolfSSLProvider.java | 4 + .../provider/jsse/WolfSSLServerSocket.java | 9 +- .../wolfssl/provider/jsse/WolfSSLUtil.java | 36 +-- .../provider/jsse/test/WolfSSLEngineTest.java | 48 +++- 16 files changed, 728 insertions(+), 205 deletions(-) diff --git a/README.md b/README.md index 9facb71..d79076c 100644 --- a/README.md +++ b/README.md @@ -373,9 +373,12 @@ Additional instructions can be found on the wolfSSL.com website: ### JSSE Class Implementation Support -wolfJSSE extends or implements the following JSSE classes: +wolfJSSE extends or implements the following JSSE classes. Note that +SSLContext `DTLSv1.3` support is only supported through the `SSLEngine` +interface. + - javax.net.ssl.SSLContextSpi - - SSL, TLS, DEFAULT, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 + - SSL, TLS, DEFAULT, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3, DTLSv1.3 - javax.net.ssl.KeyManagerFactorySpi - PKIX, X509, SunX509 - javax.net.ssl.TrustManagerFactorySpi diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c index d5a4b5b..4de663d 100644 --- a/native/com_wolfssl_WolfSSL.c +++ b/native/com_wolfssl_WolfSSL.c @@ -530,6 +530,19 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_TLSv13Enabled #endif } +JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_DTLSv13Enabled + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_ShaEnabled (JNIEnv* jenv, jclass jcl) { @@ -1996,41 +2009,43 @@ JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_WolfSSL_getProtocolsMask (void)jcl; - /* get the number of protocols enabled */ + /* Get the number of protocols enabled, based on provided mask. Native + * wolfSSL doesn't have mask values for DTLS, so we lump them together + * with their corresponding TLS version, if correct defines are set. */ #ifdef WOLFSSL_TLS13 - if(!(mask & SSL_OP_NO_TLSv1_3)) + if(!(mask & SSL_OP_NO_TLSv1_3)) { numProtocols += 1; + #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) + numProtocols += 1; + #endif + } #endif #ifndef WOLFSSL_NO_TLS12 - if(!(mask & SSL_OP_NO_TLSv1_2)) + if(!(mask & SSL_OP_NO_TLSv1_2)) { numProtocols += 1; + #if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) + numProtocols += 1; + #endif + } #endif #ifndef NO_OLD_TLS - if(!(mask & SSL_OP_NO_TLSv1_1)) + if(!(mask & SSL_OP_NO_TLSv1_1)) { numProtocols += 1; + } #ifdef WOLFSSL_ALLOW_TLSV10 - if(!(mask & SSL_OP_NO_TLSv1)) + if(!(mask & SSL_OP_NO_TLSv1)) { numProtocols += 1; + #ifdef WOLFSSL_DTLS + numProtocols += 1; + #endif + } #endif /* WOLFSSL_ALLOW_TLSv10 */ #endif /* !NO_OLD_TLS */ #ifdef WOLFSSL_ALLOW_SSLv3 - if(!(mask & SSL_OP_NO_SSLv3)) + if(!(mask & SSL_OP_NO_SSLv3)) { numProtocols += 1; + } #endif -#ifdef WOLFSSL_DTLS - #ifndef NO_OLD_TLS - /* DTLS 1.0 */ - numProtocols += 1; - #endif - #ifndef WOLFSSL_NO_TLS12 - /* DTLS 1.2 */ - numProtocols += 1; - #endif - #ifdef WOLFSSL_DTLS13 - /* DTLS 1.3 */ - numProtocols += 1; - #endif -#endif /* WOLFSSL_DTLS */ ret = (*jenv)->NewObjectArray(jenv, numProtocols, (*jenv)->FindClass(jenv, "java/lang/String"), NULL); @@ -2104,36 +2119,39 @@ JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_WolfSSL_getProtocolsMask #ifdef WOLFSSL_DTLS #ifndef NO_OLD_TLS - /* DTLS 1.0 */ - (*jenv)->SetObjectArrayElement(jenv, ret, idx++, - (*jenv)->NewStringUTF(jenv, "DTLSv1")); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - (*jenv)->ThrowNew(jenv, jcl, "Error setting DTLSv1 string"); - return NULL; + if(!(mask & SSL_OP_NO_TLSv1)) { + (*jenv)->SetObjectArrayElement(jenv, ret, idx++, + (*jenv)->NewStringUTF(jenv, "DTLSv1")); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->ThrowNew(jenv, jcl, "Error setting DTLSv1 string"); + return NULL; + } } #endif #ifndef WOLFSSL_NO_TLS12 - /* DTLS 1.2 */ - (*jenv)->SetObjectArrayElement(jenv, ret, idx++, - (*jenv)->NewStringUTF(jenv, "DTLSv1.2")); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - (*jenv)->ThrowNew(jenv, jcl, "Error setting DTLSv1.2 string"); - return NULL; + if(!(mask & SSL_OP_NO_TLSv1_2)) { + (*jenv)->SetObjectArrayElement(jenv, ret, idx++, + (*jenv)->NewStringUTF(jenv, "DTLSv1.2")); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->ThrowNew(jenv, jcl, "Error setting DTLSv1.2 string"); + return NULL; + } } #endif - #ifdef WOLFSSL_DTLS13 - /* DTLS 1.3 */ - (*jenv)->SetObjectArrayElement(jenv, ret, idx++, - (*jenv)->NewStringUTF(jenv, "DTLSv1.3")); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - (*jenv)->ThrowNew(jenv, jcl, "Error setting DTLSv1.3 string"); - return NULL; + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13) + if(!(mask & SSL_OP_NO_TLSv1_3)) { + (*jenv)->SetObjectArrayElement(jenv, ret, idx++, + (*jenv)->NewStringUTF(jenv, "DTLSv1.3")); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->ThrowNew(jenv, jcl, "Error setting DTLSv1.3 string"); + return NULL; + } } #endif #endif diff --git a/native/com_wolfssl_WolfSSL.h b/native/com_wolfssl_WolfSSL.h index 2c5ce87..a052493 100644 --- a/native/com_wolfssl_WolfSSL.h +++ b/native/com_wolfssl_WolfSSL.h @@ -93,10 +93,14 @@ extern "C" { #define com_wolfssl_WolfSSL_SOCKET_ERROR_E -308L #undef com_wolfssl_WolfSSL_FATAL_ERROR #define com_wolfssl_WolfSSL_FATAL_ERROR -313L +#undef com_wolfssl_WolfSSL_OUT_OF_ORDER_E +#define com_wolfssl_WolfSSL_OUT_OF_ORDER_E -373L #undef com_wolfssl_WolfSSL_SSL_ERROR_SOCKET_PEER_CLOSED #define com_wolfssl_WolfSSL_SSL_ERROR_SOCKET_PEER_CLOSED -397L #undef com_wolfssl_WolfSSL_UNKNOWN_ALPN_PROTOCOL_NAME_E #define com_wolfssl_WolfSSL_UNKNOWN_ALPN_PROTOCOL_NAME_E -405L +#undef com_wolfssl_WolfSSL_APP_DATA_READY +#define com_wolfssl_WolfSSL_APP_DATA_READY -441L #undef com_wolfssl_WolfSSL_WOLFSSL_CRL_CHECKALL #define com_wolfssl_WolfSSL_WOLFSSL_CRL_CHECKALL 1L #undef com_wolfssl_WolfSSL_WOLFSSL_OCSP_URL_OVERRIDE @@ -597,6 +601,14 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_TLSv12Enabled JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_TLSv13Enabled (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: DTLSv13Enabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_DTLSv13Enabled + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: ShaEnabled diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c index 3e88820..fac9ea7 100644 --- a/native/com_wolfssl_WolfSSLSession.c +++ b/native/com_wolfssl_WolfSSLSession.c @@ -1568,6 +1568,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read__JLjava_nio_ByteBuff return size; } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_pending + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jcl; + + /* Checks ssl for NULL internally, will return WOLFSSL_FAILURE */ + return (jint)wolfSSL_pending(ssl); +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept (JNIEnv* jenv, jobject jcl, jlong sslPtr, jint timeout) { @@ -2414,20 +2424,11 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGotTimeout (JNIEnv* jenv, jobject jcl, jlong sslPtr) { #if !defined(WOLFSSL_LEANPSK) && defined(WOLFSSL_DTLS) - jclass excClass; WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jenv; (void)jcl; if (ssl == NULL) { - excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLException"); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - return SSL_FATAL_ERROR; - } - (*jenv)->ThrowNew(jenv, excClass, - "Input WolfSSLSession object was null in " - "dtlsGotTimeout()"); return SSL_FATAL_ERROR; } @@ -2440,22 +2441,35 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGotTimeout #endif } -JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtls +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsRetransmit (JNIEnv* jenv, jobject jcl, jlong sslPtr) { - jclass excClass; +#if !defined(WOLFSSL_LEANPSK) && defined(WOLFSSL_DTLS) WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jenv; + (void)jcl; + + if (ssl == NULL) { + return SSL_FATAL_ERROR; + } + + return wolfSSL_dtls_retransmit(ssl); +#else + (void)jenv; + (void)jcl; + (void)sslPtr; + return NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtls + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jenv; (void)jcl; if (ssl == NULL) { - excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLException"); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - return 0; - } - (*jenv)->ThrowNew(jenv, excClass, - "Input WolfSSLSession object was null in dtls()"); return 0; } @@ -2694,6 +2708,51 @@ JNIEXPORT jobject JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGetPeer } } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setMTU + (JNIEnv* jenv, jobject jcl, jlong sslPtr, jint mtu) +{ +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_MTU) + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + + /* wolfSSL_dtls_set_mtu() checks ssl for NULL */ + return (jint)wolfSSL_dtls_set_mtu(ssl, (unsigned short)mtu); +#else + (void)jenv; + (void)jcl; + (void)sslPtr; + (void)mtu; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLSession_stateStringLong + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ +#ifdef OPENSSL_EXTRA + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + const char* stateString; + jstring stateStr = NULL; + (void)jcl; + + if (jenv == NULL || ssl == NULL) { + return NULL; + } + + stateString = wolfSSL_state_string_long(ssl); + + if (stateString != NULL) { + stateStr = (*jenv)->NewStringUTF(jenv, stateString); + } + + return stateStr; +#else + (void)jenv; + (void)jcl; + (void)sslPtr; + return NULL; +#endif +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_sessionReused (JNIEnv* jenv, jobject jcl, jlong sslPtr) { @@ -4681,9 +4740,8 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSLSession_handshakeDone if (wolfSSL_is_init_finished(ssl)) { return JNI_TRUE; } - else { - return JNI_FALSE; - } + + return JNI_FALSE; } JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setConnectState @@ -5777,24 +5835,12 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setSSLIORecv (JNIEnv* jenv, jobject jcl, jlong sslPtr) { WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jenv; (void)jcl; - /* find exception class */ - jclass excClass = (*jenv)->FindClass(jenv, - "com/wolfssl/WolfSSLJNIException"); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - return; - } - if (ssl != NULL) { /* set I/O recv callback */ wolfSSL_SSLSetIORecv(ssl, NativeSSLIORecvCb); - - } else { - (*jenv)->ThrowNew(jenv, excClass, - "Input WolfSSLContext object was null when setting IORecv"); } } @@ -5931,23 +5977,12 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setSSLIOSend (JNIEnv* jenv, jobject jcl, jlong sslPtr) { WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; - jclass excClass = NULL; + (void)jenv; (void)jcl; - /* find exception class in case we need it */ - excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException"); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - } - if (ssl != NULL) { /* set I/O send callback */ wolfSSL_SSLSetIOSend(ssl, NativeSSLIOSendCb); - - } else { - (*jenv)->ThrowNew(jenv, excClass, - "Input WolfSSLContext object was null when setting IOSend"); } } diff --git a/native/com_wolfssl_WolfSSLSession.h b/native/com_wolfssl_WolfSSLSession.h index 260032c..7c7e8eb 100644 --- a/native/com_wolfssl_WolfSSLSession.h +++ b/native/com_wolfssl_WolfSSLSession.h @@ -111,6 +111,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read__J_3BIII JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read__JLjava_nio_ByteBuffer_2II (JNIEnv *, jobject, jlong, jobject, jint, jint); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: pending + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_pending + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLSession * Method: accept @@ -279,6 +287,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGetCurrentTimeout JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGotTimeout (JNIEnv *, jobject, jlong); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: dtlsRetransmit + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsRetransmit + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLSession * Method: dtls @@ -895,6 +911,22 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_interruptBlockedIO JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getThreadsBlockedInPoll (JNIEnv *, jobject, jlong); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: setMTU + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setMTU + (JNIEnv *, jobject, jlong, jint); + +/* + * Class: com_wolfssl_WolfSSLSession + * Method: stateStringLong + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLSession_stateStringLong + (JNIEnv *, jobject, jlong); + #ifdef __cplusplus } #endif diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index 28b1e05..7f76971 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -214,10 +214,14 @@ public class WolfSSL { public static final int SOCKET_ERROR_E = -308; /** Received fatal alert error */ public static final int FATAL_ERROR = -313; + /** Out of order message */ + public static final int OUT_OF_ORDER_E = -373; /** Peer closed socket */ public static final int SSL_ERROR_SOCKET_PEER_CLOSED = -397; /** Unrecognized ALPN protocol name */ public static final int UNKNOWN_ALPN_PROTOCOL_NAME_E = -405; + /** DTLS application data ready for read */ + public static final int APP_DATA_READY = -441; /* extra definitions from ssl.h */ /** CertManager: check all cert CRLs */ @@ -844,6 +848,13 @@ public class WolfSSL { */ public static native boolean TLSv13Enabled(); + /** + * Tests if DTLS 1.3 has been compiled into the native wolfSSL library. + * + * @return true if enabled, otherwise false if not compiled in. + */ + public static native boolean DTLSv13Enabled(); + /** * Tests if SHA-1 is enabled in the native wolfSSL library. * diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java index d5a996f..22f1ed0 100644 --- a/src/java/com/wolfssl/WolfSSLSession.java +++ b/src/java/com/wolfssl/WolfSSLSession.java @@ -316,6 +316,7 @@ public class WolfSSLSession { int timeout); private native int read(long ssl, ByteBuffer data, int sz, int timeout) throws WolfSSLException; + private native int pending(long ssl); private native int accept(long ssl, int timeout); private native void freeSSL(long ssl); private native int shutdownSSL(long ssl, int timeout); @@ -337,6 +338,7 @@ public class WolfSSLSession { private native int setCipherList(long ssl, String list); private native int dtlsGetCurrentTimeout(long ssl); private native int dtlsGotTimeout(long ssl); + private native int dtlsRetransmit(long ssl); private native int dtls(long ssl); private native int dtlsSetPeer(long ssl, InetSocketAddress peer); private native InetSocketAddress dtlsGetPeer(long ssl); @@ -419,6 +421,8 @@ public class WolfSSLSession { private native int hasTicket(long session); private native int interruptBlockedIO(long ssl); private native int getThreadsBlockedInPoll(long ssl); + private native int setMTU(long ssl, int mtu); + private native String stateStringLong(long ssl); /* ------------------- session-specific methods --------------------- */ @@ -1139,7 +1143,7 @@ public class WolfSSLSession { * read() again. Use getError to * get a specific error code. * BAD_FUNC_ARC when bad arguments are used. - * @throws IllegalStateException WolfSSLContext has been freed + * @throws IllegalStateException WolfSSLSession has been freed * @throws SocketTimeoutException if socket timeout occurs * @throws SocketException Native socket select/poll() failed */ @@ -1257,6 +1261,29 @@ public class WolfSSLSession { return ret; } + /** + * Return number of bytes available on the internal output buffer that + * have already been decrypted and available immediately via a call to + * read(). + * + * @return number of bytes available on success, WolfSSL.SSL_FAILURE + * on error. + * + * @throws IllegalStateException WolfSSLSession has been freed + */ + public int pending() throws IllegalStateException { + + int ret = 0; + + confirmObjectIsActive(); + + synchronized (sslLock) { + ret = pending(this.sslPtr); + } + + return ret; + } + /** * Waits for an SSL client to initiate the SSL/TLS handshake. * This method is called on the server side. When it is called, the @@ -2260,14 +2287,12 @@ public class WolfSSLSession { * the peer. NOT_COMPILED_IN if wolfSSL was * not compiled with DTLS support. * @throws IllegalStateException WolfSSLContext has been freed - * @throws WolfSSLJNIException Internal JNI error * @see #dtlsGetCurrentTimeout() * @see #dtlsGetPeer() * @see #dtlsSetPeer(InetSocketAddress) * @see #dtls() */ - public int dtlsGotTimeout() - throws IllegalStateException, WolfSSLJNIException { + public int dtlsGotTimeout() throws IllegalStateException { confirmObjectIsActive(); @@ -2279,20 +2304,47 @@ public class WolfSSLSession { } } + /** + * When using non-blocking sockets with DTLS, this function retransmits + * the last handshake flight ignoring the expected timeout value and + * retransmit count. + * + * It is useful for applications that are using DTLS and need to manage + * even the timeout and retry count. + * + * @return SSL_SUCCESS upon success. + * SSL_FATAL_ERROR if there have been too many + * retransmissions/timeouts without getting a response from + * the peer. NOT_COMPILED_IN if wolfSSL was + * not compiled with DTLS support. + * @throws IllegalStateException WolfSSLContext has been freed + * @see #dtlsGetCurrentTimeout() + * @see #dtlsGetPeer() + * @see #dtlsSetPeer(InetSocketAddress) + * @see #dtlsGotTimeout() + * @see #dtls() + */ + public int dtlsRetransmit() throws IllegalStateException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return dtlsRetransmit(this.sslPtr); + } + } + /** * Used to determine if the SSL session has been configured to use DTLS. * * @return 1 if the SSL has been configured to use DTLS, * otherwise, 0. * @throws IllegalStateException WolfSSLContext has been freed - * @throws WolfSSLJNIException Internal JNI error * @see #dtlsGetCurrentTimeout() * @see #dtlsGetPeer() * @see #dtlsGotTimeout() * @see #dtlsSetPeer(InetSocketAddress) */ - public int dtls() - throws IllegalStateException, WolfSSLJNIException { + public int dtls() throws IllegalStateException { confirmObjectIsActive(); @@ -2355,6 +2407,57 @@ public class WolfSSLSession { } } + /** + * Set the DTLS MTU to use. + * + * Native wolfSSL must be compiled with "--enable-dtls-mtu" in addition + * to "--enable-dtls". + * + * @param mtu DTLS MTU value, must be lower than MAX_RECORD_SIZE (16k) + * + * @return SSL_SUCCESS on success, + * SSL_FAILURE on error, or + * NOT_COMPILED_IN if native support has not + * been compiled into native wolfSSL. + * + * @throws IllegalStateException WolfSSLContext has been freed + */ + public int dtlsSetMTU(int mtu) throws IllegalStateException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return setMTU(this.sslPtr, mtu); + } + } + + /** + * Return the current handshake state of the native WOLFSSL object as + * a String. + * + * @return String representing current state of handshake or empty + * String if not able to retrieve one. + * + * @throws IllegalStateException WolfSSLContext has been freed + */ + public String getStateStringLong() throws IllegalStateException { + + String state = null; + + confirmObjectIsActive(); + + synchronized (sslLock) { + state = stateStringLong(this.sslPtr); + if (state == null) { + /* Return empty string instead of null, may prevent some + * NullPoinerExceptions from callers */ + state = ""; + } + } + + return state; + } + /** * Determine if a reused session was negotiated during the SSL * handshake. @@ -4168,8 +4271,10 @@ public class WolfSSLSession { /* set user I/O recv */ internRecvSSLCb = callback; - /* register internal callback with native library */ - setSSLIORecv(this.sslPtr); + if (callback != null) { + /* register internal callback with native library */ + setSSLIORecv(this.sslPtr); + } } } @@ -4206,8 +4311,10 @@ public class WolfSSLSession { /* set user I/O send */ internSendSSLCb = callback; - /* register internal callback with native library */ - setSSLIOSend(this.sslPtr); + if (callback != null) { + /* register internal callback with native library */ + setSSLIOSend(this.sslPtr); + } } } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java index eb4666f..6e7e959 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java @@ -91,7 +91,8 @@ public class WolfSSLContext extends SSLContextSpi { ctxAttr.version == TLS_VERSION.TLSv1_1 || ctxAttr.version == TLS_VERSION.TLSv1_2 || ctxAttr.version == TLS_VERSION.TLSv1_3 || - ctxAttr.version == TLS_VERSION.SSLv23) { + ctxAttr.version == TLS_VERSION.SSLv23 || + ctxAttr.version == TLS_VERSION.DTLSv1_3) { this.currentVersion = ctxAttr.version; } else { throw new IllegalArgumentException( @@ -134,6 +135,11 @@ public class WolfSSLContext extends SSLContextSpi { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "creating WolfSSLContext with SSLv23"); break; + case DTLSv1_3: + method = WolfSSL.DTLSv1_3_Method(); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "creating WolfSSLContext with DTLSv1_3"); + break; default: throw new IllegalArgumentException( "Invalid SSL/TLS protocol version"); @@ -175,7 +181,7 @@ public class WolfSSLContext extends SSLContextSpi { * which have been disabled via system property get filtered in * WolfSSLEngineHelper.sanitizeProtocols() */ params.setProtocols(WolfSSLUtil.sanitizeProtocols( - this.getProtocolsMask(ctxAttr.noOptions))); + this.getProtocolsMask(ctxAttr.noOptions), this.currentVersion)); try { LoadTrustedRootCerts(); @@ -677,6 +683,20 @@ public class WolfSSLContext extends SSLContextSpi { } } + /** + * SSLContext implementation supporting DTLS 1.3 + */ + public static final class DTLSV13_Context extends WolfSSLContext { + /** + * Create new DTLSv13_Context, calls parent WolfSSLContext constructor + */ + public DTLSV13_Context() { + super(TLS_VERSION.DTLSv1_3); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "creating new WolfSSLContext using DTLSV13_Context"); + } + } /** * DEFAULT SSLContext class. diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java index b269940..f233040 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java @@ -72,6 +72,8 @@ public class WolfSSLEngine extends SSLEngine { private WolfSSLAuthStore authStore = null; private WolfSSLParameters params = null; private byte[] toSend = null; /* encrypted packet to send */ + private int nativeWantsToWrite = 0; + private int nativeWantsToRead = 0; private HandshakeStatus hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; /* Does TLS handshake need initialization */ @@ -218,6 +220,7 @@ public class WolfSSLEngine extends SSLEngine { try { this.engineHelper.LoadKeyAndCertChain(null, this); + certKeyLoaded = true; } catch (CertificateEncodingException | IOException | WolfSSLException e) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, @@ -441,26 +444,43 @@ public class WolfSSLEngine extends SSLEngine { * times out. This should not happen since infinite timeout is * being used for these calls. */ - private synchronized int DoHandshake() throws SSLException { + private synchronized int DoHandshake(boolean fromWrap) throws SSLException { int ret = WolfSSL.SSL_SUCCESS; try { - if (this.getUseClientMode()) { + /* If DTLS and calling from wrap() but HandshakeStatus is + * actually NEED_UNWRAP, this is a signal from the application + * that we need to retransmit messages */ + if ((this.ssl.dtls() == 1) && fromWrap && + (this.hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)) { synchronized (ioLock) { - ret = this.ssl.connect(); - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "ssl.connect() ret:err = " + ret + " : " + - ssl.getError(ret)); + "Calling wrap() while status is NEED_UNWRAP, " + + "retransmitting DTLS messages"); + ret = this.ssl.dtlsGotTimeout(); + if (ret == 0) { + ret = WolfSSL.SSL_SUCCESS; + } } } - else { - synchronized (ioLock) { - ret = this.ssl.accept(); + if (ret == WolfSSL.SSL_SUCCESS) { + if (this.getUseClientMode()) { + synchronized (ioLock) { + ret = this.ssl.connect(); - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "ssl.accept() ret:err = " + ret + " : " + - ssl.getError(ret)); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ssl.connect() ret:err = " + ret + " : " + + ssl.getError(ret)); + } + } + else { + synchronized (ioLock) { + ret = this.ssl.accept(); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ssl.accept() ret:err = " + ret + " : " + + ssl.getError(ret)); + } } } @@ -633,6 +653,12 @@ public class WolfSSLEngine extends SSLEngine { "handshakeStatus: " + hs); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "handshakeFinished: " + this.handshakeFinished); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeHandshakeState: " + this.ssl.getStateStringLong()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToRead: " + this.nativeWantsToRead); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToWrite: " + this.nativeWantsToWrite); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "==========================================================="); } @@ -691,7 +717,7 @@ public class WolfSSLEngine extends SSLEngine { else if (produced == 0) { /* continue handshake or application data */ if (!this.handshakeFinished) { - ret = DoHandshake(); + ret = DoHandshake(true); } else { try { @@ -756,6 +782,12 @@ public class WolfSSLEngine extends SSLEngine { "consumed: " + consumed); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "produced: " + produced); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeHandshakeState: " + this.ssl.getStateStringLong()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToRead: " + this.nativeWantsToRead); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToWrite: " + this.nativeWantsToWrite); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "==========================================================="); } @@ -830,10 +862,22 @@ public class WolfSSLEngine extends SSLEngine { * ByteBuffer array */ if (out.length == 1) { ret = this.ssl.read(out[0], maxOutSz, 0); + if ((ret < 0) && + (ssl.getError(ret) == WolfSSL.APP_DATA_READY)) { + /* If DTLS, we may need to call SSL_read() again + * right away again if app data was received */ + ret = this.ssl.read(out[0], maxOutSz, 0); + } } else { tmp = new byte[maxOutSz]; ret = this.ssl.read(tmp, maxOutSz); + if ((ret < 0) && + (ssl.getError(ret) == WolfSSL.APP_DATA_READY)) { + /* If DTLS, we may need to call SSL_read() again + * right away again if app data was received */ + ret = this.ssl.read(tmp, maxOutSz); + } } } catch (SocketTimeoutException | SocketException e) { throw new SSLException(e); @@ -855,6 +899,11 @@ public class WolfSSLEngine extends SSLEngine { "RecvAppData(), got WANT_READ/WANT_WRITE"); break; + case WolfSSL.APP_DATA_READY: + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "RecvAppData(), got APP_DATA_READY"); + break; + /* In 0 and ZERO_RETURN cases we may have gotten a * close_notify alert, check on shutdown status */ case WolfSSL.SSL_ERROR_ZERO_RETURN: @@ -1024,6 +1073,12 @@ public class WolfSSLEngine extends SSLEngine { "handshakeStatus: " + hs); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "status: " + status); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeHandshakeState: " + this.ssl.getStateStringLong()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToRead: " + this.nativeWantsToRead); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToWrite: " + this.nativeWantsToWrite); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "==========================================================="); } @@ -1066,12 +1121,10 @@ public class WolfSSLEngine extends SSLEngine { } } else { - if (this.handshakeFinished == false) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "starting or continuing handshake"); - ret = DoHandshake(); + ret = DoHandshake(false); } else { /* If we have input data, make sure output buffer length is @@ -1093,17 +1146,32 @@ public class WolfSSLEngine extends SSLEngine { if (ret > 0) { produced += ret; } - else { - synchronized (netDataLock) { - if (ret == 0 && in.remaining() > 0 && - getTotalOutputSize(out, ofst, - length) == 0) { - /* We have more data to read, but no more - * out space left in ByteBuffer[], ask for - * more */ - status = - SSLEngineResult.Status.BUFFER_OVERFLOW; - } + + /* Check for BUFFER_OVERFLOW status. This can happen + * if either we have data cached internally + * (in.remaining()) and we have no more output space, + * or if we have more decrypted plaintext in the native + * wolfSSL output buffer and ssl.pending() is > 0. */ + synchronized (ioLock) { + if ((ret > 0) && (ssl.pending() > 0)) { + status = + SSLEngineResult.Status.BUFFER_OVERFLOW; + } + else if ((ret == 0) && (ssl.pending() > 0) && + getTotalOutputSize(out, ofst, length) == 0) { + status = + SSLEngineResult.Status.BUFFER_OVERFLOW; + } + } + synchronized (netDataLock) { + if (ret == 0 && in.remaining() > 0 && + getTotalOutputSize(out, ofst, + length) == 0) { + /* We have more data to read, but no more + * out space left in ByteBuffer[], ask for + * more */ + status = + SSLEngineResult.Status.BUFFER_OVERFLOW; } } } @@ -1125,7 +1193,7 @@ public class WolfSSLEngine extends SSLEngine { } } } - } + } /* end DoHandshake() / RecvAppData() */ if (outBoundOpen == false || this.closeNotifySent) { /* Mark SSLEngine status as CLOSED */ @@ -1138,9 +1206,18 @@ public class WolfSSLEngine extends SSLEngine { synchronized (ioLock) { err = ssl.getError(ret); } - if (ret < 0 && + if (ret < 0 && (this.ssl.dtls() == 1) && + (err == WolfSSL.OUT_OF_ORDER_E)) { + /* Received out of order DTLS message. Ignore and set our + * status to NEED_UNWRAP again to wait for correct data */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out of order message, dropping and putting state " + + "back to NEED_UNWRAP"); + } + else if (ret < 0 && (err != WolfSSL.SSL_ERROR_WANT_READ) && (err != WolfSSL.SSL_ERROR_WANT_WRITE)) { + if (err == WolfSSL.UNKNOWN_ALPN_PROTOCOL_NAME_E) { /* Native wolfSSL could not negotiate a common ALPN * protocol */ @@ -1167,11 +1244,17 @@ public class WolfSSLEngine extends SSLEngine { synchronized (toSendLock) { synchronized (netDataLock) { - if (ret < 0 && err == WolfSSL.SSL_ERROR_WANT_READ && + if (ret <= 0 && err == WolfSSL.SSL_ERROR_WANT_READ && in.remaining() == 0 && (this.toSend == null || (this.toSend != null && this.toSend.length == 0))) { - /* Need more data */ - status = SSLEngineResult.Status.BUFFER_UNDERFLOW; + if ((this.ssl.dtls() == 0) || + this.handshakeFinished) { + /* Need more data. For DTLS only set + * after handshake has completed, since + * apps expect to switch on NEED_UNWRAP + * HandshakeStatus at that point */ + status = SSLEngineResult.Status.BUFFER_UNDERFLOW; + } } } } @@ -1191,10 +1274,19 @@ public class WolfSSLEngine extends SSLEngine { if (this.getUseClientMode() && this.handshakeFinished && this.ssl.hasSessionTicket() && this.sessionTicketReceived == false) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "received session ticket, returning " + - "HandshakeStatus FINISHED"); - hs = SSLEngineResult.HandshakeStatus.FINISHED; + if (this.ssl.dtls() == 1 && this.toSend != null && + this.toSend.length > 0) { + /* DTLS 1.3 ACK has been produced in response to + * session ticket message, let's set HS status to + * NEED_WRAP so application knows it needs to be sent. */ + hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "received session ticket, returning " + + "HandshakeStatus FINISHED"); + hs = SSLEngineResult.HandshakeStatus.FINISHED; + } this.sessionTicketReceived = true; } } @@ -1245,6 +1337,12 @@ public class WolfSSLEngine extends SSLEngine { "consumed: " + consumed); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "produced: " + produced); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeHandshakeState: " + this.ssl.getStateStringLong()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToRead: " + this.nativeWantsToRead); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "nativeWantsToWrite: " + this.nativeWantsToWrite); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "==========================================================="); } @@ -1306,7 +1404,9 @@ public class WolfSSLEngine extends SSLEngine { else { synchronized (netDataLock) { synchronized (ioLock) { - if (ssl.handshakeDone() && this.toSend == null) { + if (ssl.handshakeDone() && (this.toSend == null) && + (this.nativeWantsToWrite == 0) && + (this.nativeWantsToRead == 0)) { this.handshakeFinished = true; hs = SSLEngineResult.HandshakeStatus.FINISHED; this.engineHelper.getSession().updateStoredSessionValues(); @@ -1336,6 +1436,9 @@ public class WolfSSLEngine extends SSLEngine { else if (err == WolfSSL.SSL_ERROR_WANT_WRITE) { hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; } + else if (err == WolfSSL.OUT_OF_ORDER_E) { + hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; + } else { hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; } @@ -1489,7 +1592,13 @@ public class WolfSSLEngine extends SSLEngine { public synchronized SSLSession getHandshakeSession() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "entered getHandshakeSession()"); - return this.engineHelper.getSession(); + + if (!this.handshakeFinished) { + /* Only return handshake session during the handshake */ + return this.engineHelper.getSession(); + } + + return null; } /** @@ -1526,18 +1635,20 @@ public class WolfSSLEngine extends SSLEngine { "setUseClientMode() has not been called on this SSLEngine"); } - if (this.handshakeStartedExplicitly) { - /* Renegotiation (thus calling beginHandshake() multiple times) - * is not supported in wolfJSSE SSLEngine implementation yet. If - * already called once by user, throw SSLException. */ - throw new SSLException("Renegotiation not supported"); - } - else if (!this.needInit && !this.handshakeFinished) { - /* Handshake has started implicitly by wrap() or unwrap(). Simply - * return since this is the first time that the user has called - * beginHandshake() themselves. */ - this.handshakeStartedExplicitly = true; - return; + synchronized (initLock) { + if (this.handshakeStartedExplicitly) { + /* Renegotiation (thus calling beginHandshake() multiple times) + * is not supported in wolfJSSE SSLEngine implementation yet. If + * already called once by user, throw SSLException. */ + throw new SSLException("Renegotiation not supported"); + } + else if (!this.needInit && !this.handshakeFinished) { + /* Handshake has started implicitly by wrap() or unwrap(). Simply + * return since this is the first time that the user has called + * beginHandshake() themselves. */ + this.handshakeStartedExplicitly = true; + return; + } } /* No network data source yet */ @@ -1556,11 +1667,9 @@ public class WolfSSLEngine extends SSLEngine { throw new SSLException(e); } - if (needInit == true) { - /* will throw SSLHandshakeException if session creation is - not allowed */ - checkAndInitSSLEngine(); - } + /* will throw SSLHandshakeException if session creation is + not allowed */ + checkAndInitSSLEngine(); try { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, @@ -1865,6 +1974,30 @@ public class WolfSSLEngine extends SSLEngine { byte[] prevToSend = null; synchronized (toSendLock) { + /* As per JSSE Reference Guide, Section 8 DTLS implementation + * of wrap() contains at most one record so every DTLS record + * can be marshaled and delivered to datagram individually. As + * such, if we have data wait to send before caching more */ + /* TODO - do we need to fragment data here if larger than + * SSLParameters.getMaximumPacketSize() with DTLS? */ + if (this.ssl.dtls() == 1) { + if (this.toSend != null) { + /* Cause SSLEngine to only send one packet at a time. + * Keep track if wolfSSL had wanted to send data. We will + * use that information when setting the handshake + * status */ + if (sz > 0) { + this.nativeWantsToWrite = sz; + } + return -2; + } + + /* Reset native wants to send data flag. We have cleared + * cached data in the object, so this call of the send CB + * should be with the desired native data now. */ + this.nativeWantsToWrite = 0; + } + /* Make copy of existing toSend array before expanding */ if (this.toSend != null) { prevToSend = this.toSend.clone(); @@ -1927,9 +2060,15 @@ public class WolfSSLEngine extends SSLEngine { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "CB Read: returning WOLFSSL_CBIO_ERR_WANT_READ"); } + if (this.ssl.dtls() == 1 && this.handshakeFinished) { + this.nativeWantsToRead = 1; + } return WolfSSL.WOLFSSL_CBIO_ERR_WANT_READ; } + /* Reset native wants to read flag */ + this.nativeWantsToRead = 0; + max = (sz < this.netData.remaining()) ? sz : this.netData.remaining(); this.netData.get(toRead, 0, max); diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java index 099cb75..d6fab69 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java @@ -495,7 +495,8 @@ public class WolfSSLEngineHelper { for (int i = 0; i < suites.length; i++) { if (!supported.contains(suites[i])) { throw new IllegalArgumentException("Unsupported CipherSuite: " + - suites[i]); + suites[i] + "(Supported: " + + Arrays.toString(getAllCiphers()) + ")"); } } @@ -532,7 +533,8 @@ public class WolfSSLEngineHelper { } } - this.params.setProtocols(WolfSSLUtil.sanitizeProtocols(p)); + this.params.setProtocols( + WolfSSLUtil.sanitizeProtocols(p, WolfSSL.TLS_VERSION.INVALID)); } /** @@ -541,7 +543,8 @@ public class WolfSSLEngineHelper { * @return String array of enabled SSL/TLS protocols */ protected synchronized String[] getProtocols() { - return WolfSSLUtil.sanitizeProtocols(this.params.getProtocols()); + return WolfSSLUtil.sanitizeProtocols( + this.params.getProtocols(), WolfSSL.TLS_VERSION.INVALID); } /** @@ -552,7 +555,8 @@ public class WolfSSLEngineHelper { * @return String array of supported protocols */ protected static synchronized String[] getAllProtocols() { - return WolfSSLUtil.sanitizeProtocols(WolfSSL.getProtocols()); + return WolfSSLUtil.sanitizeProtocols( + WolfSSL.getProtocols(), WolfSSL.TLS_VERSION.INVALID); } /** @@ -751,10 +755,12 @@ public class WolfSSLEngineHelper { } for (i = 0; i < p.length; i++) { - if (p[i].equals("TLSv1.3")) { + /* TLS 1.3 needs to be enabled for DTLS 1.3 */ + if (p[i].equals("TLSv1.3") || p[i].equals("DTLSv1.3")) { set[0] = true; } - if (p[i].equals("TLSv1.2")) { + /* TLS 1.2 needs to be enabled for DTLS 1.2 */ + if (p[i].equals("TLSv1.2") || p[i].equals("DTLSv1.2")) { set[1] = true; } if (p[i].equals("TLSv1.1")) { @@ -768,6 +774,7 @@ public class WolfSSLEngineHelper { } } + /* Note: No SSL_OP_NO_* for DTLS in native wolfSSL */ if (set[0] == false) { mask |= WolfSSL.SSL_OP_NO_TLSv1_3; } @@ -1140,13 +1147,43 @@ public class WolfSSLEngineHelper { } } + private void setLocalMaximumPacketSize() { + /* Set maximum packet size, currently only makes a differnce if + * DTLS is enabled and used. Calling application will set this via + * SSLParameters.setMaximumPacketSize(). */ + int ret; + int maxPacketSize = this.params.getMaximumPacketSize(); + if (maxPacketSize != 0) { + /* Zero size means use implicit sizing logic of implementation, + * take no special action here if 0. */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Maximum packet size found in SSLParameters: " + maxPacketSize); + + ret = this.ssl.dtlsSetMTU(maxPacketSize); + if (ret == WolfSSL.SSL_SUCCESS) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "set maximum packet size (DTLS MTU): " + maxPacketSize); + } + else if (ret == WolfSSL.NOT_COMPILED_IN) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "DTLS or MTU not compiled in, skipping setting " + + "max packet size"); + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "error setting DTLS MTU, ret = " + ret); + } + } + } + private void setLocalParams(SSLSocket socket, SSLEngine engine) throws SSLException { this.setLocalCiphers( WolfSSLUtil.sanitizeSuites(this.params.getCipherSuites())); this.setLocalProtocol( - WolfSSLUtil.sanitizeProtocols(this.params.getProtocols())); + WolfSSLUtil.sanitizeProtocols( + this.params.getProtocols(), WolfSSL.TLS_VERSION.INVALID)); this.setLocalAuth(socket, engine); this.setLocalServerNames(); this.setLocalSessionTicket(); @@ -1154,6 +1191,7 @@ public class WolfSSLEngineHelper { this.setLocalSecureRenegotiation(); this.setLocalSigAlgorithms(); this.setLocalSupportedCurves(); + this.setLocalMaximumPacketSize(); } /** diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java index b7e79a3..8adc437 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java @@ -49,6 +49,8 @@ final class WolfSSLParameters { String[] applicationProtocols = new String[0]; private boolean useSessionTickets = false; private byte[] alpnProtocols = null; + /* Default to 0, means use implicit implementation size */ + private int maxPacketSize = 0; /* create duplicate copy of these parameters */ protected synchronized WolfSSLParameters copy() { @@ -62,6 +64,7 @@ final class WolfSSLParameters { cp.endpointIdAlgorithm = this.endpointIdAlgorithm; cp.setApplicationProtocols(this.applicationProtocols); cp.useCipherSuiteOrder = this.useCipherSuiteOrder; + cp.maxPacketSize = this.maxPacketSize; if (alpnProtocols != null && alpnProtocols.length != 0) { cp.setAlpnProtocols(this.alpnProtocols); @@ -213,5 +216,13 @@ final class WolfSSLParameters { this.applicationProtocols = protocols.clone(); } } + + int getMaximumPacketSize() { + return this.maxPacketSize; + } + + void setMaximumPacketSize(int maximumPacketSize) { + this.maxPacketSize = maximumPacketSize; + } } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java index 33be226..a761268 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java @@ -21,6 +21,7 @@ package com.wolfssl.provider.jsse; import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import java.security.PrivilegedAction; import javax.net.ssl.SSLParameters; @@ -37,6 +38,8 @@ public class WolfSSLParametersHelper private static Method setApplicationProtocols = null; private static Method getEndpointIdentificationAlgorithm = null; private static Method setEndpointIdentificationAlgorithm = null; + private static Method getMaximumPacketSize = null; + private static Method setMaximumPacketSize = null; /** Default WolfSSLParametersHelper constructor */ public WolfSSLParametersHelper() { } @@ -73,6 +76,12 @@ public class WolfSSLParametersHelper case "setEndpointIdentificationAlgorithm": setEndpointIdentificationAlgorithm = m; continue; + case "getMaximumPacketSize": + getMaximumPacketSize = m; + continue; + case "setMaximumPacketSize": + setMaximumPacketSize = m; + continue; default: continue; } @@ -112,15 +121,17 @@ public class WolfSSLParametersHelper ret.setWantClientAuth(in.getWantClientAuth()); } - /* Methods added as of JDK 1.8, older JDKs will not have them. Using - * Java reflection to detect availability. */ - + /* Methods added as of JDK 1.8 that rely on specific classes that + * do not existing in older JDKs. Since older JDKs will not have them, + * use Java reflection to detect availability in helper class. */ if (setServerNames != null || setApplicationProtocols != null || setEndpointIdentificationAlgorithm != null) { try { - /* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */ - Class cls = Class.forName("com.wolfssl.provider.jsse.WolfSSLJDK8Helper"); + /* load WolfSSLJDK8Helper at runtime, not compiled + * on older JDKs */ + Class cls = Class.forName( + "com.wolfssl.provider.jsse.WolfSSLJDK8Helper"); Object obj = cls.getConstructor().newInstance(); Class[] paramList = new Class[3]; paramList[0] = javax.net.ssl.SSLParameters.class; @@ -133,12 +144,15 @@ public class WolfSSLParametersHelper mth.invoke(obj, ret, setServerNames, in); } if (setApplicationProtocols != null) { - mth = cls.getDeclaredMethod("setApplicationProtocols", paramList); + mth = cls.getDeclaredMethod( + "setApplicationProtocols", paramList); mth.invoke(obj, ret, setApplicationProtocols, in); } if (setEndpointIdentificationAlgorithm != null) { - mth = cls.getDeclaredMethod("setEndpointIdentificationAlgorithm", paramList); - mth.invoke(obj, ret, setEndpointIdentificationAlgorithm, in); + mth = cls.getDeclaredMethod( + "setEndpointIdentificationAlgorithm", paramList); + mth.invoke(obj, ret, + setEndpointIdentificationAlgorithm, in); } } catch (Exception e) { @@ -146,6 +160,18 @@ public class WolfSSLParametersHelper } } + /* Methods added in later versions of SSLParameters which do not + * use any additional classes. Since no unique class names, these + * are called here directly instead of placed into a separate helper + * class. */ + try { + if (setMaximumPacketSize != null) { + setMaximumPacketSize.invoke(ret, in.getMaximumPacketSize()); + } + } catch (IllegalAccessException | InvocationTargetException e) { + /* Not available, just ignore and continue */ + } + /* The following SSLParameters features are not yet supported * by wolfJSSE (see Android API 23 note above). They are supported * with newer versions of SSLParameters, but will need to be added @@ -191,14 +217,15 @@ public class WolfSSLParametersHelper out.setWantClientAuth(in.getWantClientAuth()); } - /* Methods added as of JDK 1.8, older JDKs will not have them. Using - * Java reflection to detect availability. */ - + /* Methods added as of JDK 1.8 that rely on specific classes that + * do not existing in older JDKs. Since older JDKs will not have them, + * use Java reflection to detect availability in helper class. */ if (getServerNames != null || getApplicationProtocols != null || getEndpointIdentificationAlgorithm != null) { try { /* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */ - Class cls = Class.forName("com.wolfssl.provider.jsse.WolfSSLJDK8Helper"); + Class cls = Class.forName( + "com.wolfssl.provider.jsse.WolfSSLJDK8Helper"); Object obj = cls.getConstructor().newInstance(); Class[] paramList = new Class[2]; paramList[0] = javax.net.ssl.SSLParameters.class; @@ -210,11 +237,13 @@ public class WolfSSLParametersHelper mth.invoke(obj, in, out); } if (getApplicationProtocols != null) { - mth = cls.getDeclaredMethod("getApplicationProtocols", paramList); + mth = cls.getDeclaredMethod( + "getApplicationProtocols", paramList); mth.invoke(obj, in, out); } if (getEndpointIdentificationAlgorithm != null) { - mth = cls.getDeclaredMethod("getEndpointIdentificationAlgorithm", paramList); + mth = cls.getDeclaredMethod( + "getEndpointIdentificationAlgorithm", paramList); mth.invoke(obj, in, out); } @@ -223,6 +252,19 @@ public class WolfSSLParametersHelper } } + /* Methods added in later versions of SSLParameters which do not + * use any additional classes. Since no unique class names, these + * are called here directly instead of placed into a separate helper + * class. */ + try { + if (getMaximumPacketSize != null) { + int maxPacketSz = (int)getMaximumPacketSize.invoke(in); + out.setMaximumPacketSize(maxPacketSz); + } + } catch (IllegalAccessException | InvocationTargetException e) { + /* Not available, just ignore and continue */ + } + /* The following SSLParameters features are not yet supported * by wolfJSSE (see Android API 23 note above). They are supported * with newer versions of SSLParameters, but will need to be added diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java b/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java index 76fff4b..a6aa1c3 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java @@ -134,6 +134,10 @@ public final class WolfSSLProvider extends Provider { put("SSLContext.TLSv1.3", "com.wolfssl.provider.jsse.WolfSSLContext$TLSV13_Context"); } + if (WolfSSL.DTLSv13Enabled()) { + put("SSLContext.DTLSv1.3", + "com.wolfssl.provider.jsse.WolfSSLContext$DTLSV13_Context"); + } put("SSLContext.SSL", "com.wolfssl.provider.jsse.WolfSSLContext$TLSV23_Context"); put("SSLContext.TLS", diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java index fc9fe2c..49d6e25 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java @@ -211,7 +211,8 @@ public class WolfSSLServerSocket extends SSLServerSocket { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "entered getSupportedProtocols()"); - return WolfSSLUtil.sanitizeProtocols(params.getProtocols()); + return WolfSSLUtil.sanitizeProtocols( + params.getProtocols(), WolfSSL.TLS_VERSION.INVALID); } @Override @@ -220,7 +221,8 @@ public class WolfSSLServerSocket extends SSLServerSocket { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "entered getEnabledProtocols()"); - return WolfSSLUtil.sanitizeProtocols(params.getProtocols()); + return WolfSSLUtil.sanitizeProtocols( + params.getProtocols(), WolfSSL.TLS_VERSION.INVALID); } @Override @@ -242,7 +244,8 @@ public class WolfSSLServerSocket extends SSLServerSocket { List supported; supported = Arrays.asList( - WolfSSLUtil.sanitizeProtocols(WolfSSL.getProtocols())); + WolfSSLUtil.sanitizeProtocols( + WolfSSL.getProtocols(), WolfSSL.TLS_VERSION.INVALID)); for (int i = 0; i < protocols.length; i++) { if (!supported.contains(protocols[i])) { diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java index c57de09..c2c9805 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java @@ -33,6 +33,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLDebug; import com.wolfssl.WolfSSLException; @@ -50,7 +51,8 @@ public class WolfSSLUtil { } /** - * Sanitize or filter protocol list based on system property limitations. + * Sanitize or filter protocol list based on system property limitations + * and current TLS/DTLS protocol being established. * * Supported system properties which limit protocol list are: * - java.security.Security: @@ -61,17 +63,18 @@ public class WolfSSLUtil { * * jdk.tls.disabledAlgorithms="TLSv1, TLSv1.1" * - * This method force-removes DTLSv1.2 and DTLSv1.3 if the input - * protocols list advertises support for it. This is because native - * wolfSSL JNI supports DTLS but the JSSE layer does not yet. When - * DTLS support is added to wolfJSSE, this restriction can/will be removed. - * * @param protocols Full list of protocols to sanitize/filter, should be * in a format similar to: "TLSv1", "TLSv1.1", etc. + * @param currentVersion current protocol being used by the object + * that is calling this method. If WolfSSL.INVALID + * is passed in, no filtering is done on protocol + * list based on currentVersion. * * @return New filtered String array of protocol strings */ - protected static String[] sanitizeProtocols(String[] protocols) { + protected static String[] sanitizeProtocols(String[] protocols, + WolfSSL.TLS_VERSION currentVersion) { + ArrayList filtered = new ArrayList(); String disabledAlgos = @@ -83,14 +86,17 @@ public class WolfSSLUtil { WolfSSLDebug.log(WolfSSLUtil.class, WolfSSLDebug.INFO, "jdk.tls.disabledAlgorithms: " + disabledAlgos); - /* Force remove DTLS from supported JSSE protocols. Currently only - * JNI layer supports DTLS, not JSSE. When JSSE layer gets DTLS - * support added, take this restriction out. */ - if (disabledAlgos == null) { - disabledAlgos = "DTLSv1.2, DTLSv1.3"; - } - else { - disabledAlgos += ",DTLSv1.2,DTLSv1.3"; + /* If WolfSSL.INVALID is passed in as currentVersion, no filtering + * is done based on current protocol */ + if (currentVersion != WolfSSL.TLS_VERSION.INVALID) { + /* Remove DTLS protocols if using TLS explicitly. Needed + * since native wolfSSL doesn't have protocol masks for DTLS. */ + if (currentVersion != WolfSSL.TLS_VERSION.DTLSv1_2) { + disabledAlgos += ",DTLSv1.2"; + } + if (currentVersion != WolfSSL.TLS_VERSION.DTLSv1_3) { + disabledAlgos += ",DTLSv1.3"; + } } /* Remove spaces after commas, split into List */ diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 2f503bb..bdd7591 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -1212,6 +1212,7 @@ public class WolfSSLEngineTest { ByteBuffer netData = null; ByteBuffer peerAppData = null; ByteBuffer peerNetData = null; + boolean readAgain = false; SocketChannel sock = null; @@ -1350,7 +1351,7 @@ public class WolfSSLEngineTest { } } - /* read response */ + /* read response (might get TLS 1.3 session ticket instead) */ peerNetData.clear(); int recvd = sock.read(peerNetData); if (recvd > 0) { @@ -1370,14 +1371,55 @@ public class WolfSSLEngineTest { throw new Exception( "BUFFER_OVERFLOW during engine.unwrp()"); case BUFFER_UNDERFLOW: - throw new Exception( - "BUFFER_UNDERFLOW during engine.unwrap()"); + /* With TLS 1.3, we may get a session ticket + * message post handshake, resulting in BUFFER_UNDERFLOW + * status since we read the ticket but didn't get the + * chance to read the response waiting from the peer. */ + sess = engine.getSession(); + if (sess.getProtocol().equals("TLSv1.3")) { + readAgain = true; + break; + } + else { + throw new Exception( + "BUFFER_UNDERFLOW during engine.unwrap()"); + } default: throw new Exception( "Unknown HandshakeStatus"); } } + if (readAgain) { + /* read response */ + peerNetData.clear(); + recvd = sock.read(peerNetData); + if (recvd > 0) { + peerNetData.flip(); + result = engine.unwrap(peerNetData, peerAppData); + peerNetData.compact(); + switch (result.getStatus()) { + case OK: + peerAppData.flip(); + /* not doing anything with returned data */ + break; + case CLOSED: + engine.closeOutbound(); + engine.closeInbound(); + break; + case BUFFER_OVERFLOW: + throw new Exception( + "BUFFER_OVERFLOW during engine.unwrp()"); + case BUFFER_UNDERFLOW: + throw new Exception( + "BUFFER_UNDERFLOW during engine.unwrap()"); + default: + throw new Exception( + "Unknown HandshakeStatus"); + } + } + } + /* shutdown */ engine.closeOutbound(); while (engine.isOutboundDone() == false) {