Skip to content

Commit

Permalink
JSSE: only resume sessions from Java client cache if same cipher suit…
Browse files Browse the repository at this point in the history
…e and protocol are enabled
  • Loading branch information
cconlon committed Jul 17, 2024
1 parent bef6379 commit 7746787
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 47 deletions.
21 changes: 21 additions & 0 deletions native/com_wolfssl_WolfSSLSession.c
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,27 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_wolfsslSessionIsResumable
#endif
}

JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLSession_wolfsslSessionCipherGetName
(JNIEnv* jenv, jclass jcl, jlong sessionPtr)
{
WOLFSSL_SESSION* session = (WOLFSSL_SESSION*)(uintptr_t)sessionPtr;
const char* cipherName;
jstring cipherStr = NULL;
(void)jcl;

if (jenv == NULL || session == NULL) {
return NULL;
}

cipherName = wolfSSL_SESSION_CIPHER_get_name(session);

if (cipherName != NULL) {
cipherStr = (*jenv)->NewStringUTF(jenv, cipherName);
}

return cipherStr;
}

JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeNativeSession
(JNIEnv* jenv, jclass jcl, jlong sessionPtr)
{
Expand Down
8 changes: 8 additions & 0 deletions native/com_wolfssl_WolfSSLSession.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions src/java/com/wolfssl/WolfSSLSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ private native int read(long ssl, byte[] data, int offset, int sz,
private native long get1Session(long ssl);
private static native int wolfsslSessionIsSetup(long ssl);
private static native int wolfsslSessionIsResumable(long ssl);
private static native String wolfsslSessionCipherGetName(long ssl);
private static native void freeNativeSession(long session);
private native byte[] getSessionID(long session);
private native int setServerID(long ssl, byte[] id, int len, int newSess);
Expand Down Expand Up @@ -1364,6 +1365,29 @@ public static int sessionIsResumable(long session) {
return wolfsslSessionIsResumable(session);
}

/**
* Get cipher suite name from WOLFSSL_SESSION, calling native
* wolfSSL_SESSION_CIPHER_get_name().
*
* This method is static and does not check active state since this
* takes a native pointer and has no interaction with the rest of this
* object.
*
* @param session pointer to native WOLFSSL_SESSION structure. May have
* been obtained from getSession().
* @return String representation of the cipher suite used in native
* WOLFSSL_SESSION structure, or NULL if not able to find the
* session.
*/
public static String sessionGetCipherName(long session) {

if (session == 0) {
return null;
}

return wolfsslSessionCipherGetName(session);
}

/**
* Free the native WOLFSSL_SESSION structure pointed to be session.
*
Expand Down
173 changes: 139 additions & 34 deletions src/java/com/wolfssl/provider/jsse/WolfSSLAuthStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,24 @@ protected void resizeCache(int sz, int side) {
* @param port port number of peer being connected to
* @param host host of the peer being connected to
* @param clientMode if is client side then true, otherwise false
* @param enabledCipherSuites String array containing enabled cipher
* suites for the SSLSocket/SSLEngine requesting this session.
* Used to compare cipher suite of cached session against enabled
* cipher suites.
* @param enabledProtocols String array containing enabled protocols
* for the SSLSocket/SSLEngine requesting this session.
* Used to compare protocol of cached session against enabled
* protocols.
*
* @return an existing SSLSession from Java session cache, or a new
* object if not in cache, called on server side, or host
* is null
*/
protected synchronized WolfSSLImplementSSLSession getSession(
WolfSSLSession ssl, int port, String host, boolean clientMode) {
WolfSSLSession ssl, int port, String host, boolean clientMode,
String[] enabledCipherSuites, String[] enabledProtocols) {

boolean needNewSession = false;
WolfSSLImplementSSLSession ses = null;
String toHash = null;

Expand All @@ -327,16 +338,51 @@ protected synchronized WolfSSLImplementSSLSession getSession(
* is shared between all threads */
synchronized (storeLock) {

/* generate cache key hash (host:port) */
/* Generate cache key hash (host:port) */
toHash = host.concat(Integer.toString(port));

/* try getting session out of Java store */
/* Try getting session out of Java store */
ses = store.get(toHash.hashCode());

if (ses == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"session not found in cache table, creating new");
/* not found in stored sessions create a new one */
/* Remove old entry from table. TLS 1.3 binder changes between
* resumptions and stored session should only be used to
* resume once. New session structure/object will be cached
* after the resumed session completes the handshake, for
* subsequent resumption attempts to use. */
store.remove(toHash.hashCode());

/* Check conditions where we need to create a new new session:
* 1. Session not found in cache
* 2. Session marked as not resumable
* 3. Original session cipher suite not available
* 4. Original session protocol version not available
*/
if (ses == null ||
!ses.isResumable() ||
!sessionCipherSuiteAvailable(ses, enabledCipherSuites) ||
!sessionProtocolAvailable(ses, enabledProtocols)) {
needNewSession = true;
}

if (needNewSession) {
if (ses == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"session not found in cache table, " +
"creating new session");
}
else if (!ses.isResumable()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"native WOLFSSL_SESSION not resumable, " +
"creating new session");
}
else if (!sessionCipherSuiteAvailable(
ses, enabledCipherSuites)) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"cipher suite used in original WOLFSSL_SESSION not " +
"available, creating new session");
}

/* Not found in stored sessions create a new one */
ses = new WolfSSLImplementSSLSession(ssl, port, host, this);
ses.setValid(true); /* new sessions marked as valid */

Expand All @@ -345,35 +391,11 @@ protected synchronized WolfSSLImplementSSLSession getSession(
Integer.toString(ssl.hashCode()).getBytes());
}
else {
/* Remove old entry from table. TLS 1.3 binder changes between
* resumptions and stored session should only be used to
* resume once. New session structure/object will be cached
* after the resumed session completes the handshake, for
* subsequent resumption attempts to use. */
store.remove(toHash.hashCode());

/* Check if native WOLFSSL_SESSION is resumable before
* returning it for resumption. If not, create a new
* session instead. */
if (!ses.isResumable()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"native WOLFSSL_SESSION not resumable, " +
"creating new session");
ses = new WolfSSLImplementSSLSession(ssl, port, host, this);
ses.setValid(true); /* new sessions marked as valid */

ses.isFromTable = false;
ses.setPseudoSessionId(
Integer.toString(ssl.hashCode()).getBytes());

return ses;
}

ses.isFromTable = true;

WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"session found in cache, trying to resume");

ses.isFromTable = true;

if (ses.resume(ssl) != WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"native wolfSSL_set_session() failed, " +
Expand All @@ -384,13 +406,96 @@ protected synchronized WolfSSLImplementSSLSession getSession(
ses.isFromTable = false;
ses.setPseudoSessionId(
Integer.toString(ssl.hashCode()).getBytes());

}
}

return ses;
}
}

/**
* Check if cipher suite from original WOLFSSL_SESSION
* (WolfSSLImplementSSLSession) is available in new WolfSSLSession
* WolfSSLParameters.
*
* This is used in getSession(), since if we try resuming an old session
* but the cipher suite used in that session is not available in the
* ClientHello, the server will close the connection and send back an
* alert. If wolfSSL on the server side, this will be an illegal_parameter
* alert.
*
* @param ses WolfSSLImplementSSLSession to get existing cipher suite from
* to check.
* @param enabledCipherSuites cipher suites enabled, usually coming from
* WolfSSLEngineHelper.getCiphers().
*
* @return true if cipher suite from session is available in
* WolfSSLParameters enabled suites, otherwise false.
*/
private boolean sessionCipherSuiteAvailable(WolfSSLImplementSSLSession ses,
String[] enabledCipherSuites) {

String sessionCipher = null;

if (ses == null || enabledCipherSuites == null) {
return false;
}

sessionCipher = ses.getSessionCipherSuite();

if (Arrays.asList(enabledCipherSuites).contains(sessionCipher)) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"WOLFSSL_SESSION cipher suite available in enabled ciphers");
return true;
}

WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"WOLFSSL_SESSION cipher suite (" + sessionCipher + ") differs " +
"from enabled suites list");

return false;
}

/**
* Check if protocol from original WOLFSSL_SESSION
* (WolfSSLImplementSSLSession) is available in new WolfSSLSession
* WolfSSLParameters.
*
* @param ses WolfSSLImplementSSLSession to get existing protocol from
* to check
* @param enabledProtocols protocols enabled on this SSLSocket/SSLEngine,
* usually coming from WolfSSLEngineHelper.getProtocols().
*
* @return true if protocol from session is available in WolfSSLParameters
* enabled protocols, otherwise false.
*/
private boolean sessionProtocolAvailable(WolfSSLImplementSSLSession ses,
String[] enabledProtocols) {

String sessionProtocol = null;

if (ses == null || enabledProtocols == null) {
return false;
}

sessionProtocol = ses.getProtocol();
if (sessionProtocol == null) {
return false;
}

if (Arrays.asList(enabledProtocols).contains(sessionProtocol)) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"WOLFSSL_SESSION protocol available in enabled protocols");
return true;
}

WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"WOLFSSL_SESSION protocol (" + sessionProtocol + ") differs " +
"from enabled protocol list: " + Arrays.asList(enabledProtocols));

return false;
}

/**
* Print summary of current SessionStore (LinkedHashMap) status.
* Prints out size of current SessionStore. If size is greater than zero,
Expand Down
11 changes: 10 additions & 1 deletion src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ private void initHandshakeInternal(SSLSocket socket, SSLEngine engine)

/* create non null session */
this.session = this.authStore.getSession(ssl, this.port,
sessCacheHostname, this.clientMode);
sessCacheHostname, this.clientMode, getCiphers(), getProtocols());

if (this.session != null) {
if (this.clientMode) {
Expand Down Expand Up @@ -1345,6 +1345,11 @@ else if (peerAddr != null) {
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));

/* Update cached values in WolfSSLImplementSSLSession from
* WolfSSLSession, in case that goes out of scope and is garbage
* collected (ex: protocol version). */
this.session.updateStoredSessionValues();

return ret;
}

Expand Down Expand Up @@ -1385,6 +1390,10 @@ protected synchronized void unsetVerifyCallback() {
*/
protected synchronized int saveSession() {
if (this.session != null && this.session.isValid()) {
/* Update values from WOLFSSL which are stored in
* WolfSSLImplementSSLSession (ex: protocol) */
this.session.updateStoredSessionValues();

if (this.clientMode) {
/* Only need to set resume on client side, server-side
* maintains session cache at native level. */
Expand Down
Loading

0 comments on commit 7746787

Please sign in to comment.