Merge pull request #257 from cconlon/sslEngineFixups

SSLEngine Performance Optimizations
pull/260/head
JacobBarthelmeh 2025-04-29 15:31:54 -06:00 committed by GitHub
commit e858d7590d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1556 additions and 898 deletions

View File

@ -53,6 +53,22 @@ JavaVM* g_vm;
/* global object refs for logging callbacks */
static jobject g_loggingCbIfaceObj;
/* global method IDs we can cache for performance */
jmethodID g_sslIORecvMethodId = NULL;
jmethodID g_sslIORecvMethodId_BB = NULL;
jmethodID g_sslIOSendMethodId = NULL;
jmethodID g_sslIOSendMethodId_BB = NULL;
jmethodID g_isArrayIORecvCallbackSet = NULL;
jmethodID g_isArrayIOSendCallbackSet = NULL;
jmethodID g_isByteBufferIORecvCallbackSet = NULL;
jmethodID g_isByteBufferIOSendCallbackSet = NULL;
jmethodID g_bufferPositionMethodId = NULL;
jmethodID g_bufferLimitMethodId = NULL;
jmethodID g_bufferHasArrayMethodId = NULL;
jmethodID g_bufferArrayMethodId = NULL;
jmethodID g_bufferSetPositionMethodId = NULL;
jmethodID g_verifyCallbackMethodId = NULL;
#ifdef HAVE_FIPS
/* global object ref for FIPS error callback */
static jobject g_fipsCbIfaceObj;
@ -61,16 +77,192 @@ static jobject g_fipsCbIfaceObj;
/* custom native fn prototypes */
void NativeLoggingCallback(const int logLevel, const char *const logMessage);
/* called when native library is loaded */
/* Called when native library is loaded.
* We also cache global jmethodIDs here for performance. */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jclass sslClass = NULL;
jclass byteBufferClass = NULL;
jclass verifyClass = NULL;
(void)reserved;
/* store JavaVM */
g_vm = vm;
/* get JNIEnv from JavaVM */
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
printf("Unable to get JNIEnv from JavaVM in JNI_OnLoad()\n");
return JNI_ERR;
}
/* Cache the method ID for IO send and recv callbacks */
sslClass = (*env)->FindClass(env, "com/wolfssl/WolfSSLSession");
if (sslClass == NULL) {
return JNI_ERR;
}
g_sslIORecvMethodId = (*env)->GetMethodID(env, sslClass,
"internalIOSSLRecvCallback",
"(Lcom/wolfssl/WolfSSLSession;[BI)I");
if (g_sslIORecvMethodId == NULL) {
return JNI_ERR;
}
g_sslIORecvMethodId_BB = (*env)->GetMethodID(env, sslClass,
"internalIOSSLRecvCallback",
"(Lcom/wolfssl/WolfSSLSession;Ljava/nio/ByteBuffer;I)I");
if (g_sslIORecvMethodId_BB == NULL) {
return JNI_ERR;
}
g_sslIOSendMethodId = (*env)->GetMethodID(env, sslClass,
"internalIOSSLSendCallback",
"(Lcom/wolfssl/WolfSSLSession;[BI)I");
if (g_sslIOSendMethodId == NULL) {
return JNI_ERR;
}
g_sslIOSendMethodId_BB = (*env)->GetMethodID(env, sslClass,
"internalIOSSLSendCallback",
"(Lcom/wolfssl/WolfSSLSession;Ljava/nio/ByteBuffer;I)I");
if (g_sslIOSendMethodId_BB == NULL) {
return JNI_ERR;
}
g_isArrayIORecvCallbackSet = (*env)->GetMethodID(env, sslClass,
"isArrayIORecvCallbackSet", "()Z");
if (g_isArrayIORecvCallbackSet == NULL) {
return JNI_ERR;
}
g_isArrayIOSendCallbackSet = (*env)->GetMethodID(env, sslClass,
"isArrayIOSendCallbackSet", "()Z");
if (g_isArrayIOSendCallbackSet == NULL) {
return JNI_ERR;
}
g_isByteBufferIORecvCallbackSet = (*env)->GetMethodID(env, sslClass,
"isByteBufferIORecvCallbackSet", "()Z");
if (g_isByteBufferIORecvCallbackSet == NULL) {
return JNI_ERR;
}
g_isByteBufferIOSendCallbackSet = (*env)->GetMethodID(env, sslClass,
"isByteBufferIOSendCallbackSet", "()Z");
if (g_isByteBufferIOSendCallbackSet == NULL) {
return JNI_ERR;
}
/* Cache ByteBuffer method IDs */
byteBufferClass = (*env)->FindClass(env, "java/nio/ByteBuffer");
if (byteBufferClass == NULL) {
return JNI_ERR;
}
g_bufferPositionMethodId = (*env)->GetMethodID(env, byteBufferClass,
"position", "()I");
if (g_bufferPositionMethodId == NULL) {
return JNI_ERR;
}
g_bufferLimitMethodId = (*env)->GetMethodID(env, byteBufferClass,
"limit", "()I");
if (g_bufferLimitMethodId == NULL) {
return JNI_ERR;
}
g_bufferHasArrayMethodId = (*env)->GetMethodID(env, byteBufferClass,
"hasArray", "()Z");
if (g_bufferHasArrayMethodId == NULL) {
return JNI_ERR;
}
g_bufferArrayMethodId = (*env)->GetMethodID(env, byteBufferClass,
"array", "()[B");
if (g_bufferArrayMethodId == NULL) {
return JNI_ERR;
}
g_bufferSetPositionMethodId = (*env)->GetMethodID(env, byteBufferClass,
"position", "(I)Ljava/nio/Buffer;");
if (g_bufferSetPositionMethodId == NULL) {
return JNI_ERR;
}
/* Cache verify callback method ID */
verifyClass = (*env)->FindClass(env, "com/wolfssl/WolfSSLVerifyCallback");
if (verifyClass == NULL) {
return JNI_ERR;
}
g_verifyCallbackMethodId = (*env)->GetMethodID(env, verifyClass,
"verifyCallback", "(IJ)I");
if (g_verifyCallbackMethodId == NULL) {
return JNI_ERR;
}
/* Clean up local reference to class, not needed */
(*env)->DeleteLocalRef(env, sslClass);
(*env)->DeleteLocalRef(env, byteBufferClass);
(*env)->DeleteLocalRef(env, verifyClass);
return JNI_VERSION_1_6;
}
/* Called when native library is unloaded.
* We clear cached method IDs here. */
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return;
}
/* Clear cached method ID */
g_sslIORecvMethodId = NULL;
g_sslIORecvMethodId_BB = NULL;
g_sslIOSendMethodId = NULL;
g_sslIOSendMethodId_BB = NULL;
g_isArrayIORecvCallbackSet = NULL;
g_isArrayIOSendCallbackSet = NULL;
g_isByteBufferIORecvCallbackSet = NULL;
g_isByteBufferIOSendCallbackSet = NULL;
g_bufferPositionMethodId = NULL;
g_bufferLimitMethodId = NULL;
g_bufferHasArrayMethodId = NULL;
g_bufferArrayMethodId = NULL;
g_bufferSetPositionMethodId = NULL;
g_verifyCallbackMethodId = NULL;
}
/**
* Throw WolfSSLJNIException
*/
void throwWolfSSLJNIException(JNIEnv* jenv, const char* msg)
{
jclass excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException");
if (excClass == NULL) {
/* Unable to find exception class, give up trying to throw */
return;
}
(*jenv)->ThrowNew(jenv, excClass, msg);
}
/**
* Throw WolfSSLException
*/
void throwWolfSSLException(JNIEnv* jenv, const char* msg)
{
jclass excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLException");
if (excClass == NULL) {
/* Unable to find exception class, give up trying to throw */
return;
}
(*jenv)->ThrowNew(jenv, excClass, msg);
}
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_init
(JNIEnv* jenv, jobject jcl)
{

View File

@ -1028,7 +1028,6 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
jobject ctxRef; /* WolfSSLContext object */
jclass innerCtxClass; /* WolfSSLContext class */
jmethodID recvCbMethodId; /* internalIORecvCallback ID */
jbyteArray inData;
if (!g_vm || !ssl || !buf || !ctx) {
@ -1140,18 +1139,10 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* call internal I/O recv callback */
recvCbMethodId = (*jenv)->GetMethodID(jenv, innerCtxClass,
"internalIORecvCallback",
"(Lcom/wolfssl/WolfSSLSession;[BI)I");
if (!recvCbMethodId) {
if ((*jenv)->ExceptionOccurred(jenv)) {
(*jenv)->ExceptionDescribe(jenv);
(*jenv)->ExceptionClear(jenv);
}
/* make sure cached recv callback method ID is not null */
if (!g_sslIORecvMethodId) {
(*jenv)->ThrowNew(jenv, excClass,
"Error getting internalIORecvCallback method from JNI");
(*jenv)->DeleteLocalRef(jenv, ctxRef);
"Cached recv callback method ID is null in NativeIORecvCb");
if (needsDetach)
(*g_vm)->DetachCurrentThread(g_vm);
return WOLFSSL_CBIO_ERR_GENERAL;
@ -1161,7 +1152,7 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
inData = (*jenv)->NewByteArray(jenv, sz);
if (!inData) {
(*jenv)->ThrowNew(jenv, excClass,
"Error getting internalIORecvCallback method from JNI");
"Error creating jbyteArray in NativeIORecvCb");
(*jenv)->DeleteLocalRef(jenv, ctxRef);
if (needsDetach)
(*g_vm)->DetachCurrentThread(g_vm);
@ -1170,7 +1161,7 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
/* call Java send callback, ignore native ctx since Java
* handles it */
retval = (*jenv)->CallIntMethod(jenv, ctxRef, recvCbMethodId,
retval = (*jenv)->CallIntMethod(jenv, ctxRef, g_sslIORecvMethodId,
(jobject)(*g_cachedSSLObj),
inData, (jint)sz);

File diff suppressed because it is too large Load Diff

View File

@ -967,6 +967,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setMTU
JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLSession_stateStringLong
(JNIEnv *, jobject, jlong);
/*
* Class: com_wolfssl_WolfSSLSession
* Method: getMaxOutputSize
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getMaxOutputSize
(JNIEnv *, jobject, jlong);
#ifdef __cplusplus
}
#endif

View File

@ -25,7 +25,25 @@
#define _Included_com_wolfssl_globals
/* global JavaVM reference for JNIEnv lookup */
extern JavaVM* g_vm;
extern JavaVM* g_vm;
/* Cache static jmethodIDs for performance, since they are guaranteed to be the
* same across all threads once cached. Initialized in JNI_OnLoad() and freed in
* JNI_OnUnload(). */
extern jmethodID g_sslIORecvMethodId; /* WolfSSLSession.internalIOSSLRecvCallback */
extern jmethodID g_sslIORecvMethodId_BB; /* WolfSSLSession.internalIOSSLRecvCallback_BB */
extern jmethodID g_sslIOSendMethodId; /* WolfSSLSession.internalIOSSLSendCallback */
extern jmethodID g_sslIOSendMethodId_BB; /* WolfSSLSession.internalIOSSLSendCallback_BB */
extern jmethodID g_isArrayIORecvCallbackSet; /* WolfSSL.isArrayIORecvCallbackSet */
extern jmethodID g_isArrayIOSendCallbackSet; /* WolfSSL.isArrayIOSendCallbackSet */
extern jmethodID g_isByteBufferIORecvCallbackSet; /* WolfSSL.isByteBufferIORecvCallbackSet */
extern jmethodID g_isByteBufferIOSendCallbackSet; /* WolfSSL.isByteBufferIOSendCallbackSet */
extern jmethodID g_bufferPositionMethodId; /* ByteBuffer.position() */
extern jmethodID g_bufferLimitMethodId; /* ByteBuffer.limit() */
extern jmethodID g_bufferHasArrayMethodId; /* ByteBuffer.hasArray() */
extern jmethodID g_bufferArrayMethodId; /* ByteBuffer.array() */
extern jmethodID g_bufferSetPositionMethodId; /* ByteBuffer.position(int) */
extern jmethodID g_verifyCallbackMethodId; /* WolfSSLVerifyCallback.verifyCallback */
/* struct to hold I/O class, object refs */
typedef struct {
@ -38,5 +56,8 @@ unsigned int NativePskClientCb(WOLFSSL* ssl, const char* hint, char* identity,
unsigned int NativePskServerCb(WOLFSSL* ssl, const char* identity,
unsigned char* key, unsigned int max_key_len);
#endif
/* Helper functions to throw exceptions */
void throwWolfSSLJNIException(JNIEnv* jenv, const char* msg);
void throwWolfSSLException(JNIEnv* jenv, const char* msg);
#endif

View File

@ -35,6 +35,8 @@ done
infer --fail-on-issue run -- javac \
src/java/com/wolfssl/WolfSSL.java \
src/java/com/wolfssl/WolfSSLALPNSelectCallback.java \
src/java/com/wolfssl/WolfSSLByteBufferIORecvCallback.java \
src/java/com/wolfssl/WolfSSLByteBufferIOSendCallback.java \
src/java/com/wolfssl/WolfSSLCertManager.java \
src/java/com/wolfssl/WolfSSLCertRequest.java \
src/java/com/wolfssl/WolfSSLCertificate.java \

View File

@ -626,6 +626,9 @@ public class WolfSSL {
getTls13SecretEnum_EXPORTER_SECRET();
this.active = true;
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, "wolfSSL library initialization done");
}
/* ------------------- private/protected methods -------------------- */

View File

@ -0,0 +1,67 @@
/* WolfSSLByteBufferIORecvCallback.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;
import java.nio.ByteBuffer;
/**
* wolfSSL ByteBuffer I/O Receive Callback Interface.
*
* This interface specifies how applicaitons should implement the I/O receive
* callback class to be used by wolfSSL, using a ByteBuffer as the buffer.
* <p>
* After implementing this interface, it should be passed as a parameter
* to the {@link WolfSSLContext#setIORecv(WolfSSLIORecvCallback)
* WolfSSLContext.setIORecv()} or
* {@link WolfSSLSession#setIORecv(WolfSSLIORecvCallback)
* WolfSSLSession.setIORecv()} methods to be registered with the native wolfSSL
* library.
*/
public interface WolfSSLByteBufferIORecvCallback {
/**
* I/O receive callback method, using ByteBuffer.
*
* This method acts as the I/O receive callback to be used with wolfSSL.
* This can be registered with an SSL session at the WolfSSLContext level
* using WolfSSLContext#setIORecv(WolfSSLIORecvCallback), or at the
* WolfSSLSession level using
* WolfSSLSession#setIORecv(WolfSSLIORecvCallback).
*
* This method will be called by native wolfSSL when it needs data to
* be read from the transport layer. The callback should read data and
* place the data into the buffer provided. The number of bytes read should
* be returned. The callback should return an error code on error.
*
* @param ssl the current SSL session object from which the callback was
* initiated.
* @param buf buffer in which the application should place data which
* has been received from the peer.
* @param sz size of buffer, <b>buf</b>
* @param ctx I/O context to be used.
* @return the number of bytes read, or an error. For possible error
* codes, see the default EmbedRecv() function in
* wolfssl_package/src/io.c
*/
public int receiveCallback(WolfSSLSession ssl, ByteBuffer buf, int sz,
Object ctx);
}

View File

@ -0,0 +1,67 @@
/* WolfSSLByteBufferIOSendCallback.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;
import java.nio.ByteBuffer;
/**
* wolfSSL I/O Send Callback Interface.
*
* This interface specifies how applicaitons should implement the I/O send
* callback class to be used by wolfSSL.
* <p>
* After implementing this interface, it should be passed as a parameter
* to the {@link WolfSSLContext#setIOSend(WolfSSLIOSendCallback)
* WolfSSLContext.setIOSend()} or
* {@link WolfSSLSession#setIOSend(WolfSSLIOSendCallback)
* WolfSSLSession.setIOSend()} methods to be registered with the native wolfSSL
* library.
*/
public interface WolfSSLByteBufferIOSendCallback {
/**
* I/O send callback method, using ByteBuffer.
*
* This method acts as the I/O send callback to be used with wolfSSL.
* This can be registered with an SSL session at the WolfSSLContext level
* using WolfSSLContext#setIOSend(WolfSSLIOSendCallback), or at the
* WolfSSLSession level using
* WolfSSLSession#setIOSend(WolfSSLIOSendCallback).
*
* This method will be called by native wolfSSL when it needs to send data.
* The callback should send data from the provided ByteBuffer (buf) across
* the transport layer. The number of bytes sent should be returned, or
* a negative error code on error.
*
* @param ssl the current SSL session object from which the callback was
* initiated.
* @param buf buffer containing data to be sent to the peer.
* @param sz size of data in buffer "<b>buf</b>"
* @param ctx I/O context to be used.
* @return the number of bytes sent, or an error. For possible error
* codes, see the default EmbedSend() function in
* wolfssl_package/src/io.c
*/
public int sendCallback(WolfSSLSession ssl, ByteBuffer buf, int sz,
Object ctx);
}

View File

@ -71,11 +71,21 @@ public class WolfSSLSession {
private WolfSSLPskClientCallback internPskClientCb = null;
private WolfSSLPskServerCallback internPskServerCb = null;
/* user-registerd I/O callbacks, called by internal WolfSSLSession
* I/O callback. This is done in order to pass references to
* WolfSSLSession object */
private WolfSSLIORecvCallback internRecvSSLCb;
private WolfSSLIOSendCallback internSendSSLCb;
/* User-registerd I/O callbacks:
*
* These are called by internal WolfSSLSession I/O callback. This is done
* in order to pass references to WolfSSLSession object. There are two sets
* of I/O callbacks here, one set that will use byte[] and one that will
* use ByteBuffer. Only one send and one recv callback can be set at a time
* between the two. Native JNI code will give preference to using the
* ByteBuffer variants for performance if set, since this will avoid an
* extra native allocation (NewByteArray()). The ByteBuffer variant will
* wrap the pre-allocated wolfSSL array in a Java direct ByteBuffer
* to pass back up to Java. */
private WolfSSLIORecvCallback internRecvSSLCb_array;
private WolfSSLIOSendCallback internSendSSLCb_array;
private WolfSSLByteBufferIORecvCallback internRecvSSLCb_BB;
private WolfSSLByteBufferIOSendCallback internSendSSLCb_BB;
/* user-registered ALPN select callback, called by internal WolfSSLSession
* ALPN select callback */
@ -237,20 +247,66 @@ public class WolfSSLSession {
return this.rsaDecCtx;
}
/* Methods to detect which I/O callback variants have been set */
private boolean isArrayIOSendCallbackSet() {
return (internSendSSLCb_array != null);
}
private boolean isByteBufferIOSendCallbackSet() {
return (internSendSSLCb_BB != null);
}
private boolean isArrayIORecvCallbackSet() {
return (internRecvSSLCb_array != null);
}
private boolean isByteBufferIORecvCallbackSet() {
return (internRecvSSLCb_BB != null);
}
/* These callbacks will be registered with native wolfSSL library */
private int internalIOSSLRecvCallback(WolfSSLSession ssl, byte[] buf,
int sz)
/**
* Internal wolfSSL I/O receive callback, using byte array.
*/
private int internalIOSSLRecvCallback(WolfSSLSession ssl,
byte[] buf, int sz)
{
/* call user-registered recv method */
return internRecvSSLCb.receiveCallback(ssl, buf, sz,
return internRecvSSLCb_array.receiveCallback(ssl, buf, sz,
ssl.getIOReadCtx());
}
private int internalIOSSLSendCallback(WolfSSLSession ssl, byte[] buf,
int sz)
/**
* Internal wolfSSL I/O receive callback, using ByteBuffer.
*/
private int internalIOSSLRecvCallback(WolfSSLSession ssl,
ByteBuffer buf, int sz)
{
/* call user-registered recv method */
return internSendSSLCb.sendCallback(ssl, buf, sz,
return internRecvSSLCb_BB.receiveCallback(ssl, buf, sz,
ssl.getIOReadCtx());
}
/**
* Internal wolfSSL I/O send callback, using byte array.
*/
private int internalIOSSLSendCallback(WolfSSLSession ssl,
byte[] buf, int sz)
{
/* call user-registered recv method */
return internSendSSLCb_array.sendCallback(ssl, buf, sz,
ssl.getIOWriteCtx());
}
/**
* Internal wolfSSL I/O send callback, using ByteBuffer.
*/
private int internalIOSSLSendCallback(WolfSSLSession ssl,
ByteBuffer buf, int sz)
{
/* call user-registered recv method */
return internSendSSLCb_BB.sendCallback(ssl, buf, sz,
ssl.getIOWriteCtx());
}
@ -440,6 +496,7 @@ public class WolfSSLSession {
private native int getThreadsBlockedInPoll(long ssl);
private native int setMTU(long ssl, int mtu);
private native String stateStringLong(long ssl);
private native int getMaxOutputSize(long ssl);
/* ------------------- session-specific methods --------------------- */
@ -2556,6 +2613,35 @@ public class WolfSSLSession {
return state;
}
/**
* Return max record layer size plaintext input size
*
* @return max record layer size plaintext input size in bytes, or
* negative value on error.
*
* @throws IllegalStateException WolfSSLContext has been freed
*/
public int getMaxOutputSize() throws IllegalStateException {
int ret;
confirmObjectIsActive();
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr,
"entered getOutputSize()");
ret = getMaxOutputSize(this.sslPtr);
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr,
"max output size: " + ret);
}
return ret;
}
/**
* Determine if a reused session was negotiated during the SSL
* handshake.
@ -3098,9 +3184,6 @@ public class WolfSSLSession {
confirmObjectIsActive();
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr, "entered getIOReadCtx()");
return this.ioReadCtx;
}
}
@ -3149,9 +3232,6 @@ public class WolfSSLSession {
confirmObjectIsActive();
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr, "entered getIOWriteCtx()");
return this.ioWriteCtx;
}
}
@ -4338,11 +4418,18 @@ public class WolfSSLSession {
/**
* Registers a receive callback for wolfSSL to get input data.
* By default, wolfSSL uses EmbedReceive() in src/io.c as the callback.
* This uses the system's TCP recv() function. The user can register a
* function to get input from memory, some other network module, or from
* anywhere. Please see the EmbedReceive() function in src/io.c as a
* guide for how the function should work and for error codes.
*
* This receive callback uses WolfSSLIORecvCallback which uses
* byte arrays (byte[]). To use direct ByteBuffers, and avoid an extra
* JNI array allocaiton, use
* setIORecvByteBuffer(WolfSSLByteBufferIORecvCallback).
*
* By default, wolfSSL uses EmbedReceive() in src/io.c as the callback
* which uses the system TCP recv() function. The user can register a
* method here to get receive TLS-encoded data from memory, some other
* network module, or anywhere else. Please see the EmbedReceive() function
* in src/io.c as a guide for how the function should work and for error
* codes.
* <p>
* In particular, <b>IO_ERR_WANT_READ</b> should be returned for
* non-blocking receive when no data is ready.
@ -4364,10 +4451,16 @@ public class WolfSSLSession {
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr,
"entered setIORecv(" + callback + ")");
"entered setIORecv(" + callback + ") - byte array");
/* Only allow one recv callback registered at a time, if ByteBuffer
* version has been set, set to null before setting byte variant. */
if (internRecvSSLCb_BB != null) {
internRecvSSLCb_BB = null;
}
/* set user I/O recv */
internRecvSSLCb = callback;
internRecvSSLCb_array = callback;
if (callback != null) {
/* register internal callback with native library */
@ -4377,12 +4470,74 @@ public class WolfSSLSession {
}
/**
* Registers a send callback for wolfSSL to write output data.
* Registers a receive callback for wolfSSL to get input data.
*
* This receive callback uses WolfSSLByteBufferIORecvCallback which uses
* a direct ByteBuffer. To use a byte array instead use
* setIORecv(WolfSSLIORecvCallback), but this will incur one additional
* native JNI array allocation.
*
* By default, wolfSSL uses EmbedReceive() in src/io.c as the callback
* which uses the system TCP recv() function. The user can register a
* method here to get receive TLS-encoded data from memory, some other
* network module, or anywhere else. Please see the EmbedReceive() function
* in src/io.c as a guide for how the function should work and for error
* codes.
* <p>
* In particular, <b>IO_ERR_WANT_READ</b> should be returned for
* non-blocking receive when no data is ready.
*
* @param callback method to be registered as the receive callback for
* the wolfSSL context. The signature of this function
* must follow that as shown in
* WolfSSLByteBufferIORecvCallback#receiveCallback(
* WolfSSLSession, ByteBuffer, int, long).
* @throws IllegalStateException WolfSSLContext has been freed
* @throws WolfSSLJNIException Internal JNI error
* @see #setIOSend(WolfSSLIOSendCallback)
*/
public void setIORecvByteBuffer(WolfSSLByteBufferIORecvCallback callback)
throws IllegalStateException, WolfSSLJNIException {
confirmObjectIsActive();
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr,
"entered setIORecv(" + callback + ") - ByteBuffer");
/* Only allow one recv callback registered at a time, if array
* version has been set, set to null before setting ByteBuffer. */
if (internRecvSSLCb_array != null) {
internRecvSSLCb_array = null;
}
/* set user I/O recv */
internRecvSSLCb_BB = callback;
if (callback != null) {
/* Register internal callback with native library, registers
* JNI NativeSSLIORecvCb() with native wolfSSL.
* NativeSSLIORecvCb() will then call back into
* internRecvSSLCb_array/BB(). */
setSSLIORecv(this.sslPtr);
}
}
}
/**
* Registers a send callback for wolfSSL to write TLS encoded output data.
*
* This send callback uses WolfSSLIOSendCallback which uses byte arrays
* (byte[]). To use direct ByteBuffers, and avoid an extra JNI array
* allocation, use setIOSendByteBuffer(WolfSSLByteBufferIOSendCallback).
*
* By default, wolfSSL uses EmbedSend() in src/io.c as the callback,
* which uses the system's TCP send() function. The user can register
* a function to send output to memory, some other network module, or
* to anywhere. Please see the EmbedSend() function in src/io.c as a
* guide for how the function should work and for error codes.
* which uses the system TCP send() function. The user can register
* a method here to send TLS-encoded output data to memory, some other
* network module, or to anywhere else. Please see the EmbedSend() function
* in src/io.c as a guide for how the function should work and for error
* codes.
* <p>
* In particular, <b>IO_ERR_WANT_WRITE</b> should be returned for
* non-blocking send when the action cannot be taken yet.
@ -4404,13 +4559,78 @@ public class WolfSSLSession {
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr,
"entered setIOSend(" + callback + ")");
"entered setIOSend(" + callback + ") - byte array");
/* Only allow one send callback registered at a time, if ByteBuffer
* version has been set, set to null before setting byte variant. */
if (internSendSSLCb_BB != null) {
internSendSSLCb_BB = null;
}
/* set user I/O send */
internSendSSLCb = callback;
internSendSSLCb_array = callback;
if (callback != null) {
/* register internal callback with native library */
/* Register internal callback with native library, registers
* JNI NativeSSLIOSendCb() with native wolfSSL.
* NativeSSLIOSendCb() will then call back into
* internSendSSLCb_array/BB(). */
setSSLIOSend(this.sslPtr);
}
}
}
/**
* Registers a send callback for wolfSSL to write TLS encoded output data.
*
* This send callback uses WolfSSLByteBufferIOSendCallback which uses a
* ByteBuffer. To use a byte array instead, use
* setIOSend(WolfSSLIOSendCallback), but this will incur one
* additional native JNI array allocation.
*
* By default, wolfSSL uses EmbedSend() in src/io.c as the callback,
* which uses the system TCP send() function. The user can register
* a method here to send TLS-encoded output data to memory, some other
* network module, or to anywhere else. Please see the EmbedSend() function
* in src/io.c as a guide for how the function should work and for error
* codes.
* <p>
* In particular, <b>IO_ERR_WANT_WRITE</b> should be returned for
* non-blocking send when the action cannot be taken yet.
*
* @param callback method to be registered as the send callback for
* the wolfSSL context. The signature of this function
* must follow that as shown in
* WolfSSLByteBufferIOSendCallback#sendCallback(
* WolfSSLSession, ByteBuffer, int, Object).
* @throws IllegalStateException WolfSSLSession has been freed
* @throws WolfSSLJNIException Internal JNI error
* @see #setIORecv(WolfSSLIORecvCallback)
*/
public void setIOSendByteBuffer(WolfSSLByteBufferIOSendCallback callback)
throws IllegalStateException, WolfSSLJNIException {
confirmObjectIsActive();
synchronized (sslLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, this.sslPtr,
"entered setIOSend(" + callback + ") - ByteBuffer");
/* Only allow one recv callback registered at a time, if array
* version has been set, set to null before setting ByteBuffer. */
if (internSendSSLCb_array != null) {
internSendSSLCb_array = null;
}
/* set user I/O send */
internSendSSLCb_BB = callback;
if (callback != null) {
/* Register internal callback with native library, registers
* JNI NativeSSLIOSendCb() with native wolfSSL.
* NativeSSLIOSendCb() will then call back into
* internSendSSLCb_array/BB(). */
setSSLIOSend(this.sslPtr);
}
}

View File

@ -24,8 +24,8 @@ package com.wolfssl.provider.jsse;
import com.wolfssl.WolfSSL;
import com.wolfssl.WolfSSLDebug;
import com.wolfssl.WolfSSLException;
import com.wolfssl.WolfSSLIORecvCallback;
import com.wolfssl.WolfSSLIOSendCallback;
import com.wolfssl.WolfSSLByteBufferIORecvCallback;
import com.wolfssl.WolfSSLByteBufferIOSendCallback;
import com.wolfssl.WolfSSLJNIException;
import com.wolfssl.WolfSSLSession;
import com.wolfssl.WolfSSLALPNSelectCallback;
@ -37,7 +37,6 @@ import java.util.function.BiFunction;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.logging.Level;
import java.security.cert.CertificateEncodingException;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
@ -72,7 +71,6 @@ public class WolfSSLEngine extends SSLEngine {
private com.wolfssl.WolfSSLContext ctx = null;
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;
@ -126,6 +124,22 @@ public class WolfSSLEngine extends SSLEngine {
private ByteBuffer netData = null;
private final Object netDataLock = new Object();
/* Single buffer used to hold application data to be sent, allocated once
* inside SendAppData, of size SSLSession.getApplicationBufferSize() */
private ByteBuffer staticAppDataBuf = null;
/* Default size of internalIOSendBuf, 16k to match TLS record size.
* TODO - add upper bound on I/O send buf resize allocations. */
private static final int INTERNAL_IOSEND_BUF_SZ = 16 * 1024;
/* static buffer used to hold encrypted data to be sent, allocated inside
* internalSendCb() and expanded only if needed. Synchronize on toSendLock
* when accessing this buffer. */
private byte[] internalIOSendBuf = new byte[INTERNAL_IOSEND_BUF_SZ];
/* Total size of internalIOSendBuf */
private int internalIOSendBufSz = INTERNAL_IOSEND_BUF_SZ;
/* Offset into internalIOSendBuf to start writing data */
private int internalIOSendBufOffset = 0;
/* Locks for synchronization */
private final Object ioLock = new Object();
private final Object toSendLock = new Object();
@ -308,8 +322,8 @@ public class WolfSSLEngine extends SSLEngine {
if (recvCb == null) {
recvCb = new RecvCB();
}
ssl.setIORecv(recvCb);
ssl.setIOSend(sendCb);
ssl.setIORecvByteBuffer(recvCb);
ssl.setIOSendByteBuffer(sendCb);
ssl.setIOReadCtx(this);
ssl.setIOWriteCtx(this);
@ -335,8 +349,8 @@ public class WolfSSLEngine extends SSLEngine {
private void unsetSSLCallbacks() throws WolfSSLJNIException {
synchronized (ioLock) {
ssl.setIORecv(null);
ssl.setIOSend(null);
ssl.setIORecvByteBuffer(null);
ssl.setIOSendByteBuffer(null);
ssl.setIOReadCtx(null);
ssl.setIOWriteCtx(null);
}
@ -373,19 +387,18 @@ public class WolfSSLEngine extends SSLEngine {
int sendSz = 0;
synchronized (toSendLock) {
if (this.toSend != null) {
sendSz = Math.min(this.toSend.length, out.remaining());
out.put(this.toSend, 0, sendSz);
if (this.internalIOSendBuf != null) {
sendSz = Math.min(this.internalIOSendBufOffset, out.remaining());
out.put(this.internalIOSendBuf, 0, sendSz);
if (sendSz != this.toSend.length) {
/* resize and adjust remaining toSend data */
byte[] tmp = new byte[this.toSend.length - sendSz];
System.arraycopy(this.toSend, sendSz, tmp, 0,
this.toSend.length - sendSz);
this.toSend = tmp;
if (sendSz != this.internalIOSendBufOffset) {
System.arraycopy(this.internalIOSendBuf, sendSz,
this.internalIOSendBuf, 0,
this.internalIOSendBufOffset - sendSz);
}
else {
this.toSend = null;
/* reset internalIOSendBufOffset to zero, no data left */
this.internalIOSendBufOffset = 0;
}
}
}
@ -407,7 +420,7 @@ public class WolfSSLEngine extends SSLEngine {
this.closeNotifySent = true;
this.closeNotifyReceived = true;
this.inBoundOpen = false;
if (this.toSend == null || this.toSend.length == 0) {
if (this.internalIOSendBufOffset == 0) {
/* Don't close outbound if we have a close_notify alert
* send back to peer. Native wolfSSL may have already generated
* it and is reflected in SSL_SENT_SHUTDOWN flag, but we
@ -590,17 +603,23 @@ public class WolfSSLEngine extends SSLEngine {
int[] pos = new int[len]; /* in[] positions */
int[] limit = new int[len]; /* in[] limits */
/* get total input data size, store input array positions */
/* Get total input data size, store input array positions */
for (i = ofst; i < ofst + len; i++) {
totalIn += in[i].remaining();
pos[i] = in[i].position();
limit[i] = in[i].limit();
}
/* only send up to maximum app data size chunk */
sendSz = Math.min(totalIn,
this.engineHelper.getSession().getApplicationBufferSize());
dataBuf = ByteBuffer.allocate(sendSz);
/* Allocate static buffer for application data, clear before use */
sendSz = this.engineHelper.getSession().getApplicationBufferSize();
if (this.staticAppDataBuf == null) {
/* allocate static buffer for application data */
this.staticAppDataBuf = ByteBuffer.allocateDirect(sendSz);
}
this.staticAppDataBuf.clear();
/* Only send up to maximum app data size chunk */
sendSz = Math.min(totalIn, sendSz);
/* gather byte array of sendSz bytes from input buffers */
inputLeft = sendSz;
@ -608,7 +627,7 @@ public class WolfSSLEngine extends SSLEngine {
int bufChunk = Math.min(in[i].remaining(), inputLeft);
in[i].limit(in[i].position() + bufChunk); /* set limit */
dataBuf.put(in[i]); /* get data */
this.staticAppDataBuf.put(in[i]); /* get data */
inputLeft -= bufChunk;
in[i].limit(limit[i]); /* reset limit */
@ -618,8 +637,8 @@ public class WolfSSLEngine extends SSLEngine {
}
dataArr = new byte[sendSz];
dataBuf.rewind();
dataBuf.get(dataArr);
this.staticAppDataBuf.rewind();
this.staticAppDataBuf.get(dataArr);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"calling ssl.write() with size: " + sendSz);
@ -707,13 +726,8 @@ public class WolfSSLEngine extends SSLEngine {
"out.position(): " + out.position());
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"out.limit(): " + out.limit());
if (this.toSend != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: " + this.toSend.length);
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: 0");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"internalIOSendBufOffset: " + this.internalIOSendBufOffset);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"closeNotifySent: " + this.closeNotifySent);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -756,6 +770,10 @@ public class WolfSSLEngine extends SSLEngine {
/* Force out buffer to be large enough to hold max packet size */
if (out.remaining() <
this.engineHelper.getSession().getPacketBufferSize()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"out.remaining() too small (" +
out.remaining() + "), need at least: " +
this.engineHelper.getSession().getPacketBufferSize());
return new SSLEngineResult(Status.BUFFER_OVERFLOW, hs, 0, 0);
}
@ -832,13 +850,8 @@ public class WolfSSLEngine extends SSLEngine {
"out.position(): " + out.position());
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"out.limit(): " + out.limit());
if (this.toSend != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: " + this.toSend.length);
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: 0");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"internalIOSendBufOffset: " + this.internalIOSendBufOffset);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"closeNotifySent: " + this.closeNotifySent);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -1064,7 +1077,7 @@ public class WolfSSLEngine extends SSLEngine {
public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer[] out,
int ofst, int length) throws SSLException {
int i, ret = 0, sz = 0, err = 0;
int i, ret = 0, err = 0;
int inPosition = 0;
int inRemaining = 0;
int consumed = 0;
@ -1072,7 +1085,6 @@ public class WolfSSLEngine extends SSLEngine {
long dtlsPrevDropCount = 0;
long dtlsCurrDropCount = 0;
int prevSessionTicketCount = 0;
byte[] tmp;
/* Set initial status for SSLEngineResult return */
Status status = SSLEngineResult.Status.OK;
@ -1130,13 +1142,8 @@ public class WolfSSLEngine extends SSLEngine {
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ofst: " + ofst + ", length: " + length);
if (this.toSend != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: " + this.toSend.length);
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: 0");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"internalIOSendBufOffset: " + this.internalIOSendBufOffset);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"closeNotifySent: " + this.closeNotifySent);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -1174,7 +1181,7 @@ public class WolfSSLEngine extends SSLEngine {
hs = SSLEngineResult.HandshakeStatus.NEED_WRAP;
}
else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP &&
(this.toSend != null) && (this.toSend.length > 0)) {
this.internalIOSendBufOffset > 0) {
/* Already have data buffered to send and in NEED_WRAP state,
* just return so wrap() can be called */
hs = SSLEngineResult.HandshakeStatus.NEED_WRAP;
@ -1343,10 +1350,10 @@ public class WolfSSLEngine extends SSLEngine {
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))
&& (prevSessionTicketCount ==
this.sessionTicketCount)) {
in.remaining() == 0 &&
(this.internalIOSendBufOffset == 0) &&
(prevSessionTicketCount ==
this.sessionTicketCount)) {
if ((this.ssl.dtls() == 0) ||
(this.handshakeFinished &&
@ -1376,8 +1383,7 @@ public class WolfSSLEngine extends SSLEngine {
if (this.getUseClientMode() && this.handshakeFinished &&
this.ssl.hasSessionTicket() &&
this.sessionTicketReceived == false) {
if (this.ssl.dtls() == 1 && this.toSend != null &&
this.toSend.length > 0) {
if (this.ssl.dtls() == 1 && this.internalIOSendBufOffset > 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. */
@ -1414,13 +1420,8 @@ public class WolfSSLEngine extends SSLEngine {
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ofst: " + ofst + ", length: " + length);
if (this.toSend != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: " + this.toSend.length);
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"toSend.length: 0");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"internalIOSendBufOffset: " + this.internalIOSendBufOffset);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"handshakeFinished: " + this.handshakeFinished);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -1500,12 +1501,12 @@ public class WolfSSLEngine extends SSLEngine {
err = ssl.getError(ret);
}
/* Lock access to this.toSend and this.toRead */
/* Lock access to this.internalIOSendBuf and this.toRead */
synchronized (toSendLock) {
if (this.handshakeFinished == true) {
/* close_notify sent by wolfSSL but not across transport yet */
if (this.closeNotifySent == true &&
this.toSend != null && this.toSend.length > 0) {
this.internalIOSendBufOffset > 0) {
hs = SSLEngineResult.HandshakeStatus.NEED_WRAP;
}
/* close_notify received, need to send one back */
@ -1533,7 +1534,7 @@ public class WolfSSLEngine extends SSLEngine {
synchronized (netDataLock) {
synchronized (ioLock) {
if (sslConnectAcceptSuccess() && ssl.handshakeDone() &&
(this.toSend == null) &&
(this.internalIOSendBufOffset == 0) &&
(this.nativeWantsToWrite == 0) &&
(this.nativeWantsToRead == 0)) {
@ -1552,7 +1553,7 @@ public class WolfSSLEngine extends SSLEngine {
}
/* give priority of WRAP/UNWRAP to state of our internal
* I/O data buffers first, then wolfSSL err status */
else if (this.toSend != null && this.toSend.length > 0) {
else if (this.internalIOSendBufOffset > 0) {
hs = SSLEngineResult.HandshakeStatus.NEED_WRAP;
}
else if (this.netData != null &&
@ -2110,9 +2111,7 @@ public class WolfSSLEngine extends SSLEngine {
*
* @return number of bytes placed into send queue
*/
protected synchronized int internalSendCb(byte[] in, int sz) {
int totalSz = sz, idx = 0;
byte[] prevToSend = null;
protected synchronized int internalSendCb(ByteBuffer in, int sz) {
synchronized (toSendLock) {
/* As per JSSE Reference Guide, Section 8 DTLS implementation
@ -2122,7 +2121,8 @@ public class WolfSSLEngine extends SSLEngine {
/* TODO - do we need to fragment data here if larger than
* SSLParameters.getMaximumPacketSize() with DTLS? */
if (this.ssl.dtls() == 1) {
if (this.toSend != null) {
if ((this.internalIOSendBuf != null) &&
(this.internalIOSendBufOffset > 0)) {
/* 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
@ -2139,27 +2139,39 @@ public class WolfSSLEngine extends SSLEngine {
this.nativeWantsToWrite = 0;
}
/* Make copy of existing toSend array before expanding */
if (this.toSend != null) {
prevToSend = this.toSend.clone();
totalSz += this.toSend.length;
if (sz <= 0) {
/* No data to send */
return 0;
}
/* Allocate new array to hold data to be sent */
this.toSend = new byte[totalSz];
/* Copy existing toSend data over */
if (prevToSend != null) {
System.arraycopy(prevToSend, 0, this.toSend, idx,
prevToSend.length);
idx += prevToSend.length;
/* If we have more data than internal static buffer,
* grow buffer 2x (or up to sz needed) and copy data over */
if ((this.internalIOSendBufSz -
this.internalIOSendBufOffset) < sz) {
/* Allocate new buffer to hold data to be sent */
int newSz = this.internalIOSendBufSz * 2;
if (newSz < sz) {
newSz = sz;
}
byte[] newBuf = new byte[newSz];
System.arraycopy(this.internalIOSendBuf, 0,
newBuf, 0,
this.internalIOSendBufOffset);
this.internalIOSendBuf = newBuf;
this.internalIOSendBufSz = newSz;
}
System.arraycopy(in, 0, this.toSend, idx, sz);
}
if (ioDebugEnabled == true) {
WolfSSLDebug.logHex(getClass(), WolfSSLDebug.INFO,
"CB Write", in, sz);
/* Add data to end of internal static buffer */
in.get(this.internalIOSendBuf, this.internalIOSendBufOffset, sz);
if (ioDebugEnabled == true) {
WolfSSLDebug.logHex(getClass(), WolfSSLDebug.INFO,
"CB Write", Arrays.copyOfRange(this.internalIOSendBuf,
this.internalIOSendBufOffset,
this.internalIOSendBufOffset + sz), sz);
}
this.internalIOSendBufOffset += sz;
}
return sz;
@ -2169,15 +2181,16 @@ public class WolfSSLEngine extends SSLEngine {
* Internal receive callback. Reads from netData and gives bytes back
* to native wolfSSL for processing.
*
* @param toRead byte array into which to place data read from transport
* @param toRead ByteBuffer into which to place data read from transport
* @param sz number of bytes that should be read/copied from transport
*
* @return number of bytes read into toRead array or negative
* value on error
*/
protected synchronized int internalRecvCb(byte[] toRead, int sz) {
protected synchronized int internalRecvCb(ByteBuffer toRead, int sz) {
int max = 0;
int originalLimit = 0;
synchronized (netDataLock) {
if (ioDebugEnabled == true) {
@ -2211,13 +2224,22 @@ public class WolfSSLEngine extends SSLEngine {
this.nativeWantsToRead = 0;
max = (sz < this.netData.remaining()) ? sz : this.netData.remaining();
this.netData.get(toRead, 0, max);
/* Print out bytes read from toRead buffer */
if (ioDebugEnabled == true) {
int toReadPos = toRead.position();
byte[] tmpArr = new byte[max];
toRead.get(tmpArr, 0, max);
WolfSSLDebug.logHex(getClass(), WolfSSLDebug.INFO,
"CB Read", toRead, max);
"CB Read", tmpArr, max);
toRead.position(toReadPos);
}
originalLimit = this.netData.limit();
this.netData.limit(this.netData.position() + max);
toRead.put(this.netData);
this.netData.limit(originalLimit);
return max;
}
}
@ -2248,28 +2270,28 @@ public class WolfSSLEngine extends SSLEngine {
return 0;
}
private class SendCB implements WolfSSLIOSendCallback {
private class SendCB implements WolfSSLByteBufferIOSendCallback {
protected SendCB() {
}
public int sendCallback(WolfSSLSession ssl, byte[] toSend, int sz,
public int sendCallback(WolfSSLSession ssl, ByteBuffer toSend, int sz,
Object engine) {
return ((WolfSSLEngine)engine).internalSendCb(toSend, sz);
}
}
private class RecvCB implements WolfSSLIORecvCallback {
private class RecvCB implements WolfSSLByteBufferIORecvCallback {
protected RecvCB() {
}
public int receiveCallback(WolfSSLSession ssl, byte[] out, int sz,
public int receiveCallback(WolfSSLSession ssl, ByteBuffer buf, int sz,
Object engine) {
return ((WolfSSLEngine)engine).internalRecvCb(out, sz);
return ((WolfSSLEngine)engine).internalRecvCb(buf, sz);
}
}
@ -2292,6 +2314,11 @@ public class WolfSSLEngine extends SSLEngine {
this.ssl.freeSSL();
this.ssl = null;
}
/* Clear our reference to static application direct ByteBuffer */
if (this.staticAppDataBuf != null) {
this.staticAppDataBuf.clear();
this.staticAppDataBuf = null;
}
super.finalize();
}
}

View File

@ -760,15 +760,52 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession
return this.port;
}
/**
* Can be called by SSLEngine consumers to determine maximum
* output buffer size, to be used for buffer allocations.
*
* @return maximum output buffer size
*/
@Override
public int getPacketBufferSize() {
/* Match conscrypt's calculations here for maximum potential
* SSL/TLS record length. Used by SSLEngine consumers to allocate
* output buffer size.
public synchronized int getPacketBufferSize() {
/* JSSE implementations seem to set the SSL/TLS maximum packet size
* (record size) differently.
*
* Conscrypt calculates this as 18437 bytes:
*
* type(1) + version(2) + length(2) + 2^14 plaintext +
* max compression overhead (1024) + max AEAD overhead (1024) */
return 18437;
* max compression overhead (1024) + max AEAD overhead (1024)
*
* wolfJSSE originally matched the Conscrypt value, but it conflicts
* with the maximum allowed/used by the tls-channel project, which
* we have a few customers using. tls-channel uses a maximum size of
* 17k.
*
* Native wolfSSL has the wolfSSL_GetMaxOutputSize() function, but
* it only works post-handshake. getPacketBufferSize() can be called
* pre-handshake, so we set a default value here that matches
* the upper limit for tls-channel, which is 17k, then if
* wolfSSL_GetMaxOutputSize() is larger, we increase to that value.
*/
int nativeMax;
int ret = 17 * 1024;
/* Try to get maximum TLS record size from native wolfSSL */
if (ssl != null) {
nativeMax = ssl.getMaxOutputSize();
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ssl.getMaxOutputSize() returned: " + nativeMax);
if ((nativeMax > 0) && (nativeMax > ret)) {
ret = nativeMax;
}
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"getPacketBufferSize() returning: " + ret);
return ret;
}
@Override

View File

@ -1662,8 +1662,8 @@ public class WolfSSLEngineTest {
session = engine.getSession();
packetBufSz = session.getPacketBufferSize();
/* expected to be 18437 */
if (packetBufSz != 18437) {
/* expected to be 17k */
if (packetBufSz != (17 * 1024)) {
error("\t\t... failed");
fail("got incorrect packet buffer size (" +
enabledProtocols.get(i) + ")");

View File

@ -3393,7 +3393,7 @@ public class WolfSSLSocketTest {
throw cliException;
}
System.out.println("\t\t... passed");
System.out.println("\t... passed");
} finally {
/* Restore original property value */

View File

@ -644,13 +644,6 @@ public class WolfSSLContextTest {
fail("setMinECCKeySize should fail with negative key size");
}
/* key length not % 8 should fail */
ret = ctx.setMinECCKeySize(255);
if (ret != WolfSSL.BAD_FUNC_ARG) {
System.out.println("\t\t... failed");
fail("setMinECCKeySize should fail with non % 8 size");
}
/* valid key length should succeed */
ret = ctx.setMinECCKeySize(128);
if (ret != WolfSSL.SSL_SUCCESS) {

View File

@ -23,13 +23,10 @@ package com.wolfssl.test;
import org.junit.Test;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.IOException;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
@ -43,6 +40,8 @@ import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CountDownLatch;
import java.util.Arrays;
import java.nio.ByteBuffer;
import com.wolfssl.WolfSSL;
import com.wolfssl.WolfSSLDebug;
@ -53,12 +52,11 @@ import com.wolfssl.WolfSSLPskClientCallback;
import com.wolfssl.WolfSSLPskServerCallback;
import com.wolfssl.WolfSSLTls13SecretCallback;
import com.wolfssl.WolfSSLSession;
import com.wolfssl.WolfSSLByteBufferIORecvCallback;
import com.wolfssl.WolfSSLByteBufferIOSendCallback;
public class WolfSSLSessionTest {
private final static int TEST_FAIL = -1;
private final static int TEST_SUCCESS = 0;
private static String cliCert = "./examples/certs/client-cert.pem";
private static String cliKey = "./examples/certs/client-key.pem";
private static String srvCert = "./examples/certs/server-cert.pem";
@ -69,6 +67,9 @@ public class WolfSSLSessionTest {
private final static String exampleHost = "www.example.com";
private final static int examplePort = 443;
/* Maximum network buffer size, for test I/O callbacks */
private final static int MAX_NET_BUF_SZ = 17 * 1024;
private static WolfSSLContext ctx = null;
@BeforeClass
@ -889,7 +890,6 @@ public class WolfSSLSessionTest {
public void test_WolfSSLSession_setTls13SecretCb()
throws WolfSSLJNIException {
int ret;
WolfSSL sslLib = null;
WolfSSLContext sslCtx = null;
WolfSSLSession ssl = null;
@ -1413,5 +1413,409 @@ public class WolfSSLSessionTest {
System.out.println("\t... passed");
}
/**
* wolfSSL I/O context, is passed to I/O callbacks when called
* by native wolfSSL.
*/
private class MyIOCtx {
private byte[] cliToSrv = new byte[MAX_NET_BUF_SZ];
private byte[] srvToCli = new byte[MAX_NET_BUF_SZ];
private int cliToSrvUsed = 0;
private int srvToCliUsed = 0;
private int CLIENT_END = 1;
private int SERVER_END = 2;
private final Object cliLock = new Object();
private final Object srvLock = new Object();
private int insertData(byte[] dest, int destUsed,
ByteBuffer src, int len) {
int freeBufSpace = dest.length - destUsed;
/* Check if buffer is full */
if ((len > 0) && (freeBufSpace == 0)) {
return -1;
}
int bytesToCopy = Math.min(len, freeBufSpace);
if (bytesToCopy > 0) {
src.get(dest, destUsed, bytesToCopy);
}
return bytesToCopy;
}
private int getData(byte[] src, int srcUsed,
ByteBuffer dest, int len) {
/* src buffer is empty */
if ((len > 0) && (srcUsed == 0)) {
return -1;
}
int bytesToCopy = Math.min(len, srcUsed);
if (bytesToCopy > 0) {
dest.put(src, 0, bytesToCopy);
srcUsed -= bytesToCopy;
/* Shift remaining data to front of buffer */
if (srcUsed > 0) {
System.arraycopy(src, bytesToCopy, src, 0, srcUsed);
}
}
return bytesToCopy;
}
public int insertCliToSrvData(ByteBuffer buf, int len) {
synchronized (cliLock) {
int ret = insertData(cliToSrv, cliToSrvUsed, buf, len);
if (ret > 0) {
cliToSrvUsed += ret;
}
return ret;
}
}
public int insertSrvToCliData(ByteBuffer buf, int len) {
synchronized (srvLock) {
int ret = insertData(srvToCli, srvToCliUsed, buf, len);
if (ret > 0) {
srvToCliUsed += ret;
}
return ret;
}
}
public int getCliToSrvData(ByteBuffer buf, int len) {
synchronized (cliLock) {
int ret = getData(cliToSrv, cliToSrvUsed, buf, len);
if (ret > 0) {
cliToSrvUsed -= ret;
}
return ret;
}
}
public int getSrvToCliData(ByteBuffer buf, int len) {
synchronized (srvLock) {
int ret = getData(srvToCli, srvToCliUsed, buf, len);
if (ret > 0) {
srvToCliUsed -= ret;
}
return ret;
}
}
}
/* Client I/O callback using ByteBuffers */
private class ClientByteBufferIOCallback
implements WolfSSLByteBufferIORecvCallback,
WolfSSLByteBufferIOSendCallback {
/**
* Receive data is called when wolfSSL needs to read data from the
* transport layer. In this case, we read data from the beginning
* of the internal byte[] (buffer) and place it into the ByteBuffer buf.
*
* Return the number of bytes copied to the ByteBuffer buf, or negative
* on error.
*/
@Override
public synchronized int receiveCallback(WolfSSLSession ssl,
ByteBuffer buf, int len, Object ctx) {
int ret;
MyIOCtx ioCtx = (MyIOCtx) ctx;
ret = ioCtx.getSrvToCliData(buf, len);
if (ret == -1) {
/* No data available, return WOLFSSL_CBIO_ERR_WANT_READ */
ret = WolfSSL.WOLFSSL_CBIO_ERR_WANT_READ;
}
return ret;
}
/**
* Send data is called when wolfSSL needs to write data to the
* transport layer. In this case, we read data from the ByteBuffer
* buf and place it into our internal byte[] (buffer).
*
* Return the number of bytes copied from the ByteBuffer buf, or
* negative on error.
*/
@Override
public synchronized int sendCallback(
WolfSSLSession ssl, ByteBuffer buf, int len, Object ctx) {
int ret;
MyIOCtx ioCtx = (MyIOCtx) ctx;
ret = ioCtx.insertCliToSrvData(buf, len);
if (ret == -1) {
/* No space available, return WOLFSSL_CBIO_ERR_WANT_WRITE */
ret = WolfSSL.WOLFSSL_CBIO_ERR_WANT_WRITE;
}
return ret;
}
}
/* Server I/O callback using ByteBuffers */
private class ServerByteBufferIOCallback
implements WolfSSLByteBufferIORecvCallback,
WolfSSLByteBufferIOSendCallback {
/**
* Receive data is called when wolfSSL needs to read data from the
* transport layer. In this case, we read data from the beginning
* of the internal byte[] (buffer) and place it into the ByteBuffer buf.
*
* Return the number of bytes copied to the ByteBuffer buf, or negative
* on error.
*/
@Override
public synchronized int receiveCallback(WolfSSLSession ssl,
ByteBuffer buf, int len, Object ctx) {
int ret;
MyIOCtx ioCtx = (MyIOCtx) ctx;
ret = ioCtx.getCliToSrvData(buf, len);
if (ret == -1) {
/* No data available, return WOLFSSL_CBIO_ERR_WANT_READ */
ret = WolfSSL.WOLFSSL_CBIO_ERR_WANT_READ;
}
return ret;
}
/**
* Send data is called when wolfSSL needs to write data to the
* transport layer. In this case, we read data from the ByteBuffer
* buf and place it into our internal byte[] (buffer).
*
* Return the number of bytes copied from the ByteBuffer buf, or
* negative on error.
*/
@Override
public synchronized int sendCallback(
WolfSSLSession ssl, ByteBuffer buf, int len, Object ctx) {
int ret;
MyIOCtx ioCtx = (MyIOCtx) ctx;
ret = ioCtx.insertSrvToCliData(buf, len);
if (ret == -1) {
/* No space available, return WOLFSSL_CBIO_ERR_WANT_WRITE */
ret = WolfSSL.WOLFSSL_CBIO_ERR_WANT_WRITE;
}
return ret;
}
}
@Test
public void test_WolfSSLSession_ioBuffers() throws Exception {
int ret = 0;
int err = 0;
Socket cliSock = null;
WolfSSLSession cliSes = null;
byte[] testData = "Hello from client".getBytes();
byte[] servAppBuffer = new byte[MAX_NET_BUF_SZ];
byte[] cliAppBuffer = new byte[MAX_NET_BUF_SZ];
int bytesRead = 0;
/* Create client/server WolfSSLContext objects */
final WolfSSLContext srvCtx;
WolfSSLContext cliCtx;
System.out.print("\tTesting I/O CB with ByteBuffers");
/* Initialize library */
WolfSSL lib = new WolfSSL();
/* Create ServerSocket first to get ephemeral port */
final ServerSocket srvSocket = new ServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
WolfSSL.SSLv23_ServerMethod());
cliCtx = createAndSetupWolfSSLContext(cliCert, cliKey,
WolfSSL.SSL_FILETYPE_PEM, caCert,
WolfSSL.SSLv23_ClientMethod());
MyIOCtx myIOCb = new MyIOCtx();
ClientByteBufferIOCallback cliIOCb = new ClientByteBufferIOCallback();
ServerByteBufferIOCallback srvIOCb = new ServerByteBufferIOCallback();
ExecutorService es = Executors.newSingleThreadExecutor();
/* Start server */
try {
es.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
int ret;
int err;
Socket server = null;
WolfSSLSession srvSes = null;
int bytesRead = 0;
try {
server = srvSocket.accept();
srvSes = new WolfSSLSession(srvCtx);
/* Set I/O callback and ctx */
srvSes.setIOSendByteBuffer(srvIOCb);
srvSes.setIORecvByteBuffer(srvIOCb);
srvSes.setIOWriteCtx(myIOCb);
srvSes.setIOReadCtx(myIOCb);
/* Do handshake */
do {
ret = srvSes.accept();
err = srvSes.getError(ret);
} while (ret != WolfSSL.SSL_SUCCESS &&
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"Server accept failed: " + err);
}
/* Read data from client */
bytesRead = srvSes.read(servAppBuffer,
servAppBuffer.length, 0);
if (bytesRead <= 0) {
throw new Exception(
"Server read failed: " + bytesRead);
}
/* Send same data back to client */
ret = srvSes.write(servAppBuffer, bytesRead, 0);
if (ret != bytesRead) {
throw new Exception("Server write failed: " + ret);
}
srvSes.shutdownSSL();
srvSes.freeSSL();
srvSes = null;
server.close();
server = null;
} finally {
if (srvSes != null) {
srvSes.freeSSL();
}
if (server != null) {
server.close();
}
}
return null;
}
});
} catch (Exception e) {
System.out.println("\t... failed");
e.printStackTrace();
fail();
}
try {
/* Client connection */
cliSock = new Socket(InetAddress.getLocalHost(),
srvSocket.getLocalPort());
cliSes = new WolfSSLSession(cliCtx);
/* Set I/O callback and ctx */
cliSes.setIOSendByteBuffer(cliIOCb);
cliSes.setIORecvByteBuffer(cliIOCb);
cliSes.setIOWriteCtx(myIOCb);
cliSes.setIOReadCtx(myIOCb);
/* Do handshake */
do {
ret = cliSes.connect();
err = cliSes.getError(ret);
} while (ret != WolfSSL.SSL_SUCCESS &&
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
if (ret != WolfSSL.SSL_SUCCESS) {
throw new Exception(
"Client connect failed: " + err);
}
/* Send test data */
ret = cliSes.write(testData, testData.length, 0);
if (ret != testData.length) {
throw new Exception(
"Client write failed: " + ret);
}
/* Read response */
do {
bytesRead = cliSes.read(cliAppBuffer, cliAppBuffer.length, 0);
err = cliSes.getError(bytesRead);
} while (ret != WolfSSL.SSL_SUCCESS &&
err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE);
if (bytesRead != testData.length) {
throw new Exception(
"Client read failed: " + bytesRead);
}
/* Verify received data matches sent data using Java 8 compatible
* array comparison */
boolean arraysMatch = true;
if (testData.length != bytesRead) {
arraysMatch = false;
} else {
for (int i = 0; i < testData.length; i++) {
if (testData[i] != cliAppBuffer[i]) {
arraysMatch = false;
break;
}
}
}
if (!arraysMatch) {
throw new Exception("Received data does not match sent data");
}
cliSes.shutdownSSL();
cliSes.freeSSL();
cliSes = null;
cliSock.close();
cliSock = null;
} catch (Exception e) {
System.out.println("\t... failed");
e.printStackTrace();
fail();
} finally {
/* Free resources */
if (cliSes != null) {
cliSes.freeSSL();
}
if (cliSock != null) {
cliSock.close();
}
if (srvSocket != null) {
srvSocket.close();
}
if (srvCtx != null) {
srvCtx.free();
}
es.shutdown();
}
System.out.println("\t... passed");
}
}