diff --git a/README.md b/README.md index 9facb71a..b178bf13 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 @@ -531,6 +534,11 @@ are enabled in different ways depending on the JDK implementation. For Oracle/OpenJDK and variants, this System property enables session tickets and was added in Java 13. Should be set to "true" to enable. +**jdk.tls.useExtendedMasterSecret (boolean)** - Can be used to enable or +disable the use of the Extended Master Secret (EMS) extension. This extension +is enabled by default, unless explicitly disabled by setting this property to +false. + **wolfjsse.autoSNI (boolean)** - Controls automatic Server Name Indication (SNI) extension setting based on hostname or peer address. When set to "true", enables legacy behavior where SNI is automatically configured from hostname/peer information diff --git a/examples/Client.java b/examples/Client.java index e2f32ed8..a03ea934 100644 --- a/examples/Client.java +++ b/examples/Client.java @@ -222,10 +222,15 @@ public void run(String[] args) { /* sort out DTLS versus TLS versions */ if (doDTLS == 1) { - if (sslVersion == 3) + if (sslVersion == 4) { + sslVersion = -3; + } + else if (sslVersion == 3) { sslVersion = -2; - else + } + else { sslVersion = -1; + } } /* init library */ @@ -260,6 +265,9 @@ public void run(String[] args) { case -2: method = WolfSSL.DTLSv1_2_ClientMethod(); break; + case -3: + method = WolfSSL.DTLSv1_3_ClientMethod(); + break; default: System.err.println("Bad SSL version"); System.exit(1); @@ -786,7 +794,7 @@ void printUsage() { System.out.println("-d\t\tDisable peer checks"); if (WolfSSL.isEnabledDTLS() == 1) System.out.println("-u\t\tUse UDP DTLS, add -v 2 for DTLSv1 " + - "(default), -v 3 for DTLSv1.2"); + "(default), -v 3 for DTLSv1.2, -v 4 for DTLSv1.3"); System.out.println("-iocb\t\tEnable test I/O callbacks"); System.out.println("-logtest\tEnable test logging callback"); if (WolfSSL.isEnabledOCSP() == 1) { diff --git a/examples/Server.java b/examples/Server.java index cfe36b2e..43e02e68 100644 --- a/examples/Server.java +++ b/examples/Server.java @@ -209,10 +209,15 @@ public void run(String[] args) { /* sort out DTLS versus TLS versions */ if (doDTLS == 1) { - if (sslVersion == 3) + if (sslVersion == 4) { + sslVersion = -3; + } + else if (sslVersion == 3) { sslVersion = -2; - else + } + else { sslVersion = -1; + } } /* init library */ @@ -247,6 +252,9 @@ public void run(String[] args) { case -2: method = WolfSSL.DTLSv1_2_ServerMethod(); break; + case -3: + method = WolfSSL.DTLSv1_3_ServerMethod(); + break; default: System.err.println("Bad SSL version"); System.exit(1); @@ -683,7 +691,7 @@ void printUsage() { System.out.println("-s\t\tUse pre shared keys"); if (WolfSSL.isEnabledDTLS() == 1) System.out.println("-u\t\tUse UDP DTLS, add -v 2 for DTLSv1 (default)" + - ", -v 3 for DTLSv1.2"); + ", -v 3 for DTLSv1.2, -v 4 for DTLSv1.3"); System.out.println("-iocb\t\tEnable test I/O callbacks"); System.out.println("-logtest\tEnable test logging callback"); if (WolfSSL.isEnabledOCSP() == 1) { diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c index c0ac139c..f8a279f4 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) { @@ -747,7 +760,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_SSLv3_1ServerMethod #if defined(WOLFSSL_ALLOW_SSLV3) && !defined(NO_OLD_TLS) return (jlong)(uintptr_t)wolfSSLv3_server_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -760,7 +773,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_SSLv3_1ClientMethod #if defined(WOLFSSL_ALLOW_SSLV3) && !defined(NO_OLD_TLS) return (jlong)(uintptr_t)wolfSSLv3_client_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -773,7 +786,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_1Method #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10) return (jlong)(uintptr_t)wolfTLSv1_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -786,7 +799,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_1ServerMethod #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10) return (jlong)(uintptr_t)wolfTLSv1_server_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -799,7 +812,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_1ClientMethod #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10) return (jlong)(uintptr_t)wolfTLSv1_client_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -812,7 +825,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_11_1Method #ifndef NO_OLD_TLS return (jlong)(uintptr_t)wolfTLSv1_1_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -825,7 +838,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_11_1ServerMethod #ifndef NO_OLD_TLS return (jlong)(uintptr_t)wolfTLSv1_1_server_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -838,7 +851,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_11_1ClientMethod #ifndef NO_OLD_TLS return (jlong)(uintptr_t)wolfTLSv1_1_client_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -878,7 +891,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_13_1Method #ifdef WOLFSSL_TLS13 return (jlong)(uintptr_t)wolfTLSv1_3_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -891,7 +904,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_13_1ServerMethod #ifdef WOLFSSL_TLS13 return (jlong)(uintptr_t)wolfTLSv1_3_server_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -904,7 +917,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_TLSv1_13_1ClientMethod #ifdef WOLFSSL_TLS13 return (jlong)(uintptr_t)wolfTLSv1_3_client_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -917,7 +930,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_1Method #if defined(WOLFSSL_DTLS) && !defined(NO_OLD_TLS) return (jlong)(uintptr_t)wolfDTLSv1_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -930,7 +943,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_1ClientMethod #if defined(WOLFSSL_DTLS) && !defined(NO_OLD_TLS) return (jlong)(uintptr_t)wolfDTLSv1_client_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -943,7 +956,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_1ServerMethod #if defined(WOLFSSL_DTLS) && !defined(NO_OLD_TLS) return (jlong)(uintptr_t)wolfDTLSv1_server_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -956,7 +969,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_12_1Method #if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) return (jlong)(uintptr_t)wolfDTLSv1_2_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -969,7 +982,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_12_1ClientMethod #if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) return (jlong)(uintptr_t)wolfDTLSv1_2_client_method(); #else - return NOT_COMPILED_IN; + return 0; #endif } @@ -982,7 +995,46 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_12_1ServerMethod #if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) return (jlong)(uintptr_t)wolfDTLSv1_2_server_method(); #else - return NOT_COMPILED_IN; + return 0; +#endif +} + +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_13_1Method + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) + return (jlong)(uintptr_t)wolfDTLSv1_3_method(); +#else + return 0; +#endif +} + +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_13_1ServerMethod + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) + return (jlong)(uintptr_t)wolfDTLSv1_3_server_method(); +#else + return 0; +#endif +} + +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_13_1ClientMethod + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) + return (jlong)(uintptr_t)wolfDTLSv1_3_client_method(); +#else + return 0; #endif } @@ -1110,11 +1162,18 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_setLoggingCb return ret; } +/** + * Native wolfSSL logging callback. + * + * We skip throwing exceptions in this function and just move on without + * printing the log. Otherwise, our non-important exception here could cause + * bad things to happen at the Java level - ie, causing the + * certificate verify callback to fail unnecessarily. + */ void NativeLoggingCallback(const int logLevel, const char *const logMessage) { JNIEnv* jenv = NULL; jint vmret = 0; - jclass excClass; jclass logClass; jmethodID logMethod; jstring logMsg; @@ -1145,17 +1204,6 @@ void NativeLoggingCallback(const int logLevel, const char *const logMessage) return; } - /* find exception class */ - excClass = (*jenv)->FindClass(jenv, "java/lang/Exception"); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - if (needsDetach == 1) { - (*g_vm)->DetachCurrentThread(g_vm); - } - return; - } - /* check if our stored object reference is valid */ refcheck = (*jenv)->GetObjectRefType(jenv, g_loggingCbIfaceObj); if (refcheck == 2) { @@ -1168,9 +1216,6 @@ void NativeLoggingCallback(const int logLevel, const char *const logMessage) (*jenv)->ExceptionClear(jenv); } - (*jenv)->ThrowNew(jenv, excClass, - "Can't get native WolfSSLLoggingCallback class reference"); - if (needsDetach == 1) { (*g_vm)->DetachCurrentThread(g_vm); } @@ -1185,8 +1230,6 @@ void NativeLoggingCallback(const int logLevel, const char *const logMessage) (*jenv)->ExceptionDescribe(jenv); (*jenv)->ExceptionClear(jenv); } - (*jenv)->ThrowNew(jenv, excClass, - "Error getting loggingCallback method from JNI"); if (needsDetach == 1) { (*g_vm)->DetachCurrentThread(g_vm); } @@ -1203,22 +1246,16 @@ void NativeLoggingCallback(const int logLevel, const char *const logMessage) (*jenv)->ExceptionDescribe(jenv); (*jenv)->ExceptionClear(jenv); - (*jenv)->ThrowNew(jenv, excClass, - "Error calling logging callback from JNI"); + /* Not throwing exception, just move on without printing the log. + * Otherwise, our non-important exception here could cause + * bad things to happen at the Java level - ie, causing the + * certificate verify callback to fail unnecessarily. */ if (needsDetach == 1) { (*g_vm)->DetachCurrentThread(g_vm); } return; } - } else { - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); - } - - (*jenv)->ThrowNew(jenv, excClass, - "Object reference invalid in NativeLoggingCallback"); } if (needsDetach == 1) { @@ -1652,7 +1689,7 @@ JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSL_getAvailableCipherSuitesIana return NULL; } - if (protocolVersion < 0 || protocolVersion > 5) { + if (protocolVersion < 0 || protocolVersion > 8) { printf("Input protocol version invalid: %d\n", protocolVersion); return NULL; } @@ -1683,6 +1720,23 @@ JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSL_getAvailableCipherSuitesIana case 5: method = wolfSSLv23_client_method(); break; +#ifdef WOLFSSL_DTLS + #ifndef NO_OLD_TLS + case 6: + method = wolfDTLSv1_client_method(); + break; + #endif + #ifndef WOLFSSL_NO_TLS12 + case 7: + method = wolfDTLSv1_2_client_method(); + break; + #endif + #ifdef WOLFSSL_DTLS13 + case 8: + method = wolfDTLSv1_3_client_method(); + break; + #endif +#endif default: printf("Input protocol version invalid: %d\n", protocolVersion); return NULL; @@ -1900,6 +1954,19 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledDTLS #endif } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledSendHrrCookie + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifdef WOLFSSL_SEND_HRR_COOKIE + return 1; +#else + return 0; +#endif +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledAtomicUser (JNIEnv* jenv, jclass jcl) { @@ -1926,6 +1993,19 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledPKCallbacks #endif } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledTLSExtendedMasterSecret + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#if defined(HAVE_EXTENDED_MASTER) && !defined(NO_WOLFSSL_CLIENT) + return 1; +#else + return 0; +#endif +} + JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_WolfSSL_getProtocols (JNIEnv* jenv, jclass jcl) { @@ -1940,26 +2020,42 @@ 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 ret = (*jenv)->NewObjectArray(jenv, numProtocols, @@ -2031,6 +2127,45 @@ JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_WolfSSL_getProtocolsMask } } #endif + +#ifdef WOLFSSL_DTLS + #ifndef NO_OLD_TLS + 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 + 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 + #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 return ret; } diff --git a/native/com_wolfssl_WolfSSL.h b/native/com_wolfssl_WolfSSL.h index 3c774fff..350216a4 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 @@ -885,6 +897,30 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_12_1ServerMethod JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_12_1ClientMethod (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: DTLSv1_3_Method + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_13_1Method + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: DTLSv1_3_ServerMethod + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_13_1ServerMethod + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: DTLSv1_3_ClientMethod + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_DTLSv1_13_1ClientMethod + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: SSLv23_Method @@ -1061,6 +1097,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledPSK JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledDTLS (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: isEnabledSendHrrCookie + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledSendHrrCookie + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: isEnabledAtomicUser @@ -1077,6 +1121,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledAtomicUser JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledPKCallbacks (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: isEnabledTLSExtendedMasterSecret + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_isEnabledTLSExtendedMasterSecret + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: getProtocols diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c index 3e88820d..897e5305 100644 --- a/native/com_wolfssl_WolfSSLSession.c +++ b/native/com_wolfssl_WolfSSLSession.c @@ -63,6 +63,12 @@ int NativeALPNSelectCb(WOLFSSL *ssl, const unsigned char **out, int NativeTls13SecretCb(WOLFSSL *ssl, int id, const unsigned char* secret, int secretSz, void* ctx); +#if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_SESSION_TICKET) +/* Session ticket callback prototype */ +int NativeSessionTicketCb(WOLFSSL *ssl, const unsigned char* ticket, + int ticketLen, void* ctx); +#endif + #ifdef HAVE_CRL /* global object refs for CRL callback */ static jobject g_crlCbIfaceObj; @@ -1568,6 +1574,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 +2430,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 +2447,35 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGotTimeout #endif } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsRetransmit + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ +#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) { - 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 0; - } - (*jenv)->ThrowNew(jenv, excClass, - "Input WolfSSLSession object was null in dtls()"); return 0; } @@ -2694,6 +2714,128 @@ JNIEXPORT jobject JNICALL Java_com_wolfssl_WolfSSLSession_dtlsGetPeer } } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_sendHrrCookie + (JNIEnv* jenv, jobject jcl, jlong sslPtr, jbyteArray secret) +{ + int ret = SSL_FAILURE; +#ifdef WOLFSSL_SEND_HRR_COOKIE + byte* secretBuf = NULL; + word32 secretSz = 0; + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jcl; + + if (jenv == NULL || ssl == NULL) { + return BAD_FUNC_ARG; + } + + if (secret != NULL) { + secretBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, secret, NULL); + secretSz = (*jenv)->GetArrayLength(jenv, secret); + } + + ret = wolfSSL_send_hrr_cookie(ssl, secretBuf, secretSz); + + if (secret != NULL) { + (*jenv)->ReleaseByteArrayElements(jenv, secret, + (jbyte*)secretBuf, JNI_ABORT); + } + +#else + ret = NOT_COMPILED_IN; + (void)jenv; + (void)jcl; + (void)sslPtr; + (void)secret; +#endif /* WOLFSSL_SEND_HRR_COOKIE */ + + return (jint)ret; +} + +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getDtlsMacDropCount + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ + word32 dropCount = 0; +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_DROP_STATS) + int ret = 0; + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + + /* checks ssl for NULL internally */ + ret = wolfSSL_dtls_get_drop_stats(ssl, &dropCount, NULL); + if (ret != WOLFSSL_SUCCESS) { + return (jlong)ret; + } +#endif + (void)jenv; + (void)jcl; + + return (jlong)dropCount; +} + +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getDtlsReplayDropCount + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ + word32 dropCount = 0; +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_DROP_STATS) + int ret = 0; + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + + /* checks ssl for NULL internally */ + ret = wolfSSL_dtls_get_drop_stats(ssl, NULL, &dropCount); + if (ret != WOLFSSL_SUCCESS) { + return (jlong)ret; + } +#endif + (void)jenv; + (void)jcl; + + return (jlong)dropCount; +} + +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 +4823,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 @@ -5487,6 +5628,30 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setTls13SecretCb #endif } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setSessionTicketCb + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ +#if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_SESSION_TICKET) + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + int ret = SSL_SUCCESS; + (void)jcl; + + if (jenv == NULL || ssl == NULL) { + return BAD_FUNC_ARG; + } + + /* Java layer handles setting and giving back user CTX */ + ret = wolfSSL_set_SessionTicket_cb(ssl, NativeSessionTicketCb, NULL); + + return ret; +#else + (void)jenv; + (void)jcl; + (void)sslPtr; + return NOT_COMPILED_IN; +#endif +} + #if defined(WOLFSSL_TLS13) && !defined(WOLFCRYPT_ONLY) && \ defined(HAVE_SECRET_CALLBACK) @@ -5629,6 +5794,146 @@ int NativeTls13SecretCb(WOLFSSL *ssl, int id, const unsigned char* secret, #endif /* WOLFSSL_TLS13 && !WOLFCRYPT_ONLY && HAVE_SECRET_CALLBACK */ +#if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_SESSION_TICKET) + +int NativeSessionTicketCb(WOLFSSL* ssl, const unsigned char* ticket, + int ticketLen, void* ctx) +{ + JNIEnv* jenv; /* JNI environment */ + jclass excClass; /* WolfSSLJNIException class */ + int needsDetach = 0; /* Should we explicitly detach? */ + jint retval = 0; + jint vmret = 0; + + jobject* g_cachedSSLObj; /* WolfSSLSession cached object */ + jclass sslClass; /* WolfSSLSession class */ + jmethodID sessTicketCbMethodId; /* internalTls13SecretCallback ID */ + jbyteArray ticketArr = NULL; + + if (g_vm == NULL || ssl == NULL) { + return BAD_FUNC_ARG; + } + + /* get JavaEnv from JavaVM */ + vmret = (int)((*g_vm)->GetEnv(g_vm, (void**) &jenv, JNI_VERSION_1_6)); + if (vmret == JNI_EDETACHED) { +#ifdef __ANDROID__ + vmret = (*g_vm)->AttachCurrentThread(g_vm, &jenv, NULL); +#else + vmret = (*g_vm)->AttachCurrentThread(g_vm, (void**) &jenv, NULL); +#endif + if (vmret) { + return -1; + } + needsDetach = 1; + } + else if (vmret != JNI_OK) { + return -1; + } + + /* 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 (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* Get stored WolfSSLSession object */ + g_cachedSSLObj = (jobject*) wolfSSL_get_jobject(ssl); + if (!g_cachedSSLObj) { + (*jenv)->ThrowNew(jenv, excClass, + "Can't get native WolfSSLSession object reference in " + "NativeSessionTicketCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* Lookup WolfSSLSession class from object */ + sslClass = (*jenv)->GetObjectClass(jenv, (jobject)(*g_cachedSSLObj)); + if (sslClass == NULL) { + (*jenv)->ThrowNew(jenv, excClass, + "Can't get native WolfSSLSession class reference in " + "NativeSessionTicketCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* Call internal TLS 1.3 secret callback */ + sessTicketCbMethodId = (*jenv)->GetMethodID(jenv, sslClass, + "internalSessionTicketCallback", "(Lcom/wolfssl/WolfSSLSession;[B)I"); + if (sessTicketCbMethodId == NULL) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, + "Error getting internalSessionTicketCallback method from JNI"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + if (ticketLen > 0) { + /* Create jbyteArray to hold session ticket */ + ticketArr = (*jenv)->NewByteArray(jenv, ticketLen); + if (ticketArr == NULL) { + (*jenv)->ThrowNew(jenv, excClass, + "Error creating new jbyteArray in NativeSessionTicketCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + (*jenv)->SetByteArrayRegion(jenv, ticketArr, 0, ticketLen, + (jbyte*)ticket); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* Call Java session ticket callback, ignore native CTX since Java + * handles it */ + retval = (*jenv)->CallIntMethod(jenv, (jobject)(*g_cachedSSLObj), + sessTicketCbMethodId, (jobject)(*g_cachedSSLObj), ticketArr); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->ThrowNew(jenv, excClass, + "Exception while calling internalSessionTicketCallback()"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* Delete local refs */ + (*jenv)->DeleteLocalRef(jenv, ticketArr); + } + + /* Detach JNIEnv from thread */ + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + + return (int)retval; +} + +#endif /* WOLFSSL_TLS13 && !WOLFCRYPT_ONLY && HAVE_SECRET_CALLBACK */ + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useSecureRenegotiation (JNIEnv* jenv, jobject jcl, jlong ssl) { @@ -5707,6 +6012,26 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useSupportedCurve #endif } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_disableExtendedMasterSecret + (JNIEnv* jenv, jobject jcl, jlong sslPtr) +{ +#if defined(HAVE_EXTENDED_MASTER) && !defined(NO_WOLFSSL_CLIENT) + int ret = 0; + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jcl; + + /* Checks ssl for null internally */ + ret = wolfSSL_DisableExtendedMasterSecret(ssl); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)sslPtr; + return (jint)NOT_COMPILED_IN; +#endif +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_hasTicket (JNIEnv* jenv, jobject jcl, jlong sessionPtr) { @@ -5777,24 +6102,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 +6244,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 260032ca..017e4577 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 @@ -295,6 +311,30 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtls JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_dtlsSetPeer (JNIEnv *, jobject, jlong, jobject); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: sendHrrCookie + * Signature: (J[B)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_sendHrrCookie + (JNIEnv *, jobject, jlong, jbyteArray); + +/* + * Class: com_wolfssl_WolfSSLSession + * Method: getDtlsMacDropCount + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getDtlsMacDropCount + (JNIEnv *, jobject, jlong); + +/* + * Class: com_wolfssl_WolfSSLSession + * Method: getDtlsReplayDropCount + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getDtlsReplayDropCount + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLSession * Method: dtlsGetPeer @@ -823,6 +863,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setALPNSelectCb JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setTls13SecretCb (JNIEnv *, jobject, jlong); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: setSessionTicketCb + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setSessionTicketCb + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLSession * Method: keepArrays @@ -871,6 +919,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_set1SigAlgsList JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useSupportedCurve (JNIEnv *, jobject, jlong, jint); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: disableExtendedMasterSecret + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_disableExtendedMasterSecret + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLSession * Method: hasTicket @@ -895,6 +951,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/scripts/infer.sh b/scripts/infer.sh index 97bc30d2..531b0d48 100755 --- a/scripts/infer.sh +++ b/scripts/infer.sh @@ -61,6 +61,7 @@ infer --fail-on-issue run -- javac \ src/java/com/wolfssl/WolfSSLRsaSignCallback.java \ src/java/com/wolfssl/WolfSSLRsaVerifyCallback.java \ src/java/com/wolfssl/WolfSSLSession.java \ + src/java/com/wolfssl/WolfSSLSessionTicketCallback.java \ src/java/com/wolfssl/WolfSSLTls13SecretCallback.java \ src/java/com/wolfssl/WolfSSLVerifyCallback.java \ src/java/com/wolfssl/WolfSSLVerifyDecryptCallback.java \ diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index 98adadab..e4c9b4ad 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -51,7 +51,13 @@ public enum TLS_VERSION { /** TLS 1.3 */ TLSv1_3, /** Downgrade starting from highest supported SSL/TLS version */ - SSLv23 + SSLv23, + /** DTLS 1.0 */ + DTLSv1, + /** DTLS 1.2 */ + DTLSv1_2, + /** DTLS 1.3 */ + DTLSv1_3 } /* ------------------ wolfSSL JNI error codes ----------------------- */ @@ -208,10 +214,14 @@ public enum TLS_VERSION { 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 */ @@ -838,6 +848,13 @@ protected static byte[] fileToBytes(File file) */ 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. * @@ -1234,6 +1251,46 @@ protected static byte[] fileToBytes(File file) */ public static final native long DTLSv1_2_ClientMethod(); + /** + * Indicates that the application will only support the DTLS 1.3 protocol. + * Application is side-independent at this time, and client/server side + * will be determined at connect/accept stage. + * This method allocates memory for and initializes a new native + * WOLFSSL_METHOD structure to be used when creating the SSL/TLS + * context with newContext(). + * + * @return A pointer to the created WOLFSSL_METHOD structure if + * successful, null on failure. + * @see WolfSSLContext#newContext(long) + */ + public static final native long DTLSv1_3_Method(); + + /** + * Indicates that the application is a server and will only support the + * DTLS 1.3 protocol. + * This method allocates memory for and initializes a new native + * WOLFSSL_METHOD structure to be used when creating the SSL/TLS + * context with newContext(). + * + * @return A pointer to the created WOLFSSL_METHOD structure if + * successful, null on failure. + * @see WolfSSLContext#newContext(long) + */ + public static final native long DTLSv1_3_ServerMethod(); + + /** + * Indicates that the application is a client and will only support the + * DTLS 1.3 protocol. + * This method allocates memory for and initializes a new native + * WOLFSSL_METHOD structure to be used when creating the SSL/TLS + * context with newContext(). + * + * @return A pointer to the created WOLFSSL_METHOD structure if + * successful, null on failure. + * @see WolfSSLContext#newContext(long) + */ + public static final native long DTLSv1_3_ClientMethod(); + /** * Indicates that the application will use the highest possible SSL/TLS * version from SSL 3.0 up to TLS 1.2, but is side-independent at creation @@ -1590,6 +1647,14 @@ public static int cryptoCbUnRegisterDevice(int devId) { */ public static native int isEnabledDTLS(); + /** + * Checks if (D)TLS 1.3 HRR Cookie is enabled in the native wolfSSL + * library. Checks if native WOLFSSL_SEND_HRR_COOKIE is defined. + * + * @return 1 if enabled, 9 if not compiled in. + */ + public static native int isEnabledSendHrrCookie(); + /** * Checks if Atomic User support is enabled in wolfSSL native library. * @@ -1605,6 +1670,14 @@ public static int cryptoCbUnRegisterDevice(int devId) { */ public static native int isEnabledPKCallbacks(); + /** + * Checks if TLS Extended Master Secret support has been compiled into + * native wolfSSL library. + * + * @return 1 if available, 0 if not compiled in. + */ + public static native int isEnabledTLSExtendedMasterSecret(); + /** * Checks which protocols where built into wolfSSL * diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java index 987a29c8..dd39fb65 100644 --- a/src/java/com/wolfssl/WolfSSLSession.java +++ b/src/java/com/wolfssl/WolfSSLSession.java @@ -62,6 +62,7 @@ public class WolfSSLSession { private Object rsaDecCtx; private Object alpnSelectArg; private Object tls13SecretCtx; + private Object sessionTicketCtx; /* reference to the associated WolfSSLContext */ private WolfSSLContext ctx = null; @@ -80,10 +81,14 @@ public class WolfSSLSession { * ALPN select callback */ private WolfSSLALPNSelectCallback internAlpnSelectCb; - /* user-registered TLS 1.3 secret callbcak, called by internal + /* user-registered TLS 1.3 secret callback, called by internal * WolfSSLSession TLS 1.3 secret callback */ private WolfSSLTls13SecretCallback internTls13SecretCb; + /* user-registered session ticket callback, called by internal + * WolfSSLSession session ticket callback */ + private WolfSSLSessionTicketCallback internSessionTicketCb; + /* have session tickets been enabled for this session? Default to false. */ private boolean sessionTicketsEnabled = false; @@ -282,6 +287,13 @@ private int internalTls13SecretCallback(WolfSSLSession ssl, int id, this.tls13SecretCtx); } + private int internalSessionTicketCallback(WolfSSLSession ssl, byte[] ticket) + { + /* call user-registered session ticket callback */ + return internSessionTicketCb.sessionTicketCallback(ssl, ticket, + this.sessionTicketCtx); + } + /** * Verifies that the current WolfSSLSession object is active. * @@ -316,6 +328,7 @@ private native int read(long ssl, byte[] data, int offset, int sz, 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,8 +350,12 @@ private native int read(long ssl, ByteBuffer data, int sz, int timeout) 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 int sendHrrCookie(long ssl, byte[] secret); + private native long getDtlsMacDropCount(long ssl); + private native long getDtlsReplayDropCount(long ssl); private native InetSocketAddress dtlsGetPeer(long ssl); private native int sessionReused(long ssl); private native long getPeerCertificate(long ssl); @@ -410,15 +427,19 @@ private native int setTlsHmacInner(long ssl, byte[] inner, long sz, private native int useALPN(long ssl, String protocols, int options); private native int setALPNSelectCb(long ssl); private native int setTls13SecretCb(long ssl); + private native int setSessionTicketCb(long ssl); private native void keepArrays(long ssl); private native byte[] getClientRandom(long ssl); private native int useSecureRenegotiation(long ssl); private native int rehandshake(long ssl); private native int set1SigAlgsList(long ssl, String list); private native int useSupportedCurve(long ssl, int name); + private native int disableExtendedMasterSecret(long ssl); 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 +1160,7 @@ public int read(byte[] data, int sz, int timeout) * 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 +1278,29 @@ public int read(ByteBuffer data, int sz, int timeout) 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 @@ -2214,6 +2258,23 @@ public int useSupportedCurves(String[] curveNames) return ret; } + /** + * Disable TLS Extended Master Secret usage. + * + * @return WolfSSL.SSL_SUCCESS on success, otherwise + * negative on error. + * @throws IllegalStateException WolfSSLSession has been freed + */ + public int disableExtendedMasterSecret() + throws IllegalStateException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return disableExtendedMasterSecret(this.sslPtr); + } + } + /* ---------------- Nonblocking DTLS helper functions -------------- */ /** @@ -2260,14 +2321,12 @@ public int dtlsGetCurrentTimeout() * 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 +2338,47 @@ public int dtlsGotTimeout() } } + /** + * 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(); @@ -2332,6 +2418,70 @@ public int dtlsSetPeer(InetSocketAddress peer) } } + /** + * Send a cookie with the HelloRetryRequest to avoid storing state. + * + * @param secret Secret to use when generating integrity check for cookie. + * A value of null indicates to generate a new random secret. + * + * @return WolfSSL.SSL_SUCCESS on success, + * WolfSSL.BAD_FUNC_ARG when not using (D)TLS 1.3, + * WolfSSL.SIDE_ERROR when called on a client. + * + * @throws IllegalStateException WolfSSLContext has been freed + */ + public int sendHrrCookie(byte[] secret) + throws IllegalStateException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return sendHrrCookie(this.sslPtr, secret); + } + } + + /** + * Get the number of DTLS packets that have been dropped due + * to verify MAC decrypt errors. + * + * Native wolfSSL must be compiled with WOLFSSL_DTLS_DROP_STATS + * defined for this functionality to be available, otherwise this + * method will return 0. + * + * @return number of dropped packets + * + * @throws IllegalStateException WolfSSLSession has been freed + */ + public long getDtlsMacDropCount() throws IllegalStateException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return getDtlsMacDropCount(this.sslPtr); + } + } + + /** + * Get the number of DTLS packets that have been dropped due to + * receiving a replayed / duplicated packet. + * + * Native wolfSSL must be compiled with WOLFSSL_DTLS_DROP_STATS + * defined for this functionality to be available, otherwise this + * method will return 0. + * + * @return number of dropped packets + * + * @throws IllegalStateException WolfSSLSession has been freed + */ + public long getDtlsReplayDropCount() throws IllegalStateException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return getDtlsReplayDropCount(this.sslPtr); + } + } + /** * Gets the InetSocketAddress of the DTLS peer. * @@ -2355,6 +2505,57 @@ public InetSocketAddress dtlsGetPeer() throws IllegalStateException { } } + /** + * 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. @@ -2511,8 +2712,8 @@ public String getPeerX509AltName(long x509) /** * Returns the SSL/TLS version being used with this session object in * String format. - * Examples include "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "DTLS", and - * "DTLS 1.2". + * Examples include "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "DTLS", + * "DTLS 1.2", and "DTLS 1.3. * * @return SSL/TLS protocol version being used in String format, * or "unknown". @@ -4168,8 +4369,10 @@ public void setIORecv(WolfSSLIORecvCallback callback) /* 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 +4409,10 @@ public void setIOSend(WolfSSLIOSendCallback callback) /* 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); + } } } @@ -4627,6 +4832,47 @@ public int setTls13SecretCb(WolfSSLTls13SecretCallback cb, Object ctx) return ret; } + /** + * Register session ticket callback. + * + * The callback registered by this method is called by native wolfSSL + * when a session ticket is received from the peer. + * + * @param cb callback to be registered with this SSL session + * @param ctx Object that will be passed back to user inside callback + * + * @return SSL_SUCCESS on success. + * NOT_COMPILED_IN if wolfSSL was not compiled with + * session ticket support, and other negative value on error. + * + * @throws IllegalStateException WolfSSLSession has been freed + * @throws WolfSSLJNIException Internal JNI error + */ + public int setSessionTicketCb(WolfSSLSessionTicketCallback cb, Object ctx) + throws IllegalStateException, WolfSSLJNIException { + + int ret = 0; + + confirmObjectIsActive(); + + synchronized (sslLock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.sslPtr, "entered setSessionTicketCb(" + + cb + ", ctx: " + ctx + ")"); + + ret = setSessionTicketCb(this.sslPtr); + if (ret == WolfSSL.SSL_SUCCESS) { + /* Set session ticket callback */ + internSessionTicketCb = cb; + + /* Set session ticket ctx Object, returned to user in cb */ + this.sessionTicketCtx = ctx; + } + } + + return ret; + } + /** * Do not free temporary arrays at end of handshake. * diff --git a/src/java/com/wolfssl/WolfSSLSessionTicketCallback.java b/src/java/com/wolfssl/WolfSSLSessionTicketCallback.java new file mode 100644 index 00000000..08bffc67 --- /dev/null +++ b/src/java/com/wolfssl/WolfSSLSessionTicketCallback.java @@ -0,0 +1,59 @@ +/* WolfSSLSessionTicketCallback.java + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +package com.wolfssl; + +/** + * wolfSSL session ticket callback interface. + * This interface specifies how applications should implement the session + * ticket callback class, to be used by wolfSSL when receiving a session + * ticket message. + *

+ * To use this interface, native wolfSSL must be compiled with + * HAVE_SESSION_TICKET defined. + *

+ * After implementing this interface, it should be passed as a parameter + * to the + * {@link WolfSSLSession#setSessionTicketCb(WolfSSLSessionTicketCallback, Object) + * WolfSSLSession.setSessionTicketCb()} method to be registered with the native + * wolfSSL library. + */ +public interface WolfSSLSessionTicketCallback { + + /** + * Callback method which is called when native wolfSSL receives a + * session ticket message. + * + * @param ssl the current SSL session object from which the + * callback was initiated + * @param ticket Session ticket received as a byte array + * @param ctx Optional user context if set when callback was + * registered + * + * @return 0 on success. wolfSSL does not currently do anything with + * the return value of this method, but is in place for + * future expansion if needed. + */ + public int sessionTicketCallback(WolfSSLSession ssl, byte[] ticket, + Object ctx); +} + + diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java index eb4666fa..6e7e9593 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java @@ -91,7 +91,8 @@ private void createCtx() throws WolfSSLException { 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 @@ private void createCtx() throws WolfSSLException { 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 @@ private void createCtx() throws WolfSSLException { * 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 TLSV23_Context() { } } + /** + * 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 b2699402..c25dd0f7 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java @@ -29,6 +29,7 @@ import com.wolfssl.WolfSSLJNIException; import com.wolfssl.WolfSSLSession; import com.wolfssl.WolfSSLALPNSelectCallback; +import com.wolfssl.WolfSSLSessionTicketCallback; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; @@ -72,6 +73,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 */ @@ -93,6 +96,12 @@ public class WolfSSLEngine extends SSLEngine { /* handshake completed */ private boolean handshakeFinished = false; + /* Last return values of ssl.connect() / ssl.accept(). Protected + * by ioLock. Can be used during state transitions to see if handshake + * has finished successfully from native wolfSSL perspective. */ + private int lastSSLConnectRet = WolfSSL.SSL_FAILURE; + private int lastSSLAcceptRet = WolfSSL.SSL_FAILURE; + /* closeNotify status when shutting down */ private boolean closeNotifySent = false; private boolean closeNotifyReceived = false; @@ -103,11 +112,16 @@ public class WolfSSLEngine extends SSLEngine { /* TLS 1.3 session ticket received (on client side) */ private boolean sessionTicketReceived = false; + /* Number of session tickets received, incremented in + * SessionTicketCB callback */ + private int sessionTicketCount = 0; + /* client/server mode has been set */ private boolean clientModeSet = false; private SendCB sendCb = null; private RecvCB recvCb = null; + private SessionTicketCB sessTicketCb = null; private ByteBuffer netData = null; private final Object netDataLock = new Object(); @@ -218,6 +232,7 @@ private synchronized void LoadCertAndKey() throws SSLException { try { this.engineHelper.LoadKeyAndCertChain(null, this); + certKeyLoaded = true; } catch (CertificateEncodingException | IOException | WolfSSLException e) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, @@ -239,6 +254,8 @@ private synchronized void LoadCertAndKey() throws SSLException { */ private void checkAndInitSSLEngine() throws SSLException { + int ret = 0; + synchronized (initLock) { if (!needInit) { @@ -248,6 +265,21 @@ private void checkAndInitSSLEngine() throws SSLException { LoadCertAndKey(); this.engineHelper.initHandshake(this); + + if ((this.ssl.dtls() == 1) && + (!this.engineHelper.getUseClientMode())) { + ret = this.ssl.sendHrrCookie(null); + if (ret == WolfSSL.SSL_SUCCESS) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Enabled sending of DTLS cookie in HelloRetryRequest"); + } + else if (ret < 0) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Failed to enable DTLS cookie in HelloRetryRequest, " + + "ret: " + ret); + } + } + needInit = false; closed = false; /* opened a connection */ } @@ -280,6 +312,12 @@ private void setSSLCallbacks() throws WolfSSLJNIException { ssl.setIOSend(sendCb); ssl.setIOReadCtx(this); ssl.setIOWriteCtx(this); + + /* Session ticket callback */ + if (sessTicketCb == null) { + sessTicketCb = new SessionTicketCB(); + } + ssl.setSessionTicketCb(sessTicketCb, this); } } @@ -305,12 +343,16 @@ private void unsetSSLCallbacks() throws WolfSSLJNIException { } private void initSSL() throws WolfSSLException, WolfSSLJNIException { + if (sendCb == null) { sendCb = new SendCB(); } if (recvCb == null) { recvCb = new RecvCB(); } + if (sessTicketCb == null) { + sessTicketCb = new SessionTicketCB(); + } /* will throw WolfSSLException if issue creating WOLFSSL */ ssl = new WolfSSLSession(ctx, false); @@ -383,6 +425,33 @@ private synchronized void UpdateCloseNotifyStatus() { } } + /** + * Returns if current error in WOLFSSL session should be considered + * fatal. Used in ClosingConnection() for detection of storing + * client cache entry. + * + * @param ssl WOLFSSL session to check error on + * + * @return true if error is not fatal, false if fatal + */ + private synchronized boolean sslErrorNotFatal(WolfSSLSession ssl) { + + int err; + + if (ssl == null) { + return false; + } + + err = ssl.getError(0); + if (err == 0 || + err == WolfSSL.SSL_ERROR_WANT_READ || + err == WolfSSL.SSL_ERROR_WANT_WRITE) { + return true; + } + + return false; + } + /** * Handles logic during shutdown * @@ -403,10 +472,17 @@ private synchronized int ClosingConnection() * not have an active error state, and the session has not been * stored previously. */ synchronized (ioLock) { - if (this.handshakeFinished && (ssl.getError(0) == 0) && + if (this.handshakeFinished && sslErrorNotFatal(ssl) && !this.sessionStored) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "saving WOLFSSL_SESSION into cache"); this.engineHelper.saveSession(); } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "not saving WOLFSSL_SESSION into cache, " + + "handshake not complete or already stored"); + } } /* get current close_notify state */ @@ -441,26 +517,45 @@ private synchronized int ClosingConnection() * 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(); + lastSSLConnectRet = ret; - 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(); + lastSSLAcceptRet = ret; + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ssl.accept() ret:err = " + ret + " : " + + ssl.getError(ret)); + } } } @@ -633,6 +728,12 @@ public synchronized SSLEngineResult wrap(ByteBuffer[] in, int ofst, int len, "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 +792,7 @@ else if ((produced > 0) && !inBoundOpen && else if (produced == 0) { /* continue handshake or application data */ if (!this.handshakeFinished) { - ret = DoHandshake(); + ret = DoHandshake(true); } else { try { @@ -756,6 +857,12 @@ else if (produced == 0) { "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 +937,22 @@ private synchronized int RecvAppData(ByteBuffer[] out, int ofst, int length) * 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 +974,11 @@ private synchronized int RecvAppData(ByteBuffer[] out, int ofst, int length) "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: @@ -945,6 +1069,9 @@ public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer[] out, int inRemaining = 0; int consumed = 0; int produced = 0; + long dtlsPrevDropCount = 0; + long dtlsCurrDropCount = 0; + int prevSessionTicketCount = 0; byte[] tmp; /* Set initial status for SSLEngineResult return */ @@ -1024,6 +1151,12 @@ public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer[] out, "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 +1199,19 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && } } else { + /* Get previous DTLS drop count and session ticket count, + * before we process any incomming data. Allows us to set + * BUFFER_UNDERFLOW status (or not if packet decrypt failed + * and was dropped) */ + synchronized (ioLock) { + dtlsPrevDropCount = ssl.getDtlsMacDropCount(); + prevSessionTicketCount = this.sessionTicketCount; + } 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 @@ -1084,6 +1224,7 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && status = SSLEngineResult.Status.BUFFER_OVERFLOW; } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "receiving application data"); ret = RecvAppData(out, ofst, length); @@ -1093,17 +1234,32 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && 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 +1281,7 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && } } } - } + } /* end DoHandshake() / RecvAppData() */ if (outBoundOpen == false || this.closeNotifySent) { /* Mark SSLEngine status as CLOSED */ @@ -1138,9 +1294,18 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && 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 */ @@ -1165,13 +1330,33 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && } } + /* Get DTLS drop count after data has been processed. To be + * used below when setting BUFFER_UNDERFLOW status. */ + synchronized (ioLock) { + dtlsCurrDropCount = ssl.getDtlsMacDropCount(); + } + + /* Detect if we need to set BUFFER_UNDERFLOW. + * If we consume data in unwrap() but it's just a session + * ticket, we don't set BUFFER_UNDERFLOW and just continue + * on to set status as OK. */ 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; + (this.toSend != null && this.toSend.length == 0)) + && (prevSessionTicketCount == + this.sessionTicketCount)) { + + if ((this.ssl.dtls() == 0) || + (this.handshakeFinished && + (dtlsPrevDropCount == dtlsCurrDropCount))) { + /* 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 +1376,19 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && 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 +1439,12 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && "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, "==========================================================="); } @@ -1261,6 +1461,32 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP && return new SSLEngineResult(status, hs, consumed, produced); } + /** + * Return if native ssl.connect() or ssl.accept() have finished + * successfully or not. Used to correctly set this.handshakeFinished + * from SetHandshakeStatus(). + * + * Caller of this method should protect it with lock on ioLock. + * + * @return true if native handshake finished, otherwise false. + */ + private boolean sslConnectAcceptSuccess() { + boolean client = this.engineHelper.getUseClientMode(); + + if (client) { + if (this.lastSSLConnectRet == WolfSSL.SSL_SUCCESS) { + return true; + } + } + else { + if (this.lastSSLAcceptRet == WolfSSL.SSL_SUCCESS) { + return true; + } + } + + return false; + } + /** * Sets handshake status after I/O operation of unwrap(), helper function. */ @@ -1306,7 +1532,11 @@ else if (!this.outBoundOpen && !this.closeNotifySent) { else { synchronized (netDataLock) { synchronized (ioLock) { - if (ssl.handshakeDone() && this.toSend == null) { + if (sslConnectAcceptSuccess() && ssl.handshakeDone() && + (this.toSend == null) && + (this.nativeWantsToWrite == 0) && + (this.nativeWantsToRead == 0)) { + this.handshakeFinished = true; hs = SSLEngineResult.HandshakeStatus.FINISHED; this.engineHelper.getSession().updateStoredSessionValues(); @@ -1336,6 +1566,9 @@ else if (err == WolfSSL.SSL_ERROR_WANT_READ) { 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 +1722,13 @@ public synchronized boolean sessionResumed() throws SSLException { 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 +1765,20 @@ public synchronized void beginHandshake() throws SSLException { "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 +1797,9 @@ else if (!this.needInit && !this.handshakeFinished) { 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 +2104,30 @@ protected synchronized int internalSendCb(byte[] in, int sz) { 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 +2190,15 @@ protected synchronized int internalRecvCb(byte[] toRead, int sz) { 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); @@ -1942,6 +2211,32 @@ protected synchronized int internalRecvCb(byte[] toRead, int sz) { } } + /** + * Internal session ticket callback. Called when native wolfSSL + * receives a session ticket from peer. + * + * @param ticket byte array containing session ticket data + * + * @return 0 on success, negative value on error + */ + protected synchronized int internalSessionTicketCb(byte[] ticket) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "entered internalSessionTicketCb()"); + + if (ticket != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Session ticket received, length = " + ticket.length); + if (ticket.length > 0) { + this.sessionTicketCount++; + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Total session tickets received by this SSLEngine: " + + this.sessionTicketCount); + } + + return 0; + } + private class SendCB implements WolfSSLIOSendCallback { protected SendCB() { @@ -1968,6 +2263,17 @@ public int receiveCallback(WolfSSLSession ssl, byte[] out, int sz, } + private class SessionTicketCB implements WolfSSLSessionTicketCallback { + + protected SessionTicketCB() { + } + + public int sessionTicketCallback(WolfSSLSession ssl, byte[] ticket, + Object engine) { + return ((WolfSSLEngine)engine).internalSessionTicketCb(ticket); + } + } + @SuppressWarnings("deprecation") @Override protected synchronized void finalize() throws Throwable { diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java index 099cb75c..cff34248 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java @@ -495,7 +495,8 @@ protected synchronized void setCiphers(String[] suites) 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 @@ protected synchronized void setProtocols(String[] p) } } - this.params.setProtocols(WolfSSLUtil.sanitizeProtocols(p)); + this.params.setProtocols( + WolfSSLUtil.sanitizeProtocols(p, WolfSSL.TLS_VERSION.INVALID)); } /** @@ -541,7 +543,8 @@ protected synchronized void setProtocols(String[] p) * @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 @@ protected synchronized String[] getProtocols() { * @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 @@ private void setLocalProtocol(String[] p) } 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 @@ private void setLocalProtocol(String[] p) } } + /* Note: No SSL_OP_NO_* for DTLS in native wolfSSL */ if (set[0] == false) { mask |= WolfSSL.SSL_OP_NO_TLSv1_3; } @@ -1140,13 +1147,75 @@ private void setLocalSupportedCurves() throws SSLException { } } + 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 setLocalExtendedMasterSecret() { + /* Native wolfSSL enables TLS Extended Master Secret by default. + * Check the Java System property (jdk.tls.useExtendedMasterSecret) + * to see if the user has explicitly disabled it. */ + int ret; + boolean useEMS = WolfSSLUtil.useExtendedMasterSecret(); + + if (!useEMS) { + ret = this.ssl.disableExtendedMasterSecret(); + if (ret == WolfSSL.SSL_SUCCESS) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "TLS Extended Master Secret disabled due to " + + "jdk.tls.useExtendedMasterSecret System property"); + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Failed to disable TLS Extended Master Secret, " + + "ret = " + ret); + } + } + else { + if (WolfSSL.isEnabledTLSExtendedMasterSecret() == 1) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "using TLS Extended Master Secret"); + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "not using TLS Extended Master Secret, not compiled in"); + } + } + } + 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 +1223,8 @@ private void setLocalParams(SSLSocket socket, SSLEngine engine) this.setLocalSecureRenegotiation(); this.setLocalSigAlgorithms(); this.setLocalSupportedCurves(); + this.setLocalMaximumPacketSize(); + this.setLocalExtendedMasterSecret(); } /** diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java index b7e79a36..8adc437f 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 @@ protected synchronized WolfSSLParameters copy() { 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 @@ void setApplicationProtocols(String[] protocols) { 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 33be2265..a2b34743 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java @@ -21,9 +21,11 @@ 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; +import com.wolfssl.provider.jsse.WolfSSLJDK8Helper; /** * WolfSSLParametersHelper class @@ -37,6 +39,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 +77,12 @@ public Object run() { case "setEndpointIdentificationAlgorithm": setEndpointIdentificationAlgorithm = m; continue; + case "getMaximumPacketSize": + getMaximumPacketSize = m; + continue; + case "setMaximumPacketSize": + setMaximumPacketSize = m; + continue; default: continue; } @@ -112,15 +122,17 @@ protected static SSLParameters decoupleParams(WolfSSLParameters in) { 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 +145,15 @@ protected static SSLParameters decoupleParams(WolfSSLParameters in) { 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 +161,18 @@ protected static SSLParameters decoupleParams(WolfSSLParameters in) { } } + /* 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 +218,15 @@ protected static void importParams(SSLParameters in, 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 +238,13 @@ protected static void importParams(SSLParameters in, 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 +253,19 @@ protected static void importParams(SSLParameters in, } } + /* 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 76fff4b4..a6aa1c3f 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java @@ -134,6 +134,10 @@ public WolfSSLProvider() { 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 fc9fe2cd..49d6e255 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLServerSocket.java @@ -211,7 +211,8 @@ synchronized public String[] getSupportedProtocols() { 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 @@ synchronized public String[] getEnabledProtocols() { 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 @@ synchronized public void setEnabledProtocols(String[] protocols) 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 55d752d5..c60a8883 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.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLDebug; import com.wolfssl.WolfSSLException; @@ -50,7 +51,8 @@ public 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: @@ -63,26 +65,40 @@ public WolfSSLUtil() { * * @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 = Security.getProperty("jdk.tls.disabledAlgorithms"); List disabledList = null; - /* If system property not set, no filtering needed */ - if (disabledAlgos == null || disabledAlgos.isEmpty()) { - return protocols; - } - WolfSSLDebug.log(WolfSSLUtil.class, WolfSSLDebug.INFO, "sanitizing enabled protocols"); WolfSSLDebug.log(WolfSSLUtil.class, WolfSSLDebug.INFO, "jdk.tls.disabledAlgorithms: " + disabledAlgos); + /* 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 */ disabledAlgos = disabledAlgos.replaceAll(", ",","); disabledList = Arrays.asList(disabledAlgos.split(",")); @@ -270,6 +286,35 @@ protected static boolean sessionCacheDisabled() { return false; } + /** + * Return if TLS Extended Master Secret support has been enabled or + * disabled via the following System property: + * + * jdk.tls.useExtendedMasterSecret + * + * If property is not set (null) or an empty string, we default to + * leaving TLS Extended Master Secret enabled. + * + * @return true if enabled, otherwise false + */ + protected static boolean useExtendedMasterSecret() { + + String useEMS = + System.getProperty("jdk.tls.useExtendedMasterSecret"); + + /* Native wolfSSL defaults to having extended master secret support + * enabled. Do the same here if property not set or empty. */ + if (useEMS == null || useEMS.isEmpty()) { + return true; + } + + if (useEMS.equalsIgnoreCase("false")) { + return false; + } + + return true; + } + /** * Check given KeyStore against any pre-defind requirements for * KeyStore use, including the following. diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java index 27618a7e..c367e55e 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java @@ -96,31 +96,33 @@ public static void testProviderInstallationAtRuntime() fail("Failed to get proper wolfJSSE provider name"); } - /* populate enabledProtocols */ - for (int i = 0; i < allProtocols.length; i++) { - try { - SSLContext ctx = SSLContext.getInstance(allProtocols[i], - ctxProvider); - - if (WolfSSLTestFactory.securityPropContains( - "jdk.tls.disabledAlgorithms", allProtocols[i])) { - /* skip adding, protocol has been disabled */ - continue; - } - - enabledProtocols.add(allProtocols[i]); - - } catch (NoSuchAlgorithmException e) { - /* protocol not enabled */ - } - } - try { tf = new WolfSSLTestFactory(); } catch (WolfSSLException e) { // TODO Auto-generated catch block e.printStackTrace(); } + + /* populate enabledProtocols */ + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + for (int i = 0; i < allProtocols.length; i++) { + try { + SSLContext ctx = SSLContext.getInstance(allProtocols[i], + ctxProvider); + + if (WolfSSLTestFactory.securityPropContains( + "jdk.tls.disabledAlgorithms", allProtocols[i])) { + /* skip adding, protocol has been disabled */ + continue; + } + + enabledProtocols.add(allProtocols[i]); + + } catch (NoSuchAlgorithmException e) { + /* protocol not enabled */ + } + } + } /* jdkTlsDisabledAlgorithmsLock */ } @Test @@ -414,7 +416,7 @@ private ArrayList buildExpectedDefaultProtocolList( /* already sorted highest to lowest (ie TLSv1.3, ..., TLSv1.1) */ List enabledNativeProtocols = Arrays.asList(WolfSSL.getProtocols()); - if (ctxProtocol == "TLS") { + if (ctxProtocol.equals("TLS")) { if (enabledNativeProtocols.contains("TLSv1.3")) { expected.add("TLSv1.3"); } @@ -429,7 +431,7 @@ private ArrayList buildExpectedDefaultProtocolList( } } - else if (ctxProtocol == "TLSv1.3") { + else if (ctxProtocol.equals("TLSv1.3")) { if (enabledNativeProtocols.contains("TLSv1.3")) { expected.add("TLSv1.3"); } @@ -444,7 +446,7 @@ else if (ctxProtocol == "TLSv1.3") { } } - else if (ctxProtocol == "TLSv1.2") { + else if (ctxProtocol.equals("TLSv1.2")) { if (enabledNativeProtocols.contains("TLSv1.2")) { expected.add("TLSv1.2"); } @@ -456,7 +458,7 @@ else if (ctxProtocol == "TLSv1.2") { } } - else if (ctxProtocol == "TLSv1.1") { + else if (ctxProtocol.equals("TLSv1.1")) { if (enabledNativeProtocols.contains("TLSv1.1")) { expected.add("TLSv1.1"); } @@ -465,7 +467,7 @@ else if (ctxProtocol == "TLSv1.1") { } } - else if (ctxProtocol == "TLSv1") { + else if (ctxProtocol.equals("TLSv1")) { if (enabledNativeProtocols.contains("TLSv1")) { expected.add("TLSv1"); } @@ -496,68 +498,121 @@ public void testJdkTlsDisabledAlgorithms() throws NoSuchProviderException, fail("WolfSSL.getProtocols() returned null"); } - /* Save original property value to reset after test */ - String originalProperty = - Security.getProperty("jdk.tls.disabledAlgorithms"); - if (originalProperty == null) { - /* Default back to empty string, otherwise we may get a NullPointerException when - * trying to restore this back to the original value later */ - originalProperty = ""; - } + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + /* Save original property value to reset after test */ + String originalProperty = + Security.getProperty("jdk.tls.disabledAlgorithms"); + if (originalProperty == null) { + /* Default back to empty string, otherwise we may get a + * NullPointerException when trying to restore this back to + * the original value later */ + originalProperty = ""; + } - /* Test with no protocols disabled */ - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - for (int i = 0; i < allProtocols.length; i++) { + /* Test with no protocols disabled */ + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + for (int i = 0; i < allProtocols.length; i++) { - if (!enabledNativeProtocols.contains(allProtocols[i])) { - /* protocol not available in native library, skip */ - continue; - } + if (!enabledNativeProtocols.contains(allProtocols[i])) { + /* protocol not available in native library, skip */ + continue; + } - ctx = SSLContext.getInstance(allProtocols[i]); - ctx.init(null, null, null); + ctx = SSLContext.getInstance(allProtocols[i]); + ctx.init(null, null, null); - expectedList = buildExpectedDefaultProtocolList(allProtocols[i]); - defaultSSLContextProtocols = - ctx.getDefaultSSLParameters().getProtocols(); + expectedList = + buildExpectedDefaultProtocolList(allProtocols[i]); + defaultSSLContextProtocols = + ctx.getDefaultSSLParameters().getProtocols(); - if (!Arrays.equals(defaultSSLContextProtocols, - expectedList.toArray(new String[expectedList.size()]))) { - System.out.print("\t... failed"); - fail("Default SSLContext protocol list did not " + - "match expected"); - } + if (!Arrays.equals(defaultSSLContextProtocols, + expectedList.toArray( + new String[expectedList.size()]))) { + System.out.print("\t... failed"); + fail("Default SSLContext(" + allProtocols[i] + + ") protocol list did not match " + + "expected. Got: " + + Arrays.toString(defaultSSLContextProtocols) + + " Expected: " + Arrays.toString(expectedList.toArray( + new String[expectedList.size()]))); + } - /* Also test SSLSocket.getEnabledProtocols() */ - sf = ctx.getSocketFactory(); - sock = (SSLSocket)sf.createSocket(); - String[] sockEnabledProtocols = sock.getEnabledProtocols(); + /* Also test SSLSocket.getEnabledProtocols() */ + sf = ctx.getSocketFactory(); + sock = (SSLSocket)sf.createSocket(); + String[] sockEnabledProtocols = sock.getEnabledProtocols(); - if (!Arrays.equals(sockEnabledProtocols, - expectedList.toArray(new String[expectedList.size()]))) { - System.out.print("\t... failed"); - fail("Default SSLSocket protocol list did not " + - "match expected"); + if (!Arrays.equals(sockEnabledProtocols, + expectedList.toArray( + new String[expectedList.size()]))) { + System.out.print("\t... failed"); + fail("Default SSLSocket protocol list did not " + + "match expected"); + } } - } - /* Test with each allProtocol disabled individually */ - for (int i = 0; i < allProtocols.length; i++) { - Security.setProperty("jdk.tls.disabledAlgorithms", allProtocols[i]); - for (int j = 0; j < allProtocols.length; j++) { + /* Test with each allProtocol disabled individually */ + for (int i = 0; i < allProtocols.length; i++) { + Security.setProperty("jdk.tls.disabledAlgorithms", + allProtocols[i]); + for (int j = 0; j < allProtocols.length; j++) { + + if (!enabledNativeProtocols.contains(allProtocols[j])) { + /* protocol not available in native library, skip */ + continue; + } + + ctx = SSLContext.getInstance(allProtocols[j]); + ctx.init(null, null, null); + + expectedList = + buildExpectedDefaultProtocolList(allProtocols[j]); + /* remove protocol under test */ + expectedList.remove(allProtocols[i]); + defaultSSLContextProtocols = + ctx.getDefaultSSLParameters().getProtocols(); + + if (!Arrays.equals(defaultSSLContextProtocols, + expectedList.toArray( + new String[expectedList.size()]))) { + System.out.print("\t... failed"); + fail("Default SSLContext protocol list did not " + + "match expected"); + } + + /* Also test SSLSocket.getEnabledProtocols() */ + sf = ctx.getSocketFactory(); + sock = (SSLSocket)sf.createSocket(); + String[] sockEnabledProtocols = sock.getEnabledProtocols(); + + if (!Arrays.equals(sockEnabledProtocols, + expectedList.toArray( + new String[expectedList.size()]))) { + System.out.print("\t... failed"); + fail("Default SSLSocket protocol list did not " + + "match expected"); + } + } + } + + /* Test with TLSv1, TLSv1.1 protocols disabled */ + Security.setProperty("jdk.tls.disabledAlgorithms", + "TLSv1, TLSv1.1"); + for (int i = 0; i < allProtocols.length; i++) { - if (!enabledNativeProtocols.contains(allProtocols[j])) { + if (!enabledNativeProtocols.contains(allProtocols[i])) { /* protocol not available in native library, skip */ continue; } - ctx = SSLContext.getInstance(allProtocols[j]); + ctx = SSLContext.getInstance(allProtocols[i]); ctx.init(null, null, null); expectedList = - buildExpectedDefaultProtocolList(allProtocols[j]); - /* remove protocol under test */ - expectedList.remove(allProtocols[i]); + buildExpectedDefaultProtocolList(allProtocols[i]); + expectedList.remove("TLSv1"); + expectedList.remove("TLSv1.1"); defaultSSLContextProtocols = ctx.getDefaultSSLParameters().getProtocols(); @@ -582,88 +637,53 @@ public void testJdkTlsDisabledAlgorithms() throws NoSuchProviderException, "match expected"); } } - } - - /* Test with TLSv1, TLSv1.1 protocols disabled */ - Security.setProperty("jdk.tls.disabledAlgorithms", "TLSv1, TLSv1.1"); - for (int i = 0; i < allProtocols.length; i++) { - - if (!enabledNativeProtocols.contains(allProtocols[i])) { - /* protocol not available in native library, skip */ - continue; - } - - ctx = SSLContext.getInstance(allProtocols[i]); - ctx.init(null, null, null); - - expectedList = buildExpectedDefaultProtocolList(allProtocols[i]); - expectedList.remove("TLSv1"); - expectedList.remove("TLSv1.1"); - defaultSSLContextProtocols = - ctx.getDefaultSSLParameters().getProtocols(); - - if (!Arrays.equals(defaultSSLContextProtocols, - expectedList.toArray(new String[expectedList.size()]))) { - System.out.print("\t... failed"); - fail("Default SSLContext protocol list did not " + - "match expected"); - } - /* Also test SSLSocket.getEnabledProtocols() */ - sf = ctx.getSocketFactory(); - sock = (SSLSocket)sf.createSocket(); - String[] sockEnabledProtocols = sock.getEnabledProtocols(); - - if (!Arrays.equals(sockEnabledProtocols, - expectedList.toArray( - new String[expectedList.size()]))) { - System.out.print("\t... failed"); - fail("Default SSLSocket protocol list did not " + - "match expected"); - } - } - - /* Test with TLSv1.1, TLSv1.2 protocols disabled */ - Security.setProperty("jdk.tls.disabledAlgorithms", "TLSv1.1, TLSv1.2"); - for (int i = 0; i < allProtocols.length; i++) { + /* Test with TLSv1.1, TLSv1.2 protocols disabled */ + Security.setProperty("jdk.tls.disabledAlgorithms", + "TLSv1.1, TLSv1.2"); + for (int i = 0; i < allProtocols.length; i++) { - if (!enabledNativeProtocols.contains(allProtocols[i])) { - /* protocol not available in native library, skip */ - continue; - } + if (!enabledNativeProtocols.contains(allProtocols[i])) { + /* protocol not available in native library, skip */ + continue; + } - ctx = SSLContext.getInstance(allProtocols[i]); - ctx.init(null, null, null); + ctx = SSLContext.getInstance(allProtocols[i]); + ctx.init(null, null, null); - expectedList = buildExpectedDefaultProtocolList(allProtocols[i]); - expectedList.remove("TLSv1.1"); - expectedList.remove("TLSv1.2"); - defaultSSLContextProtocols = - ctx.getDefaultSSLParameters().getProtocols(); + expectedList = + buildExpectedDefaultProtocolList(allProtocols[i]); + expectedList.remove("TLSv1.1"); + expectedList.remove("TLSv1.2"); + defaultSSLContextProtocols = + ctx.getDefaultSSLParameters().getProtocols(); - if (!Arrays.equals(defaultSSLContextProtocols, - expectedList.toArray(new String[expectedList.size()]))) { - System.out.print("\t... failed"); - fail("Default SSLContext protocol list did not " + - "match expected"); - } + if (!Arrays.equals(defaultSSLContextProtocols, + expectedList.toArray( + new String[expectedList.size()]))) { + System.out.print("\t... failed"); + fail("Default SSLContext protocol list did not " + + "match expected"); + } - /* Also test SSLSocket.getEnabledProtocols() */ - sf = ctx.getSocketFactory(); - sock = (SSLSocket)sf.createSocket(); - String[] sockEnabledProtocols = sock.getEnabledProtocols(); + /* Also test SSLSocket.getEnabledProtocols() */ + sf = ctx.getSocketFactory(); + sock = (SSLSocket)sf.createSocket(); + String[] sockEnabledProtocols = sock.getEnabledProtocols(); - if (!Arrays.equals(sockEnabledProtocols, - expectedList.toArray( - new String[expectedList.size()]))) { - System.out.print("\t... failed"); - fail("Default SSLSocket protocol list did not " + - "match expected"); + if (!Arrays.equals(sockEnabledProtocols, + expectedList.toArray( + new String[expectedList.size()]))) { + System.out.print("\t... failed"); + fail("Default SSLSocket protocol list did not " + + "match expected"); + } } - } - /* Restore original system property value */ - Security.setProperty("jdk.tls.disabledAlgorithms", originalProperty); + /* Restore original system property value */ + Security.setProperty("jdk.tls.disabledAlgorithms", + originalProperty); + } /* jdkTlsDisabledAlgorithmsLock */ System.out.println("\t... passed"); } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 2f503bb2..ef37f489 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -24,10 +24,8 @@ import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLException; import com.wolfssl.provider.jsse.WolfSSLProvider; -import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.nio.channels.SocketChannel; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -39,11 +37,8 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; -import java.security.KeyStore; import java.util.Random; import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; import java.net.Socket; import java.net.InetSocketAddress; import javax.net.ssl.SSLContext; @@ -51,10 +46,8 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLException; @@ -65,8 +58,15 @@ import java.util.concurrent.atomic.AtomicIntegerArray; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import org.junit.Rule; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.rules.Timeout; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import java.util.Arrays; + +import com.wolfssl.provider.jsse.WolfSSLEngine; /** * @@ -75,26 +75,31 @@ public class WolfSSLEngineTest { public final static char[] jksPass = "wolfSSL test".toCharArray(); public final static String engineProvider = "wolfJSSE"; - private static boolean extraDebug = false; private static WolfSSLTestFactory tf; private SSLContext ctx = null; private static String allProtocols[] = { + "TLS", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", - "TLS" + "TLS", + "DTLSv1.3" }; private static ArrayList enabledProtocols = new ArrayList(); + /** + * Global timeout for all tests in this class. + */ + @Rule + public Timeout globalTimeout = new Timeout(60, TimeUnit.SECONDS); + @BeforeClass public static void testProviderInstallationAtRuntime() - throws NoSuchProviderException { - - SSLContext ctx; + throws NoSuchProviderException, WolfSSLException { System.out.println("WolfSSLEngine Class"); @@ -107,7 +112,7 @@ public static void testProviderInstallationAtRuntime() /* populate enabledProtocols */ for (int i = 0; i < allProtocols.length; i++) { try { - ctx = SSLContext.getInstance(allProtocols[i], "wolfJSSE"); + SSLContext.getInstance(allProtocols[i], "wolfJSSE"); enabledProtocols.add(allProtocols[i]); } catch (NoSuchAlgorithmException e) { @@ -118,8 +123,8 @@ public static void testProviderInstallationAtRuntime() try { tf = new WolfSSLTestFactory(); } catch (WolfSSLException e) { - // TODO Auto-generated catch block e.printStackTrace(); + throw e; } } @@ -164,49 +169,88 @@ public void testSSLEngineSetCipher() return; } - this.ctx = tf.createSSLContext("TLSv1.2", engineProvider); - e = this.ctx.createSSLEngine(); - if (e == null) { - error("\t\t... failed"); - fail("failed to create engine"); - return; - } + for (int i = 0; i < enabledProtocols.size(); i++) { - sup = e.getSupportedProtocols(); - for (String x : sup) { - if (x.equals("TLSv1.2")) { - ok = true; + /* 'TLS' is not a 'supported' protocol from + * SSLEngine.getSupportedProtocols(). That list returns + * Strings such as: TLSv1, TLSv1.2, DTLSv1.3, etc. */ + if (enabledProtocols.get(i).equals("TLS")) { + continue; } - } - if (!ok) { - error("\t\t... failed"); - fail("failed to find TLSv1.2 in supported protocols"); - } - sup = e.getEnabledProtocols(); - for (String x : sup) { - if (x.equals("TLSv1.2")) { - ok = true; + this.ctx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); + + e = this.ctx.createSSLEngine(); + if (e == null) { + error("\t\t... failed"); + fail("failed to create engine"); + return; } - } - if (!ok) { - error("\t\t... failed"); - fail("failed to find TLSv1.2 in enabled protocols"); - } - /* check supported cipher suites */ - sup = e.getSupportedCipherSuites(); - e.setEnabledCipherSuites(new String[] {sup[0]}); - if (e.getEnabledCipherSuites() == null || - !sup[0].equals(e.getEnabledCipherSuites()[0])) { - error("\t\t... failed"); - fail("unexpected empty cipher list"); + sup = e.getSupportedProtocols(); + for (String x : sup) { + if (x.equals(enabledProtocols.get(i))) { + ok = true; + } + } + if (!ok) { + error("\t\t... failed"); + fail("failed to find " + enabledProtocols.get(i) + + " in supported protocols"); + } + + sup = e.getEnabledProtocols(); + for (String x : sup) { + if (x.equals(enabledProtocols.get(i))) { + ok = true; + } + } + if (!ok) { + error("\t\t... failed"); + fail("failed to find " + enabledProtocols.get(i) + + " in enabled protocols"); + } + + /* check supported cipher suites */ + sup = e.getSupportedCipherSuites(); + e.setEnabledCipherSuites(new String[] {sup[0]}); + if (e.getEnabledCipherSuites() == null || + !sup[0].equals(e.getEnabledCipherSuites()[0])) { + error("\t\t... failed"); + fail("unexpected empty cipher list"); + } } + pass("\t\t... passed"); } @Test - public void testCipherConnection() + public void testCipherConnectionTLS() + throws NoSuchProviderException, NoSuchAlgorithmException, + KeyManagementException, KeyStoreException, CertificateException, + IOException, UnrecoverableKeyException { + + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + for (int i = 0; i < enabledProtocols.size(); i++) { + if (enabledProtocols.get(i).equals("TLS")) { + /* 'TLS' is not a 'supported' protocol from + * SSLEngine.getSupportedProtocols(). That list returns + * Strings such as: TLSv1, TLSv1.2, DTLSv1.3, etc. */ + continue; + } + + testCipherConnectionByProtocol(enabledProtocols.get(i)); + } + } + } + + /** + * Test the connection using the given protocol. + * + * Private method, called by testCipherConnectionTLS() + */ + private void testCipherConnectionByProtocol(String protocol) throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException { @@ -214,15 +258,15 @@ public void testCipherConnection() SSLEngine server; SSLEngine client; String cipher = null; - int ret, i; + int ret; String[] ciphers; String certType; Certificate[] certs; /* create new SSLEngine */ - System.out.print("\tBasic ciphersuiet connection"); + System.out.print("\tBasic connection: " + protocol); - this.ctx = tf.createSSLContext("TLS", engineProvider); + this.ctx = tf.createSSLContext(protocol, engineProvider); server = this.ctx.createSSLEngine(); client = this.ctx.createSSLEngine("wolfSSL client test", 11111); @@ -258,7 +302,7 @@ public void testCipherConnection() server.setNeedClientAuth(false); client.setUseClientMode(true); ret = tf.testConnection(server, client, new String[] { cipher }, - new String[] { "TLSv1.2" }, "Test cipher suite"); + new String[] { protocol }, "Test cipher suite"); if (ret != 0) { error("\t... failed"); fail("failed to create engine"); @@ -281,26 +325,24 @@ public void testCipherConnection() error("\t... failed"); fail("invalid client mode"); } - pass("\t... passed"); - System.out.print("\tclose connection"); + /* Test closing connection */ try { - /* test close connection */ tf.CloseConnection(server, client, false); } catch (SSLException ex) { - error("\t\t... failed"); + error("\t... failed"); fail("failed to create engine"); } /* check if inbound is still open */ if (!server.isInboundDone() || !client.isInboundDone()) { - error("\t\t... failed"); + error("\t... failed"); fail("inbound is not done"); } /* check if outbound is still open */ if (!server.isOutboundDone() || !client.isOutboundDone()) { - error("\t\t... failed"); + error("\t... failed"); fail("outbound is not done"); } @@ -308,11 +350,11 @@ public void testCipherConnection() try { server.closeInbound(); } catch (SSLException ex) { - error("\t\t... failed"); + error("\t... failed"); fail("close inbound failure"); } - pass("\t\t... passed"); + pass("\t... passed"); } @Test @@ -328,63 +370,71 @@ public void testBeginHandshake() /* create new SSLEngine */ System.out.print("\tbeginHandshake()"); - this.ctx = tf.createSSLContext("TLS", engineProvider); - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL begin handshake test", 11111); + for (int i = 0; i < enabledProtocols.size(); i++) { - /* Calling beginHandshake() before setUseClientMode() should throw - * IllegalStateException */ - try { - server.beginHandshake(); - error("\t\t... failed"); - fail("beginHandshake() before setUseClientMode() should throw " + - "IllegalStateException"); - } catch (IllegalStateException e) { - /* expected */ - } - try { - client.beginHandshake(); - error("\t\t... failed"); - fail("beginHandshake() before setUseClientMode() should throw " + - "IllegalStateException"); - } catch (IllegalStateException e) { - /* expected */ - } + this.ctx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); - /* Set client/server mode, disable auth to simplify tests below */ - server.setUseClientMode(false); - server.setNeedClientAuth(false); - client.setUseClientMode(true); + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine( + "wolfSSL begin handshake test", 11111); - try { - server.beginHandshake(); - client.beginHandshake(); - } catch (SSLException e) { - error("\t\t... failed"); - fail("failed to begin handshake"); - } + /* Calling beginHandshake() before setUseClientMode() should throw + * IllegalStateException */ + try { + server.beginHandshake(); + error("\t\t... failed"); + fail("beginHandshake() before setUseClientMode() should " + + "throw IllegalStateException"); + } catch (IllegalStateException e) { + /* expected */ + } - ret = tf.testConnection(server, client, null, null, "Test in/out bound"); - if (ret != 0) { - error("\t\t... failed"); - fail("failed to create engine"); - } + try { + client.beginHandshake(); + error("\t\t... failed"); + fail("beginHandshake() before setUseClientMode() should " + + "throw IllegalStateException"); + } catch (IllegalStateException e) { + /* expected */ + } - /* Calling beginHandshake() again should throw SSLException - * since renegotiation is not yet supported in wolfJSSE */ - try { - server.beginHandshake(); - error("\t\t... failed"); - fail("beginHandshake() called again should throw SSLException"); - } catch (SSLException e) { - /* expected */ - } - try { - client.beginHandshake(); - error("\t\t... failed"); - fail("beginHandshake() called again should throw SSLException"); - } catch (SSLException e) { - /* expected */ + /* Set client/server mode, disable auth to simplify tests below */ + server.setUseClientMode(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + + try { + server.beginHandshake(); + client.beginHandshake(); + } catch (SSLException e) { + error("\t\t... failed"); + fail("failed to begin handshake"); + } + + ret = tf.testConnection(server, client, null, null, + "Test in/out bound"); + if (ret != 0) { + error("\t\t... failed"); + fail("failed to create engine"); + } + + /* Calling beginHandshake() again should throw SSLException + * since renegotiation is not yet supported in wolfJSSE */ + try { + server.beginHandshake(); + error("\t\t... failed"); + fail("beginHandshake() called again should throw SSLException"); + } catch (SSLException e) { + /* expected */ + } + try { + client.beginHandshake(); + error("\t\t... failed"); + fail("beginHandshake() called again should throw SSLException"); + } catch (SSLException e) { + /* expected */ + } } pass("\t\t... passed"); @@ -403,38 +453,45 @@ public void testConnectionOutIn() /* create new SSLEngine */ System.out.print("\tisIn/OutboundDone()"); - this.ctx = tf.createSSLContext("TLS", engineProvider); - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL in/out test", 11111); + for (int i = 0; i < enabledProtocols.size(); i++) { - server.setUseClientMode(false); - server.setNeedClientAuth(false); - client.setUseClientMode(true); - ret = tf.testConnection(server, client, null, null, "Test in/out bound"); - if (ret != 0) { - error("\t\t... failed"); - fail("failed to create engine"); - } + this.ctx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); - /* check if inbound is still open */ - if (server.isInboundDone() && client.isInboundDone()) { - error("\t\t... failed"); - fail("inbound done too early"); - } + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL in/out test", 11111); - /* check if outbound is still open */ - if (server.isOutboundDone() && client.isOutboundDone()) { - error("\t\t... failed"); - fail("outbound done too early"); - } + server.setUseClientMode(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); - /* close inbound before peer responded to shutdown should fail */ - try { - server.closeInbound(); - error("\t\t... failed"); - fail("was able to incorrectly close inbound"); - } catch (SSLException ex) { - /* expected to fail here */ + ret = tf.testConnection(server, client, null, null, + "Test in/out bound"); + if (ret != 0) { + error("\t\t... failed"); + fail("failed to create engine"); + } + + /* check if inbound is still open */ + if (server.isInboundDone() && client.isInboundDone()) { + error("\t\t... failed"); + fail("inbound done too early"); + } + + /* check if outbound is still open */ + if (server.isOutboundDone() && client.isOutboundDone()) { + error("\t\t... failed"); + fail("outbound done too early"); + } + + /* close inbound before peer responded to shutdown should fail */ + try { + server.closeInbound(); + error("\t\t... failed"); + fail("was able to incorrectly close inbound"); + } catch (SSLException ex) { + /* expected to fail here */ + } } pass("\t\t... passed"); @@ -446,67 +503,71 @@ public void testSetUseClientMode() KeyManagementException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException { - int ret; SSLEngine client; SSLEngine server; System.out.print("\tsetUseClientMode()"); - /* expected to fail, not calling setUseClientMode() */ - this.ctx = tf.createSSLContext("TLS", engineProvider); - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL test", 11111); - server.setWantClientAuth(false); - server.setNeedClientAuth(false); - try { - ret = tf.testConnection(server, client, null, null, "Testing"); - error("\t\t... failed"); - fail("did not fail without setUseClientMode()"); - } catch (IllegalStateException e) { - /* expected */ - } + for (int i = 0; i < enabledProtocols.size(); i++) { - /* expected to fail, only calling client.setUseClientMode() */ - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL test", 11111); - server.setWantClientAuth(false); - server.setNeedClientAuth(false); - client.setUseClientMode(true); - try { - ret = tf.testConnection(server, client, null, null, "Testing"); - error("\t\t... failed"); - fail("did not fail without server.setUseClientMode()"); - } catch (IllegalStateException e) { - /* expected */ - } + /* expected to fail, not calling setUseClientMode() */ + this.ctx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); - /* expected to fail, only calling client.setUseClientMode() */ - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL test", 11111); - server.setWantClientAuth(false); - server.setNeedClientAuth(false); - server.setUseClientMode(false); - try { - ret = tf.testConnection(server, client, null, null, "Testing"); - error("\t\t... failed"); - fail("did not fail without client.setUseClientMode()"); - } catch (IllegalStateException e) { - /* expected */ - } + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + try { + tf.testConnection(server, client, null, null, "Testing"); + error("\t\t... failed"); + fail("did not fail without setUseClientMode()"); + } catch (IllegalStateException e) { + /* expected */ + } - /* expected to succeed, both setUseClientMode() set */ - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL test", 11111); - server.setWantClientAuth(false); - server.setNeedClientAuth(false); - client.setUseClientMode(true); - server.setUseClientMode(false); - try { - ret = tf.testConnection(server, client, null, null, "Testing"); - } catch (IllegalStateException e) { - e.printStackTrace(); - error("\t\t... failed"); - fail("failed with setUseClientMode(), should succeed"); + /* expected to fail, only calling client.setUseClientMode() */ + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + try { + tf.testConnection(server, client, null, null, "Testing"); + error("\t\t... failed"); + fail("did not fail without server.setUseClientMode()"); + } catch (IllegalStateException e) { + /* expected */ + } + + /* expected to fail, only calling client.setUseClientMode() */ + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + server.setUseClientMode(false); + try { + tf.testConnection(server, client, null, null, "Testing"); + error("\t\t... failed"); + fail("did not fail without client.setUseClientMode()"); + } catch (IllegalStateException e) { + /* expected */ + } + + /* expected to succeed, both setUseClientMode() set */ + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + server.setUseClientMode(false); + try { + tf.testConnection(server, client, null, null, "Testing"); + } catch (IllegalStateException e) { + e.printStackTrace(); + error("\t\t... failed"); + fail("failed with setUseClientMode(), should succeed"); + } } pass("\t\t... passed"); @@ -525,42 +586,50 @@ public void testMutualAuth() /* create new SSLEngine */ System.out.print("\tMutual authentication"); - /* success case */ - this.ctx = tf.createSSLContext("TLS", engineProvider); - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL auth test", 11111); + for (int i = 0; i < enabledProtocols.size(); i++) { - server.setWantClientAuth(true); - server.setNeedClientAuth(true); - client.setUseClientMode(true); - server.setUseClientMode(false); - ret = tf.testConnection(server, client, null, null, "Test mutual auth"); - if (ret != 0) { - error("\t\t... failed"); - fail("failed to create connection with engine"); - } + /* success case */ + this.ctx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); - /* want client auth should be overwritten by need client auth */ - if (!server.getNeedClientAuth() || server.getWantClientAuth()) { - error("\t\t... failed"); - fail("failed with mutual auth getter check"); - } + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL auth test", 11111); + + server.setWantClientAuth(true); + server.setNeedClientAuth(true); + client.setUseClientMode(true); + server.setUseClientMode(false); + ret = tf.testConnection(server, client, null, null, + "Test mutual auth"); + if (ret != 0) { + error("\t\t... failed"); + fail("failed to create connection with engine"); + } + + /* want client auth should be overwritten by need client auth */ + if (!server.getNeedClientAuth() || server.getWantClientAuth()) { + error("\t\t... failed"); + fail("failed with mutual auth getter check"); + } - /* fail case */ - this.ctx = tf.createSSLContext("TLS", engineProvider, + /* fail case */ + this.ctx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider, tf.createTrustManager("SunX509", tf.serverJKS, engineProvider), tf.createKeyManager("SunX509", tf.serverJKS, engineProvider)); - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL auth fail test", 11111); + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL auth fail test", 11111); - server.setWantClientAuth(true); - server.setNeedClientAuth(true); - client.setUseClientMode(true); - server.setUseClientMode(false); - ret = tf.testConnection(server, client, null, null, "Test in/out bound"); - if (ret == 0) { - error("\t\t... failed"); - fail("failed to create engine"); + server.setWantClientAuth(true); + server.setNeedClientAuth(true); + client.setUseClientMode(true); + server.setUseClientMode(false); + ret = tf.testConnection(server, client, null, null, + "Test in/out bound"); + if (ret == 0) { + error("\t\t... failed"); + fail("failed to create engine"); + } } pass("\t\t... passed"); @@ -626,31 +695,36 @@ public void testSetWantNeedClientAuth_ClientServerDefaultKeyManager() System.out.print("\tsetWantClientAuth(default KM)"); - for (PeerAuthConfig c : configsDefaultManagers) { - - sCtx = tf.createSSLContext("TLS", engineProvider); - server = sCtx.createSSLEngine(); - server.setUseClientMode(false); - server.setWantClientAuth(c.serverWantClientAuth); - server.setNeedClientAuth(c.serverNeedClientAuth); - - cCtx = tf.createSSLContext("TLS", engineProvider); - client = cCtx.createSSLEngine("wolfSSL test case", 11111); - client.setUseClientMode(true); - client.setWantClientAuth(c.clientWantClientAuth); - client.setNeedClientAuth(c.clientNeedClientAuth); - - ret = tf.testConnection(server, client, null, null, "Test"); - if ((c.expectSuccess && ret != 0) || - (!c.expectSuccess && ret == 0)) { - error("\t... failed"); - fail("SSLEngine want/needClientAuth failed: \n" + - "\n cWantClientAuth = " + c.clientWantClientAuth + - "\n cNeedClientAuth = " + c.clientNeedClientAuth + - "\n sWantClientAuth = " + c.serverWantClientAuth + - "\n sNeedClientAuth = " + c.serverNeedClientAuth + - "\n expectSuccess = " + c.expectSuccess + - "\n got ret = " + ret); + for (int i = 0; i < enabledProtocols.size(); i++) { + for (PeerAuthConfig c : configsDefaultManagers) { + + sCtx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret + + "\n protocol = " + enabledProtocols.get(i)); + } } } @@ -692,33 +766,39 @@ public void testSetWantNeedClientAuth_ClientNoKeyManager() System.out.print("\tsetWantClientAuth(no client KM)"); - for (PeerAuthConfig c : configs) { - - sCtx = tf.createSSLContext("TLS", engineProvider); - server = sCtx.createSSLEngine(); - server.setUseClientMode(false); - server.setWantClientAuth(c.serverWantClientAuth); - server.setNeedClientAuth(c.serverNeedClientAuth); - - cCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, - tf.createTrustManager("SunX509", tf.clientJKS, engineProvider), - null); - client = cCtx.createSSLEngine("wolfSSL test case", 11111); - client.setUseClientMode(true); - client.setWantClientAuth(c.clientWantClientAuth); - client.setNeedClientAuth(c.clientNeedClientAuth); - - ret = tf.testConnection(server, client, null, null, "Test"); - if ((c.expectSuccess && ret != 0) || - (!c.expectSuccess && ret == 0)) { - error("\t... failed"); - fail("SSLEngine want/needClientAuth failed: \n" + - "\n cWantClientAuth = " + c.clientWantClientAuth + - "\n cNeedClientAuth = " + c.clientNeedClientAuth + - "\n sWantClientAuth = " + c.serverWantClientAuth + - "\n sNeedClientAuth = " + c.serverNeedClientAuth + - "\n expectSuccess = " + c.expectSuccess + - "\n got ret = " + ret); + for (int i = 0; i < enabledProtocols.size(); i++) { + for (PeerAuthConfig c : configs) { + + sCtx = tf.createSSLContext( + enabledProtocols.get(i), engineProvider); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContextNoDefaults( + enabledProtocols.get(i), engineProvider, + tf.createTrustManager("SunX509", tf.clientJKS, + engineProvider), + null); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret + + "\n protocol = " + enabledProtocols.get(i)); + } } } @@ -762,33 +842,39 @@ public void testSetWantNeedClientAuth_ServerNoKeyManager() System.out.print("\tsetWantClientAuth(no server KM)"); - for (PeerAuthConfig c : configs) { - - sCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, - tf.createTrustManager("SunX509", tf.clientJKS, engineProvider), - null); - server = sCtx.createSSLEngine(); - server.setUseClientMode(false); - server.setWantClientAuth(c.serverWantClientAuth); - server.setNeedClientAuth(c.serverNeedClientAuth); - - cCtx = tf.createSSLContext("TLS", engineProvider); - client = cCtx.createSSLEngine("wolfSSL test case", 11111); - client.setUseClientMode(true); - client.setWantClientAuth(c.clientWantClientAuth); - client.setNeedClientAuth(c.clientNeedClientAuth); - - ret = tf.testConnection(server, client, null, null, "Test"); - if ((c.expectSuccess && ret != 0) || - (!c.expectSuccess && ret == 0)) { - error("\t... failed"); - fail("SSLEngine want/needClientAuth failed: \n" + - "\n cWantClientAuth = " + c.clientWantClientAuth + - "\n cNeedClientAuth = " + c.clientNeedClientAuth + - "\n sWantClientAuth = " + c.serverWantClientAuth + - "\n sNeedClientAuth = " + c.serverNeedClientAuth + - "\n expectSuccess = " + c.expectSuccess + - "\n got ret = " + ret); + for (int i = 0; i < enabledProtocols.size(); i++) { + for (PeerAuthConfig c : configs) { + + sCtx = tf.createSSLContextNoDefaults(enabledProtocols.get(i), + engineProvider, + tf.createTrustManager("SunX509", tf.clientJKS, + engineProvider), + null); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContext(enabledProtocols.get(i), + engineProvider); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret + + "\n protocol = " + enabledProtocols.get(i)); + } } } @@ -858,35 +944,42 @@ public void checkServerTrusted(X509Certificate[] chain, System.out.print("\tsetWantClientAuth(ext KM all)"); - for (PeerAuthConfig c : configsDefaultManagers) { - - sCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, - trustAllCerts, - tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); - server = sCtx.createSSLEngine(); - server.setUseClientMode(false); - server.setWantClientAuth(c.serverWantClientAuth); - server.setNeedClientAuth(c.serverNeedClientAuth); - - cCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, - trustAllCerts, - tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); - client = cCtx.createSSLEngine("wolfSSL test case", 11111); - client.setUseClientMode(true); - client.setWantClientAuth(c.clientWantClientAuth); - client.setNeedClientAuth(c.clientNeedClientAuth); - - ret = tf.testConnection(server, client, null, null, "Test"); - if ((c.expectSuccess && ret != 0) || - (!c.expectSuccess && ret == 0)) { - error("\t... failed"); - fail("SSLEngine want/needClientAuth failed: \n" + - "\n cWantClientAuth = " + c.clientWantClientAuth + - "\n cNeedClientAuth = " + c.clientNeedClientAuth + - "\n sWantClientAuth = " + c.serverWantClientAuth + - "\n sNeedClientAuth = " + c.serverNeedClientAuth + - "\n expectSuccess = " + c.expectSuccess + - "\n got ret = " + ret); + for (int i = 0; i < enabledProtocols.size(); i++) { + for (PeerAuthConfig c : configsDefaultManagers) { + + sCtx = tf.createSSLContextNoDefaults(enabledProtocols.get(i), + engineProvider, + trustAllCerts, + tf.createKeyManager("SunX509", tf.clientJKS, + engineProvider)); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContextNoDefaults(enabledProtocols.get(i), + engineProvider, + trustAllCerts, + tf.createKeyManager("SunX509", tf.clientJKS, + engineProvider)); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret + + "\n protocol = " + enabledProtocols.get(i)); + } } } @@ -971,35 +1064,42 @@ public void checkServerTrusted(X509Certificate[] chain, System.out.print("\tsetWantClientAuth(ext KM no)"); - for (PeerAuthConfig c : configsDefaultManagers) { - - sCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, - trustNoClientCerts, - tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); - server = sCtx.createSSLEngine(); - server.setUseClientMode(false); - server.setWantClientAuth(c.serverWantClientAuth); - server.setNeedClientAuth(c.serverNeedClientAuth); - - cCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, - trustNoClientCerts, - tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); - client = cCtx.createSSLEngine("wolfSSL test case", 11111); - client.setUseClientMode(true); - client.setWantClientAuth(c.clientWantClientAuth); - client.setNeedClientAuth(c.clientNeedClientAuth); - - ret = tf.testConnection(server, client, null, null, "Test"); - if ((c.expectSuccess && ret != 0) || - (!c.expectSuccess && ret == 0)) { - error("\t... failed"); - fail("SSLEngine want/needClientAuth failed: \n" + - "\n cWantClientAuth = " + c.clientWantClientAuth + - "\n cNeedClientAuth = " + c.clientNeedClientAuth + - "\n sWantClientAuth = " + c.serverWantClientAuth + - "\n sNeedClientAuth = " + c.serverNeedClientAuth + - "\n expectSuccess = " + c.expectSuccess + - "\n got ret = " + ret); + for (int i = 0; i < enabledProtocols.size(); i++) { + for (PeerAuthConfig c : configsDefaultManagers) { + + sCtx = tf.createSSLContextNoDefaults(enabledProtocols.get(i), + engineProvider, + trustNoClientCerts, + tf.createKeyManager("SunX509", tf.clientJKS, + engineProvider)); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContextNoDefaults(enabledProtocols.get(i), + engineProvider, + trustNoClientCerts, + tf.createKeyManager("SunX509", tf.clientJKS, + engineProvider)); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret + + "\n protocol = " + enabledProtocols.get(i)); + } } } @@ -1028,51 +1128,56 @@ public void testReuseSession() Security.setProperty("wolfjsse.clientSessionCache.disabled", "false"); try { - /* create new SSLEngine */ - this.ctx = tf.createSSLContext("TLS", engineProvider); - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL client test", 11111); - - server.setUseClientMode(false); - server.setNeedClientAuth(false); - client.setUseClientMode(true); - ret = tf.testConnection(server, client, null, null, "Test reuse"); - if (ret != 0) { - error("\t\t\t... failed"); - fail("failed to create engine"); - } + for (int i = 0; i < enabledProtocols.size(); i++) { + /* create new SSLEngine */ + this.ctx = tf.createSSLContext(enabledProtocols.get(i), + engineProvider); + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL client test", 11111); + + server.setUseClientMode(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + ret = tf.testConnection(server, client, null, null, + "Test reuse"); + if (ret != 0) { + error("\t\t\t... failed"); + fail("failed to create engine"); + } - try { - /* test close connection */ - tf.CloseConnection(server, client, false); - } catch (SSLException ex) { - error("\t\t\t... failed"); - fail("failed to create engine"); - } + try { + /* test close connection */ + tf.CloseConnection(server, client, false); + } catch (SSLException ex) { + error("\t\t\t... failed"); + fail("failed to create engine"); + } - server = this.ctx.createSSLEngine(); - client = this.ctx.createSSLEngine("wolfSSL client test", 11111); - client.setEnableSessionCreation(false); - server.setUseClientMode(false); - server.setNeedClientAuth(false); - client.setUseClientMode(true); - ret = tf.testConnection(server, client, null, null, "Test reuse"); - if (ret != 0) { - error("\t\t\t... failed"); - fail("failed to create engine"); - } - try { - /* test close connection */ - tf.CloseConnection(server, client, false); - } catch (SSLException ex) { - error("\t\t\t... failed"); - fail("failed to create engine"); - } + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL client test", 11111); + client.setEnableSessionCreation(false); + server.setUseClientMode(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + ret = tf.testConnection(server, client, null, null, + "Test reuse"); + if (ret != 0) { + error("\t\t\t... failed"); + fail("failed to create engine"); + } + try { + /* test close connection */ + tf.CloseConnection(server, client, false); + } catch (SSLException ex) { + error("\t\t\t... failed"); + fail("failed to create engine"); + } - if (client.getEnableSessionCreation() || - !server.getEnableSessionCreation()) { - error("\t\t\t... failed"); - fail("bad enabled session creation"); + if (client.getEnableSessionCreation() || + !server.getEnableSessionCreation()) { + error("\t\t\t... failed"); + fail("bad enabled session creation"); + } } pass("\t\t\t... passed"); @@ -1212,6 +1317,7 @@ public void connect() throws Exception { ByteBuffer netData = null; ByteBuffer peerAppData = null; ByteBuffer peerNetData = null; + boolean readAgain = false; SocketChannel sock = null; @@ -1331,7 +1437,7 @@ public void connect() throws Exception { case OK: netData.flip(); while (netData.hasRemaining()) { - int sent = sock.write(netData); + sock.write(netData); } break; case CLOSED: @@ -1350,7 +1456,7 @@ public void connect() throws Exception { } } - /* read response */ + /* read response (might get TLS 1.3 session ticket instead) */ peerNetData.clear(); int recvd = sock.read(peerNetData); if (recvd > 0) { @@ -1370,14 +1476,55 @@ public void connect() throws Exception { 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) { @@ -1470,20 +1617,23 @@ public void testGetApplicationBufferSize() { SSLEngine engine; SSLSession session; - System.out.print("\tTesting getAppBufferSize"); + System.out.print("\tgetApplicationBufferSize()"); try { - /* create SSLContext */ - this.ctx = tf.createSSLContext("TLS", engineProvider); - - engine = this.ctx.createSSLEngine("test", 11111); - session = engine.getSession(); - appBufSz = session.getApplicationBufferSize(); - - /* expected to be 16384 */ - if (appBufSz != 16384) { - error("\t... failed"); - fail("got incorrect application buffer size"); + for (int i = 0; i < enabledProtocols.size(); i++) { + this.ctx = tf.createSSLContext(enabledProtocols.get(i), + engineProvider); + + engine = this.ctx.createSSLEngine("test", 11111); + session = engine.getSession(); + appBufSz = session.getApplicationBufferSize(); + + /* expected to be 16384 */ + if (appBufSz != 16384) { + error("\t... failed"); + fail("got incorrect application buffer size (" + + enabledProtocols.get(i) + ")"); + } } } catch (Exception e) { e.printStackTrace(); @@ -1501,28 +1651,31 @@ public void testGetPacketBufferSize() { SSLEngine engine; SSLSession session; - System.out.print("\tTesting getPacketBufferSize"); + System.out.print("\tgetPacketBufferSize()"); try { - /* create SSLContext */ - this.ctx = tf.createSSLContext("TLS", engineProvider); - - engine = this.ctx.createSSLEngine("test", 11111); - session = engine.getSession(); - packetBufSz = session.getPacketBufferSize(); - - /* expected to be 18437 */ - if (packetBufSz != 18437) { - error("\t... failed"); - fail("got incorrect packet buffer size"); + for (int i = 0; i < enabledProtocols.size(); i++) { + this.ctx = tf.createSSLContext(enabledProtocols.get(i), + engineProvider); + + engine = this.ctx.createSSLEngine("test", 11111); + session = engine.getSession(); + packetBufSz = session.getPacketBufferSize(); + + /* expected to be 18437 */ + if (packetBufSz != 18437) { + error("\t\t... failed"); + fail("got incorrect packet buffer size (" + + enabledProtocols.get(i) + ")"); + } } } catch (Exception e) { e.printStackTrace(); - error("\t... failed"); + error("\t\t... failed"); fail("unexpected Exception during getPacketBufferSize test"); } - pass("\t... passed"); + pass("\t\t... passed"); } @Test @@ -1540,9 +1693,6 @@ public void testSSLEngineBigInput() throws Exception { /* big input buffer to test, 16k */ byte[] bigInput = new byte[16384]; - SSLEngineResult cResult; - SSLEngineResult sResult; - System.out.print("\tTesting large data transfer"); try { @@ -1578,14 +1728,14 @@ public void testSSLEngineBigInput() throws Exception { while (!(client.isOutboundDone() && client.isInboundDone()) && !(server.isOutboundDone() && server.isInboundDone())) { - cResult = client.wrap(cOut, clientToServer); - sResult = server.wrap(sOut, serverToClient); + client.wrap(cOut, clientToServer); + server.wrap(sOut, serverToClient); clientToServer.flip(); serverToClient.flip(); - cResult = client.unwrap(serverToClient, cIn); - sResult = server.unwrap(clientToServer, sIn); + client.unwrap(serverToClient, cIn); + server.unwrap(clientToServer, sIn); clientToServer.compact(); serverToClient.compact(); @@ -1649,12 +1799,6 @@ public void testSSLEngineSplitInput() throws Exception { ByteBuffer clientToServer; ByteBuffer serverToClient; - byte[] input1Buf = "Hello client, ".getBytes(); - byte[] input2Buf = "from server".getBytes(); - - SSLEngineResult cResult; - SSLEngineResult sResult; - System.out.print("\tTesting split input data"); try { @@ -1694,14 +1838,14 @@ public void testSSLEngineSplitInput() throws Exception { while (!(client.isOutboundDone() && client.isInboundDone()) && !(server.isOutboundDone() && server.isInboundDone())) { - cResult = client.wrap(cOutBuffs, clientToServer); - sResult = server.wrap(sOutBuffs, serverToClient); + client.wrap(cOutBuffs, clientToServer); + server.wrap(sOutBuffs, serverToClient); clientToServer.flip(); serverToClient.flip(); - cResult = client.unwrap(serverToClient, cIn); - sResult = server.unwrap(clientToServer, sIn); + client.unwrap(serverToClient, cIn); + server.unwrap(clientToServer, sIn); clientToServer.compact(); serverToClient.compact(); @@ -1742,5 +1886,140 @@ public void testSSLEngineSplitInput() throws Exception { } pass("\t... passed"); } + + @Test + public void testDTLSv13Engine() + throws NoSuchProviderException, NoSuchAlgorithmException, + KeyManagementException, KeyStoreException, IOException, + CertificateException, UnrecoverableKeyException { + + System.out.print("\tDTLSv1.3 basic connection"); + + /* Skip if DTLSv1.3 not enabled */ + if (!enabledProtocols.contains("DTLSv1.3")) { + System.out.println("\t... skipped"); + return; + } + + SSLEngine client = null; + SSLEngine server = null; + + try { + /* Create SSLContext */ + this.ctx = tf.createSSLContext("DTLSv1.3", engineProvider); + + /* Create client engine */ + client = this.ctx.createSSLEngine("wolfSSL client", 11111); + client.setUseClientMode(true); + + /* Create server engine */ + server = this.ctx.createSSLEngine(); + server.setUseClientMode(false); + server.setNeedClientAuth(true); + + /* Test buffer sizes */ + SSLSession session = client.getSession(); + assertTrue(session.getApplicationBufferSize() > 0); + assertTrue(session.getPacketBufferSize() > 0); + + /* Test handshake with small app data */ + tf.testConnection(client, server, null, null, "Test Message"); + + /* Test handshake with large app data */ + byte[] largeData = new byte[16384]; + new Random().nextBytes(largeData); + tf.testConnection(client, server, null, null, new String(largeData)); + + pass("\t... passed"); + + } catch (Exception e) { + error("\t... failed"); + e.printStackTrace(); + fail("Failed DTLSv1.3 test with exception: " + e); + } + } + + /** + * Internal helper method for testDTLSv13EngineResumeSession. + */ + private void dtls13ResumeTest(String con1Host, String con2Host, + boolean expectResume) throws Exception { + + SSLEngine client1 = null; + SSLEngine client2 = null; + SSLEngine server1 = null; + SSLEngine server2 = null; + boolean resumed = false; + + /* Create SSLContext */ + SSLContext dtlsCtx = tf.createSSLContext("DTLSv1.3", engineProvider); + + /* First connection */ + client1 = dtlsCtx.createSSLEngine(con1Host, 11111); + client1.setUseClientMode(true); + server1 = dtlsCtx.createSSLEngine(); + server1.setUseClientMode(false); + server1.setNeedClientAuth(true); + + /* First handshake */ + tf.testConnection(client1, server1, null, null, "First Connection"); + + /* Second connection */ + client2 = dtlsCtx.createSSLEngine(con2Host, 11111); + client2.setUseClientMode(true); + server2 = dtlsCtx.createSSLEngine(); + server2.setUseClientMode(false); + server2.setNeedClientAuth(true); + + /* Second handshake */ + tf.testConnection(client2, server2, null, null, "Second Connection"); + + /* Verify session was resumed */ + WolfSSLEngine we = (WolfSSLEngine)client2; + resumed = we.sessionResumed(); + + if (expectResume && !resumed) { + throw new Exception( + "Session was not resumed, but should have been"); + } + else if (!expectResume && resumed) { + throw new Exception( + "Session was resumed, but should not have been"); + } + } + + /** + * Test that SSLEngine with DTLSv1.3 resumes (and not) as expected. + */ + @Test + public void testDTLSv13EngineResumeSession() + throws NoSuchProviderException, NoSuchAlgorithmException, + KeyManagementException, KeyStoreException, IOException, + CertificateException, UnrecoverableKeyException { + + System.out.print("\tDTLSv1.3 session resumption"); + + /* Skip if DTLSv1.3 not enabled */ + if (!enabledProtocols.contains("DTLSv1.3")) { + System.out.println("\t... skipped"); + return; + } + + try { + + /* Test expected resumption case */ + dtls13ResumeTest("wolfSSL client", "wolfSSL client", true); + /* Test expected not resumption case */ + dtls13ResumeTest("wolfSSL client", "wolfSSL client 2", false); + + pass("\t... passed"); + + } catch (Exception e) { + error("\t... failed"); + e.printStackTrace(); + fail("Failed DTLSv1.3 session resumption test " + + "with exception: " + e); + } + } } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java index 313c52fc..a0f318e8 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java @@ -414,47 +414,54 @@ public void testGetSetEnabledProtocols() /* test that removing protocols with jdk.tls.disabledAlgorithms * behaves as expected */ - String originalProperty = - Security.getProperty("jdk.tls.disabledAlgorithms"); + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + String originalProperty = + Security.getProperty("jdk.tls.disabledAlgorithms"); - try { - Security.setProperty("jdk.tls.disabledAlgorithms", "TLSv1"); - s.setEnabledProtocols(new String[] {"TLSv1"}); - System.out.println("\t\t... failed"); - fail("SSLSocket.setEnabledProtocols() failed"); - } catch (IllegalArgumentException e) { - /* expected */ - } + try { + Security.setProperty( + "jdk.tls.disabledAlgorithms", "TLSv1"); + s.setEnabledProtocols(new String[] {"TLSv1"}); + System.out.println("\t\t... failed"); + fail("SSLSocket.setEnabledProtocols() failed"); + } catch (IllegalArgumentException e) { + /* expected */ + } - try { - Security.setProperty("jdk.tls.disabledAlgorithms", "TLSv1.1"); - s.setEnabledProtocols(new String[] {"TLSv1.1"}); - System.out.println("\t\t... failed"); - fail("SSLSocket.setEnabledProtocols() failed"); - } catch (IllegalArgumentException e) { - /* expected */ - } + try { + Security.setProperty( + "jdk.tls.disabledAlgorithms", "TLSv1.1"); + s.setEnabledProtocols(new String[] {"TLSv1.1"}); + System.out.println("\t\t... failed"); + fail("SSLSocket.setEnabledProtocols() failed"); + } catch (IllegalArgumentException e) { + /* expected */ + } - try { - Security.setProperty("jdk.tls.disabledAlgorithms", "TLSv1.2"); - s.setEnabledProtocols(new String[] {"TLSv1.2"}); - System.out.println("\t\t... failed"); - fail("SSLSocket.setEnabledProtocols() failed"); - } catch (IllegalArgumentException e) { - /* expected */ - } + try { + Security.setProperty( + "jdk.tls.disabledAlgorithms", "TLSv1.2"); + s.setEnabledProtocols(new String[] {"TLSv1.2"}); + System.out.println("\t\t... failed"); + fail("SSLSocket.setEnabledProtocols() failed"); + } catch (IllegalArgumentException e) { + /* expected */ + } - try { - Security.setProperty("jdk.tls.disabledAlgorithms", "TLSv1.3"); - s.setEnabledProtocols(new String[] {"TLSv1.3"}); - System.out.println("\t\t... failed"); - fail("SSLSocket.setEnabledProtocols() failed"); - } catch (IllegalArgumentException e) { - /* expected */ - } + try { + Security.setProperty( + "jdk.tls.disabledAlgorithms", "TLSv1.3"); + s.setEnabledProtocols(new String[] {"TLSv1.3"}); + System.out.println("\t\t... failed"); + fail("SSLSocket.setEnabledProtocols() failed"); + } catch (IllegalArgumentException e) { + /* expected */ + } - /* restore original property value */ - System.setProperty("jdk.tls.disabledAlgorithms", originalProperty); + /* restore original property value */ + Security.setProperty("jdk.tls.disabledAlgorithms", + originalProperty); + } } System.out.println("\t... passed"); @@ -1857,17 +1864,20 @@ public void testProtocolTLSv10() throws Exception { /* reset disabledAlgorithms property to test TLS 1.0 which is * disabled by default */ - String originalProperty = - Security.getProperty("jdk.tls.disabledAlgorithms"); - Security.setProperty("jdk.tls.disabledAlgorithms", ""); + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + String originalProperty = + Security.getProperty("jdk.tls.disabledAlgorithms"); + Security.setProperty("jdk.tls.disabledAlgorithms", ""); - protocolConnectionTest("TLSv1"); + protocolConnectionTest("TLSv1"); - System.out.print("\tTLS 1.0 extended Socket test"); - protocolConnectionTestExtendedSocket("TLSv1"); + System.out.print("\tTLS 1.0 extended Socket test"); + protocolConnectionTestExtendedSocket("TLSv1"); - /* restore system property */ - Security.setProperty("jdk.tls.disabledAlgorithms", originalProperty); + /* restore system property */ + Security.setProperty( + "jdk.tls.disabledAlgorithms", originalProperty); + } } @Test @@ -1883,17 +1893,20 @@ public void testProtocolTLSv11() throws Exception { /* reset disabledAlgorithms property to test TLS 1.1 which is * disabled by default */ - String originalProperty = - Security.getProperty("jdk.tls.disabledAlgorithms"); - Security.setProperty("jdk.tls.disabledAlgorithms", ""); + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + String originalProperty = + Security.getProperty("jdk.tls.disabledAlgorithms"); + Security.setProperty("jdk.tls.disabledAlgorithms", ""); - protocolConnectionTest("TLSv1.1"); + protocolConnectionTest("TLSv1.1"); - System.out.print("\tTLS 1.1 extended Socket test"); - protocolConnectionTestExtendedSocket("TLSv1.1"); + System.out.print("\tTLS 1.1 extended Socket test"); + protocolConnectionTestExtendedSocket("TLSv1.1"); - /* restore system property */ - Security.setProperty("jdk.tls.disabledAlgorithms", originalProperty); + /* restore system property */ + Security.setProperty( + "jdk.tls.disabledAlgorithms", originalProperty); + } } @Test @@ -1902,17 +1915,19 @@ public void testProtocolTLSv12() throws Exception { System.out.print("\tTLS 1.2 connection test"); /* skip if TLS 1.2 is not compiled in at native level */ - if (WolfSSL.TLSv12Enabled() == false || - WolfSSLTestFactory.securityPropContains( - "jdk.tls.disabledAlgorithms", "TLSv1.2")) { - System.out.println("\t\t... skipped"); - return; - } + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + if (WolfSSL.TLSv12Enabled() == false || + WolfSSLTestFactory.securityPropContains( + "jdk.tls.disabledAlgorithms", "TLSv1.2")) { + System.out.println("\t\t... skipped"); + return; + } - protocolConnectionTest("TLSv1.2"); + protocolConnectionTest("TLSv1.2"); - System.out.print("\tTLS 1.2 extended Socket test"); - protocolConnectionTestExtendedSocket("TLSv1.2"); + System.out.print("\tTLS 1.2 extended Socket test"); + protocolConnectionTestExtendedSocket("TLSv1.2"); + } } @Test @@ -1921,17 +1936,19 @@ public void testProtocolTLSv13() throws Exception { System.out.print("\tTLS 1.3 connection test"); /* skip if TLS 1.3 is not compiled in at native level */ - if (WolfSSL.TLSv13Enabled() == false || - WolfSSLTestFactory.securityPropContains( - "jdk.tls.disabledAlgorithms", "TLSv1.3")) { - System.out.println("\t\t... skipped"); - return; - } + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + if (WolfSSL.TLSv13Enabled() == false || + WolfSSLTestFactory.securityPropContains( + "jdk.tls.disabledAlgorithms", "TLSv1.3")) { + System.out.println("\t\t... skipped"); + return; + } - protocolConnectionTest("TLSv1.3"); + protocolConnectionTest("TLSv1.3"); - System.out.print("\tTLS 1.3 extended Socket test"); - protocolConnectionTestExtendedSocket("TLSv1.3"); + System.out.print("\tTLS 1.3 extended Socket test"); + protocolConnectionTestExtendedSocket("TLSv1.3"); + } } private void protocolConnectionTest(String protocol) throws Exception { @@ -2119,48 +2136,52 @@ public void testConnectionWithDisabledAlgorithms() throws Exception { this.ctx = tf.createSSLContext("TLS", ctxProvider); /* save current system property value */ - String originalProperty = - Security.getProperty("jdk.tls.disabledAlgorithms"); + synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) { + String originalProperty = + Security.getProperty("jdk.tls.disabledAlgorithms"); - for (int i = 0; i < enabledProtocols.size(); i++) { + for (int i = 0; i < enabledProtocols.size(); i++) { - /* skip generic "TLS" */ - if (enabledProtocols.get(i).equals("TLS")) { - continue; - } + /* skip generic "TLS" */ + if (enabledProtocols.get(i).equals("TLS")) { + continue; + } - /* create SSLServerSocket first to get ephemeral port */ - SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory() - .createServerSocket(0); + /* create SSLServerSocket first to get ephemeral port */ + SSLServerSocket ss = + (SSLServerSocket)ctx.getServerSocketFactory() + .createServerSocket(0); - SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket(); - /* restrict to single protocol that is being disabled */ - cs.setEnabledProtocols(new String[] {enabledProtocols.get(i)}); + SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket(); + /* restrict to single protocol that is being disabled */ + cs.setEnabledProtocols(new String[] {enabledProtocols.get(i)}); - /* disable protocol after socket setup, should fail connection */ - Security.setProperty("jdk.tls.disabledAlgorithms", - enabledProtocols.get(i)); + /* disable protocol after socket setup, should fail conn */ + Security.setProperty("jdk.tls.disabledAlgorithms", + enabledProtocols.get(i)); - /* don't need server since should throw exception before */ - cs.connect(new InetSocketAddress(ss.getLocalPort())); + /* don't need server since should throw exception before */ + cs.connect(new InetSocketAddress(ss.getLocalPort())); - try { - cs.startHandshake(); - System.out.println("\t... failed"); - fail(); + try { + cs.startHandshake(); + System.out.println("\t... failed"); + fail(); + + } catch (SSLException e) { + /* expected, should fail with + * "No protocols enabled or available" */ + } - } catch (SSLException e) { - /* expected, should fail with - * "No protocols enabled or available" */ + cs.close(); + ss.close(); } - cs.close(); - ss.close(); + /* restore system property */ + Security.setProperty( + "jdk.tls.disabledAlgorithms", originalProperty); } - /* restore system property */ - Security.setProperty("jdk.tls.disabledAlgorithms", originalProperty); - System.out.println("\t... passed"); } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java index 2b66c431..dab334c8 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java @@ -27,7 +27,6 @@ import java.math.BigInteger; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; @@ -44,13 +43,10 @@ import java.security.NoSuchProviderException; import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; -import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.util.List; import java.util.Arrays; import java.util.Enumeration; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -98,6 +94,12 @@ class WolfSSLTestFactory { protected String keyStoreType = "JKS"; private boolean extraDebug = false; + /** + * Shared lock for synchronization around tests that modify or use the Java + * Security property: jdk.tls.disabledAlgorithms + */ + public static final Object jdkTlsDisabledAlgorithmsLock = new Object(); + protected WolfSSLTestFactory() throws WolfSSLException { /* wolfJSSE example Java KeyStore files, containing: * all.jks All certs @@ -585,166 +587,200 @@ static void pass(String msg) { */ } + /** + * Run SSLEngine delegated tasks. + * + * wolfJSSE doesn't use delegated tasks, but we use this for + * compatibility so tests can be switched and run against different + * providers. + */ + private void runDelegatedTasks(SSLEngine engine) { + Runnable run; + + while ((run = engine.getDelegatedTask()) != null) { + run.run(); + } + } + + /** + * Helper method to verify received data, called by testConnection(). + */ + private boolean verifyReceivedData(ByteBuffer buf, String expected) { + buf.flip(); + byte[] b = new byte[buf.remaining()]; + buf.get(b); + String received = new String(b, StandardCharsets.UTF_8).trim(); + return expected.equals(received); + } /** - * Engine connection. Makes server/client handshake in memory + * Does SSL/TLS handshake between two SSLEngine objects, then + * sends application data from client to server and vice versa. + * Application data received by client/server is compared to + * original sent as a sanity check. + * * @param server SSLEngine for server side of connection * @param client SSLEngine for client side of connection - * @param cipherSuites cipher suites to use can be null + * @param cipherSuites cipher suites to use, can be null * @param protocols TLS protocols to use i.e. TLSv1.2, can be null - * @param appData message to send after handshake, can be null - * @return + * @param appData message to send after handshake, can be null to not + * send any data. + * + * @return 0 on success, -1 on error. Any exceptions thrown + * inside the body of this method are caught and converted into + * a -1 return. */ protected int testConnection(SSLEngine server, SSLEngine client, - String[] cipherSuites, String[] protocols, String appData) { - ByteBuffer serToCli = ByteBuffer.allocateDirect(server.getSession().getPacketBufferSize()); - ByteBuffer cliToSer = ByteBuffer.allocateDirect(client.getSession().getPacketBufferSize()); + String[] cipherSuites, String[] protocols, String appData) { + + /* Max loop protection against infinite loops */ + int loops = 0; + int maxLoops = 50; + boolean handshakeComplete = false; + + /* Allocate buffers large enough for protocol packets */ + ByteBuffer serToCli = ByteBuffer.allocateDirect( + server.getSession().getPacketBufferSize()); + ByteBuffer cliToSer = ByteBuffer.allocateDirect( + client.getSession().getPacketBufferSize()); + + /* Application data buffers */ ByteBuffer toSendCli = ByteBuffer.wrap(appData.getBytes()); ByteBuffer toSendSer = ByteBuffer.wrap(appData.getBytes()); - ByteBuffer serPlain = ByteBuffer.allocate(server.getSession().getApplicationBufferSize()); - ByteBuffer cliPlain = ByteBuffer.allocate(client.getSession().getApplicationBufferSize()); - boolean done = false; + ByteBuffer serPlain = ByteBuffer.allocate( + server.getSession().getApplicationBufferSize()); + ByteBuffer cliPlain = ByteBuffer.allocate( + client.getSession().getApplicationBufferSize()); + /* Configure protocols and cipher suites if specified */ if (cipherSuites != null) { server.setEnabledCipherSuites(cipherSuites); client.setEnabledCipherSuites(cipherSuites); } - if (protocols != null) { server.setEnabledProtocols(protocols); client.setEnabledProtocols(protocols); } - while (!done) { + while (!handshakeComplete && loops++ < maxLoops) { try { - Runnable run; - SSLEngineResult result; - HandshakeStatus s; - - result = client.wrap(toSendCli, cliToSer); - if (extraDebug) { - System.out.println("[client wrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); -// + " sequence # = " + result.sequenceNumber()); - } - while ((run = client.getDelegatedTask()) != null) { - run.run(); - } - - result = server.wrap(toSendSer, serToCli); - if (extraDebug) { - System.out.println("[server wrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); - } - while ((run = server.getDelegatedTask()) != null) { - run.run(); + HandshakeStatus clientStatus = client.getHandshakeStatus(); + HandshakeStatus serverStatus = server.getHandshakeStatus(); + + /* client wrap() */ + if (clientStatus == HandshakeStatus.NEED_WRAP || + (clientStatus == HandshakeStatus.NOT_HANDSHAKING && + toSendCli.hasRemaining())) { + SSLEngineResult result = client.wrap(toSendCli, cliToSer); + runDelegatedTasks(client); + if (result.getStatus() == + SSLEngineResult.Status.BUFFER_OVERFLOW) { + return -1; + } + if (extraDebug) { + System.out.println("[client wrap] " + result); + } } - if (extraDebug) { - s = client.getHandshakeStatus(); - System.out.println("client status = " + s.toString()); - s = server.getHandshakeStatus(); - System.out.println("server status = " + s.toString()); + /* server wrap() */ + if (serverStatus == HandshakeStatus.NEED_WRAP || + (serverStatus == HandshakeStatus.NOT_HANDSHAKING && + toSendSer.hasRemaining())) { + SSLEngineResult result = server.wrap(toSendSer, serToCli); + runDelegatedTasks(server); + if (result.getStatus() == + SSLEngineResult.Status.BUFFER_OVERFLOW) { + return -1; + } + if (extraDebug) { + System.out.println("[server wrap] " + result); + } } + /* flip buffers for unwrap() */ cliToSer.flip(); serToCli.flip(); - if (extraDebug) { - if (cliToSer.remaining() > 0) { - System.out.println("Client -> Server"); - printHex(cliToSer); + /* client unwrap() */ + if ((clientStatus == HandshakeStatus.NEED_UNWRAP || + clientStatus == HandshakeStatus.NOT_HANDSHAKING) && + serToCli.hasRemaining()) { + SSLEngineResult result = client.unwrap(serToCli, cliPlain); + runDelegatedTasks(client); + if (result.getStatus() == + SSLEngineResult.Status.BUFFER_OVERFLOW) { + return -1; } - - if (serToCli.remaining() > 0) { - System.out.println("Server -> Client"); - printHex(serToCli); + if (extraDebug) { + System.out.println("[client unwrap] " + result); } - - System.out.println("cliToSer remaining = " + cliToSer.remaining()); - System.out.println("serToCli remaining = " + serToCli.remaining()); - } - result = client.unwrap(serToCli, cliPlain); - if (extraDebug) { - System.out.println("[client unwrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); } - while ((run = client.getDelegatedTask()) != null) { - run.run(); - } - - result = server.unwrap(cliToSer, serPlain); - if (extraDebug) { - System.out.println("[server unwrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); - } - while ((run = server.getDelegatedTask()) != null) { - run.run(); + /* server unwrap() */ + if ((serverStatus == HandshakeStatus.NEED_UNWRAP || + serverStatus == HandshakeStatus.NOT_HANDSHAKING) && + cliToSer.hasRemaining()) { + SSLEngineResult result = server.unwrap(cliToSer, serPlain); + runDelegatedTasks(server); + if (result.getStatus() == + SSLEngineResult.Status.BUFFER_OVERFLOW) { + return -1; + } + if (extraDebug) { + System.out.println("[server unwrap] " + result); + } } + /* compact network buffers */ cliToSer.compact(); serToCli.compact(); - if (extraDebug) { - s = client.getHandshakeStatus(); - System.out.println("client status = " + s.toString()); - s = server.getHandshakeStatus(); - System.out.println("server status = " + s.toString()); - } - - if (toSendCli.remaining() == 0 && toSendSer.remaining() == 0) { - byte[] b; - String st; - - /* check what the client received */ - cliPlain.rewind(); - b = new byte[cliPlain.remaining()]; - cliPlain.get(b); - st = new String(b, StandardCharsets.UTF_8).trim(); - if (!appData.equals(st)) { - return -1; - } + /* Check if handshake is complete and data exchanged */ + if (!toSendCli.hasRemaining() && !toSendSer.hasRemaining() && + client.getHandshakeStatus() == + HandshakeStatus.NOT_HANDSHAKING && + server.getHandshakeStatus() == + HandshakeStatus.NOT_HANDSHAKING) { - /* check what the server received */ - serPlain.rewind(); - b = new byte[serPlain.remaining()]; - serPlain.get(b); - st = new String(b, StandardCharsets.UTF_8).trim(); - if (!appData.equals(st)) { + /* Verify received data matches expected */ + if (!verifyReceivedData(cliPlain, appData) || + !verifyReceivedData(serPlain, appData)) { return -1; } - - done = true; + handshakeComplete = true; } } catch (SSLException ex) { return -1; } } - return 0; + + return handshakeComplete ? 0 : -1; } /** - * Close down an engine connection + * Close down an SSLEngine connection. + * * @param server * @param client * @param earlyClose - * @return + * + * @return 0 on success, negative on error + * * @throws SSLException */ - public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClose) throws SSLException { - ByteBuffer serToCli = ByteBuffer.allocateDirect(server.getSession().getPacketBufferSize()); - ByteBuffer cliToSer = ByteBuffer.allocateDirect(client.getSession().getPacketBufferSize()); - ByteBuffer empty = ByteBuffer.allocate(server.getSession().getPacketBufferSize()); + public int CloseConnection(SSLEngine server, SSLEngine client, + boolean earlyClose) throws SSLException { + + ByteBuffer serToCli = ByteBuffer.allocateDirect( + server.getSession().getPacketBufferSize()); + ByteBuffer cliToSer = ByteBuffer.allocateDirect( + client.getSession().getPacketBufferSize()); + ByteBuffer empty = ByteBuffer.allocate( + server.getSession().getPacketBufferSize()); SSLEngineResult result; HandshakeStatus s; boolean passed; - Runnable run; /* Close outBound to begin process of sending close_notify alert */ client.closeOutbound(); @@ -755,13 +791,13 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos /* Generate close_notify alert */ result = client.wrap(empty, cliToSer); if (extraDebug) { - System.out.println("[client wrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); - } - while ((run = client.getDelegatedTask()) != null) { - run.run(); + System.out.println( + "[client wrap] consumed = " + result.bytesConsumed() + + " produced = " + result.bytesProduced() + + " status = " + result.getStatus().name()); } + runDelegatedTasks(client); + s = client.getHandshakeStatus(); if (extraDebug) { System.out.println("client status = " + s.toString()); @@ -812,17 +848,17 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos result = server.unwrap(cliToSer, empty); cliToSer.compact(); if (extraDebug) { - System.out.println("[server unwrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); + System.out.println( + "[server unwrap] consumed = " + result.bytesConsumed() + + " produced = " + result.bytesProduced() + + " status = " + result.getStatus().name()); } if (result.getStatus().name().equals("CLOSED")) { /* odd case where server tries to send "empty" if not set close */ server.closeOutbound(); } - while ((run = server.getDelegatedTask()) != null) { - run.run(); - } + runDelegatedTasks(server); + s = server.getHandshakeStatus(); if (result.bytesProduced() != 0 || result.bytesConsumed() <= 0) { throw new SSLException("Server unwrap consumed/produced error"); @@ -836,13 +872,13 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos result = server.wrap(empty, serToCli); serToCli.flip(); if (extraDebug) { - System.out.println("[server wrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); - } - while ((run = server.getDelegatedTask()) != null) { - run.run(); + System.out.println( + "[server wrap] consumed = " + result.bytesConsumed() + + " produced = " + result.bytesProduced() + + " status = " + result.getStatus().name()); } + runDelegatedTasks(server); + s = server.getHandshakeStatus(); if (result.bytesProduced() <= 0 || result.bytesConsumed() != 0) { throw new SSLException("Server wrap consumed/produced error"); @@ -859,13 +895,13 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos result = client.unwrap(serToCli, empty); serToCli.compact(); if (extraDebug) { - System.out.println("[client unwrap] consumed = " + result.bytesConsumed() + - " produced = " + result.bytesProduced() + - " status = " + result.getStatus().name()); - } - while ((run = client.getDelegatedTask()) != null) { - run.run(); + System.out.println( + "[client unwrap] consumed = " + result.bytesConsumed() + + " produced = " + result.bytesProduced() + + " status = " + result.getStatus().name()); } + runDelegatedTasks(client); + s = client.getHandshakeStatus(); if (result.bytesProduced() != 0 || result.bytesConsumed() <= 0) { throw new SSLException("Client unwrap consumed/produced error"); diff --git a/src/test/com/wolfssl/test/WolfSSLTest.java b/src/test/com/wolfssl/test/WolfSSLTest.java index 83424c3d..4c88f362 100644 --- a/src/test/com/wolfssl/test/WolfSSLTest.java +++ b/src/test/com/wolfssl/test/WolfSSLTest.java @@ -27,6 +27,9 @@ import org.junit.runners.JUnit4; import static org.junit.Assert.*; +import java.util.Arrays; +import java.util.List; + import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLException; @@ -85,20 +88,45 @@ public void test_WolfSSL_protocol() { } public void test_WolfSSL_Method_Allocators(WolfSSL lib) { - tstMethod(WolfSSL.SSLv3_ServerMethod(), "SSLv3_ServerMethod()"); - tstMethod(WolfSSL.SSLv3_ClientMethod(), "SSLv3_ClientMethod()"); - tstMethod(WolfSSL.TLSv1_ServerMethod(), "TLSv1_ServerMethod()"); - tstMethod(WolfSSL.TLSv1_ClientMethod(), "TLSv1_ClientMethod()"); - tstMethod(WolfSSL.TLSv1_1_ServerMethod(), "TLSv1_1_ServerMethod()"); - tstMethod(WolfSSL.TLSv1_1_ClientMethod(), "TLSv1_1_ClientMethod()"); - tstMethod(WolfSSL.TLSv1_2_ServerMethod(), "TLSv1_2_ServerMethod()"); - tstMethod(WolfSSL.TLSv1_2_ClientMethod(), "TLSv1_2_ClientMethod()"); - tstMethod(WolfSSL.TLSv1_3_ServerMethod(), "TLSv1_3_ServerMethod()"); - tstMethod(WolfSSL.TLSv1_3_ClientMethod(), "TLSv1_3_ClientMethod()"); - tstMethod(WolfSSL.DTLSv1_ServerMethod(), "DTLSv1_ServerMethod()"); - tstMethod(WolfSSL.DTLSv1_ClientMethod(), "DTLSv1_ClientMethod()"); - tstMethod(WolfSSL.DTLSv1_2_ServerMethod(), "DTLSv1_2_ServerMethod()"); - tstMethod(WolfSSL.DTLSv1_2_ClientMethod(), "DTLSv1_2_ClientMethod()"); + /* Get protocols compiled into native wolfSSL */ + List enabledProtocols = Arrays.asList(WolfSSL.getProtocols()); + + if (enabledProtocols.contains("SSLv3")) { + tstMethod(WolfSSL.SSLv3_ServerMethod(), "SSLv3_ServerMethod()"); + tstMethod(WolfSSL.SSLv3_ClientMethod(), "SSLv3_ClientMethod()"); + } + if (enabledProtocols.contains("TLSv1")) { + tstMethod(WolfSSL.TLSv1_ServerMethod(), "TLSv1_ServerMethod()"); + tstMethod(WolfSSL.TLSv1_ClientMethod(), "TLSv1_ClientMethod()"); + } + if (enabledProtocols.contains("TLSv1.1")) { + tstMethod(WolfSSL.TLSv1_1_ServerMethod(), "TLSv1_1_ServerMethod()"); + tstMethod(WolfSSL.TLSv1_1_ClientMethod(), "TLSv1_1_ClientMethod()"); + } + if (enabledProtocols.contains("TLSv1.2")) { + tstMethod(WolfSSL.TLSv1_2_ServerMethod(), "TLSv1_2_ServerMethod()"); + tstMethod(WolfSSL.TLSv1_2_ClientMethod(), "TLSv1_2_ClientMethod()"); + } + if (enabledProtocols.contains("TLSv1.3")) { + tstMethod(WolfSSL.TLSv1_3_ServerMethod(), "TLSv1_3_ServerMethod()"); + tstMethod(WolfSSL.TLSv1_3_ClientMethod(), "TLSv1_3_ClientMethod()"); + } + if (enabledProtocols.contains("DTLSv1")) { + tstMethod(WolfSSL.DTLSv1_ServerMethod(), "DTLSv1_ServerMethod()"); + tstMethod(WolfSSL.DTLSv1_ClientMethod(), "DTLSv1_ClientMethod()"); + } + if (enabledProtocols.contains("DTLSv1.2")) { + tstMethod(WolfSSL.DTLSv1_2_ServerMethod(), + "DTLSv1_2_ServerMethod()"); + tstMethod(WolfSSL.DTLSv1_2_ClientMethod(), + "DTLSv1_2_ClientMethod()"); + } + if (enabledProtocols.contains("DTLSv1.3")) { + tstMethod(WolfSSL.DTLSv1_3_ServerMethod(), + "DTLSv1_3_ServerMethod()"); + tstMethod(WolfSSL.DTLSv1_3_ClientMethod(), + "DTLSv1_3_ClientMethod()"); + } tstMethod(WolfSSL.SSLv23_ServerMethod(), "SSLv23_ServerMethod()"); tstMethod(WolfSSL.SSLv23_ClientMethod(), "SSLv23_ClientMethod()"); }