JNI/JSSE: wrap wolfSSL_set_SessionTicket_cb(), add session ticket callback to SSLEngine for detection of ticket received

pull/254/head
Chris Conlon 2025-02-13 14:27:05 -07:00
parent d245630133
commit 8449b6744e
6 changed files with 358 additions and 6 deletions

View File

@ -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;
@ -5622,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)
@ -5764,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)
{

View File

@ -863,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

View File

@ -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 \

View File

@ -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 @@ public class WolfSSLSession {
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.
*
@ -415,6 +427,7 @@ public class WolfSSLSession {
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);
@ -4819,6 +4832,47 @@ public class WolfSSLSession {
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 <code>SSL_SUCCESS</code> on success. <code>
* NOT_COMPILED_IN</code> 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.
*

View File

@ -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.
* <p>
* To use this interface, native wolfSSL must be compiled with
* HAVE_SESSION_TICKET defined.
* </p>
* 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);
}

View File

@ -29,6 +29,7 @@ import com.wolfssl.WolfSSLIOSendCallback;
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;
@ -111,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();
@ -306,6 +312,12 @@ public class WolfSSLEngine extends SSLEngine {
ssl.setIOSend(sendCb);
ssl.setIOReadCtx(this);
ssl.setIOWriteCtx(this);
/* Session ticket callback */
if (sessTicketCb == null) {
sessTicketCb = new SessionTicketCB();
}
ssl.setSessionTicketCb(sessTicketCb, this);
}
}
@ -338,6 +350,9 @@ public class WolfSSLEngine extends SSLEngine {
if (recvCb == null) {
recvCb = new RecvCB();
}
if (sessTicketCb == null) {
sessTicketCb = new SessionTicketCB();
}
/* will throw WolfSSLException if issue creating WOLFSSL */
ssl = new WolfSSLSession(ctx, false);
@ -1022,6 +1037,7 @@ public class WolfSSLEngine extends SSLEngine {
int produced = 0;
long dtlsPrevDropCount = 0;
long dtlsCurrDropCount = 0;
int prevSessionTicketCount = 0;
byte[] tmp;
/* Set initial status for SSLEngineResult return */
@ -1149,11 +1165,13 @@ public class WolfSSLEngine extends SSLEngine {
}
}
else {
/* Get previous DTLS drop count, before we process any
* incomming data. Allows us to set BUFFER_UNDERFLOW status
* (or not if packet decrypt failed and was dropped) */
/* 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) {
@ -1284,12 +1302,17 @@ public class WolfSSLEngine extends SSLEngine {
dtlsCurrDropCount = ssl.getDtlsMacDropCount();
}
/* Detect if we need to set BUFFER_UNDERFLOW */
/* 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 &&
in.remaining() == 0 && (this.toSend == null ||
(this.toSend != null && this.toSend.length == 0))) {
(this.toSend != null && this.toSend.length == 0))
&& (prevSessionTicketCount ==
this.sessionTicketCount)) {
if ((this.ssl.dtls() == 0) ||
(this.handshakeFinished &&
@ -2154,6 +2177,32 @@ public class WolfSSLEngine extends SSLEngine {
}
}
/**
* 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() {
@ -2180,6 +2229,17 @@ public class WolfSSLEngine extends SSLEngine {
}
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 {