From 1aec153270a051e61a44b8372359e4331ebad9b0 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 25 Feb 2026 14:17:29 -0700 Subject: [PATCH] JNI: add RSA-PSS sign/verify and RSA sign check PK callbacks --- native/com_wolfssl_WolfCryptRSA.c | 185 ++++ native/com_wolfssl_WolfCryptRSA.h | 16 + native/com_wolfssl_WolfSSLContext.c | 850 +++++++++++++++++- native/com_wolfssl_WolfSSLContext.h | 24 + scripts/infer.sh | 2 + src/java/com/wolfssl/WolfCryptRSA.java | 37 + src/java/com/wolfssl/WolfSSLContext.java | 192 +++- .../wolfssl/WolfSSLRsaPssSignCallback.java | 66 ++ .../wolfssl/WolfSSLRsaPssVerifyCallback.java | 63 ++ .../com/wolfssl/test/WolfSSLContextTest.java | 458 ++++++++++ 10 files changed, 1872 insertions(+), 21 deletions(-) create mode 100644 src/java/com/wolfssl/WolfSSLRsaPssSignCallback.java create mode 100644 src/java/com/wolfssl/WolfSSLRsaPssVerifyCallback.java diff --git a/native/com_wolfssl_WolfCryptRSA.c b/native/com_wolfssl_WolfCryptRSA.c index fe97dd8..74a341b 100644 --- a/native/com_wolfssl_WolfCryptRSA.c +++ b/native/com_wolfssl_WolfCryptRSA.c @@ -26,6 +26,8 @@ #include #endif #include +#include +#include #include "com_wolfssl_WolfCryptRSA.h" @@ -214,6 +216,189 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doEnc return ret; } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doPssSign + (JNIEnv* jenv, jobject jcl, jobject in, jlong inSz, jobject out, jintArray outSz, jint hash, jint mgf, jobject keyDer, jlong keySz) +{ +#ifdef WC_RSA_PSS + int ret; + WC_RNG rng; + RsaKey myKey; + int rngInit = 0; + int keyInit = 0; + unsigned int idx = 0; + unsigned int tmpOut; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + unsigned char* keyBuf = NULL; + enum wc_HashType hashType; + (void)jcl; + + if ((inSz < 0) || (keySz < 0)) { + return -1; + } + + inBuf = (*jenv)->GetDirectBufferAddress(jenv, in); + if (inBuf == NULL) { + printf("problem getting in buffer address\n"); + return -1; + } + + outBuf = (*jenv)->GetDirectBufferAddress(jenv, out); + if (outBuf == NULL) { + printf("problem getting out buffer address\n"); + return -1; + } + + keyBuf = (*jenv)->GetDirectBufferAddress(jenv, keyDer); + if (keyBuf == NULL) { + printf("problem getting key buffer address\n"); + return -1; + } + + hashType = wc_OidGetHash(hash); + if (hashType == WC_HASH_TYPE_NONE) { + printf("doPssSign: unsupported hash OID %d\n", hash); + return -1; + } + + /* get output buffer size */ + (*jenv)->GetIntArrayRegion(jenv, outSz, 0, 1, (jint*)&tmpOut); + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed, ret = %d\n", ret); + return ret; + } + rngInit = 1; + + ret = wc_InitRsaKey(&myKey, NULL); + if (ret != 0) { + printf("wc_InitRsaKey failed, ret = %d\n", ret); + wc_FreeRng(&rng); + return ret; + } + keyInit = 1; + + ret = wc_RsaPrivateKeyDecode(keyBuf, &idx, &myKey, (unsigned int)keySz); + if (ret == 0) { + + ret = wc_RsaPSS_Sign(inBuf, (unsigned int)inSz, outBuf, tmpOut, + hashType, mgf, &myKey, &rng); + if (ret > 0) { + tmpOut = ret; + (*jenv)->SetIntArrayRegion(jenv, outSz, 0, 1, (jint*)&tmpOut); + ret = 0; + } + } else { + printf("wc_RsaPrivateKeyDecode failed, ret = %d\n", ret); + } + + if (keyInit) { + wc_FreeRsaKey(&myKey); + } + if (rngInit) { + wc_FreeRng(&rng); + } + + return ret; +#else + (void)jenv; + (void)jcl; + (void)in; + (void)inSz; + (void)out; + (void)outSz; + (void)hash; + (void)mgf; + (void)keyDer; + (void)keySz; + return (jint)NOT_COMPILED_IN; +#endif /* WC_RSA_PSS */ +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doPssVerify + (JNIEnv* jenv, jobject jcl, jobject sig, jlong sigSz, jobject out, jlong outSz, jint hash, jint mgf, jobject keyDer, jlong keySz) +{ +#ifdef WC_RSA_PSS + int ret; + RsaKey myKey; + unsigned int idx = 0; + unsigned char* sigBuf = NULL; + unsigned char* outBuf = NULL; + unsigned char* keyBuf = NULL; + enum wc_HashType hashType; + (void)jcl; + + if ((sigSz < 0) || (keySz < 0) || (outSz < 0)) { + return -1; + } + + sigBuf = (*jenv)->GetDirectBufferAddress(jenv, sig); + if (sigBuf == NULL) { + printf("problem getting sig buffer address\n"); + return -1; + } + + outBuf = (*jenv)->GetDirectBufferAddress(jenv, out); + if (outBuf == NULL) { + printf("problem getting out buffer address\n"); + return -1; + } + + keyBuf = (*jenv)->GetDirectBufferAddress(jenv, keyDer); + if (keyBuf == NULL) { + printf("problem getting key buffer address\n"); + return -1; + } + + hashType = wc_OidGetHash(hash); + if (hashType == WC_HASH_TYPE_NONE) { + printf("doPssVerify: unsupported hash OID %d\n", hash); + return -1; + } + + ret = wc_InitRsaKey(&myKey, NULL); + if (ret != 0) { + printf("wc_InitRsaKey failed, ret = %d\n", ret); + return ret; + } + + /* Try private key decode first (sign check receives the server private), + * fall back to public key decode (verify receives the peer public) */ + ret = wc_RsaPrivateKeyDecode(keyBuf, &idx, &myKey, (unsigned int)keySz); + if (ret != 0) { + idx = 0; + ret = wc_RsaPublicKeyDecode(keyBuf, &idx, &myKey, (unsigned int)keySz); + } + + if (ret == 0) { + ret = wc_RsaPSS_Verify(sigBuf, (unsigned int)sigSz, outBuf, + (unsigned int)outSz, hashType, mgf, &myKey); + if (ret < 0) { + printf("wc_RsaPSS_Verify failed, ret = %d\n", ret); + } + } else { + printf("RSA key decode failed, ret = %d\n", ret); + } + + wc_FreeRsaKey(&myKey); + + return ret; +#else + (void)jenv; + (void)jcl; + (void)sig; + (void)sigSz; + (void)out; + (void)outSz; + (void)hash; + (void)mgf; + (void)keyDer; + (void)keySz; + return (jint)NOT_COMPILED_IN; +#endif /* WC_RSA_PSS */ +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doDec (JNIEnv* jenv, jobject jcl, jobject in, jlong inSz, jobject out, jlong outSz, jobject keyDer, jlong keySz) diff --git a/native/com_wolfssl_WolfCryptRSA.h b/native/com_wolfssl_WolfCryptRSA.h index d9e413e..4690f6c 100644 --- a/native/com_wolfssl_WolfCryptRSA.h +++ b/native/com_wolfssl_WolfCryptRSA.h @@ -39,6 +39,22 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doEnc JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doDec (JNIEnv *, jobject, jobject, jlong, jobject, jlong, jobject, jlong); +/* + * Class: com_wolfssl_WolfCryptRSA + * Method: doPssSign + * Signature: (Ljava/nio/ByteBuffer;JLjava/nio/ByteBuffer;[IIILjava/nio/ByteBuffer;J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doPssSign + (JNIEnv *, jobject, jobject, jlong, jobject, jintArray, jint, jint, jobject, jlong); + +/* + * Class: com_wolfssl_WolfCryptRSA + * Method: doPssVerify + * Signature: (Ljava/nio/ByteBuffer;JLjava/nio/ByteBuffer;JIILjava/nio/ByteBuffer;J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfCryptRSA_doPssVerify + (JNIEnv *, jobject, jobject, jlong, jobject, jlong, jint, jint, jobject, jlong); + #ifdef __cplusplus } #endif diff --git a/native/com_wolfssl_WolfSSLContext.c b/native/com_wolfssl_WolfSSLContext.c index ab9bec2..b4ea500 100644 --- a/native/com_wolfssl_WolfSSLContext.c +++ b/native/com_wolfssl_WolfSSLContext.c @@ -70,9 +70,19 @@ int NativeEccSharedSecretCb(WOLFSSL* ssl, ecc_key* otherKey, int NativeRsaSignCb(WOLFSSL* ssl, const unsigned char* in, unsigned int inSz, unsigned char* out, unsigned int* outSz, const unsigned char* keyDer, unsigned int keySz, void* ctx); +int NativeRsaPssSignCb(WOLFSSL* ssl, + const unsigned char* in, unsigned int inSz, unsigned char* out, + unsigned int* outSz, int hash, int mgf, const unsigned char* keyDer, + unsigned int keySz, void* ctx); int NativeRsaVerifyCb(WOLFSSL* ssl, unsigned char* sig, unsigned int sigSz, unsigned char** out, const unsigned char* keyDer, unsigned int keySz, void* ctx); +int NativeRsaSignCheckCb(WOLFSSL* ssl, unsigned char* sig, unsigned int sigSz, + unsigned char** out, const unsigned char* keyDer, unsigned int keySz, + void* ctx); +int NativeRsaPssSignCheckCb(WOLFSSL* ssl, unsigned char* sig, + unsigned int sigSz, unsigned char** out, int hash, int mgf, + const unsigned char* keyDer, unsigned int keySz, void* ctx); int NativeRsaEncCb(WOLFSSL* ssl, const unsigned char* in, unsigned int inSz, unsigned char* out, unsigned int* outSz, const unsigned char* keyDer, unsigned int keySz, void* ctx); @@ -4152,6 +4162,324 @@ int NativeRsaSignCb(WOLFSSL* ssl, const unsigned char* in, unsigned int inSz, #endif /* HAVE_PK_CALLBACKS */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaPssSignCb + (JNIEnv* jenv, jobject jcl, jlong ctxPtr) +{ + WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr; + jclass excClass = NULL; + (void)jcl; + + /* find exception class */ + excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException"); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + return; + } + +#if defined(HAVE_PK_CALLBACKS) && defined(WC_RSA_PSS) && !defined(NO_RSA) + if (ctx != NULL) { + wolfSSL_CTX_SetRsaPssSignCb(ctx, NativeRsaPssSignCb); + + } else { + (*jenv)->ThrowNew(jenv, excClass, + "Input WolfSSLContext object was null when setting RsaPssSignCb"); + } +#else + (void)ctx; + (*jenv)->ThrowNew(jenv, excClass, + "wolfSSL not compiled with PK Callback support and/or RSA-PSS support"); +#endif +} + +#if defined(HAVE_PK_CALLBACKS) && defined(WC_RSA_PSS) && !defined(NO_RSA) + +int NativeRsaPssSignCb(WOLFSSL* ssl, const unsigned char* in, unsigned int inSz, + unsigned char* out, unsigned int* outSz, int hash, int mgf, + const unsigned char* keyDer, unsigned int keySz, void* ctx) +{ + jint retval = 0; + jint vmret = 0; + + JNIEnv* jenv; + jclass excClass; + int needsDetach = 0; + + static jobject* g_cachedSSLObj; + jclass sessClass; + jfieldID ctxFid; + jmethodID getCtxMethodId; + + jobject ctxRef; + jclass innerCtxClass; + jmethodID rsaPssSignMethodId; + jintArray j_outSz; + + jobject inBB = NULL; + jobject outBB = NULL; + jobject keyDerBB = NULL; + jint tmpVal = 0; + + (void)ctx; + + if (!g_vm || !ssl || !in || !out || !outSz || !keyDer) { + return -1; + } + + /* 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 jobject */ + g_cachedSSLObj = (jobject*) wolfSSL_get_jobject((WOLFSSL*)ssl); + if (!g_cachedSSLObj) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLSession " + "object reference in NativeRsaPssSignCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* lookup WolfSSLSession class from object */ + sessClass = (*jenv)->GetObjectClass(jenv, (jobject)(*g_cachedSSLObj)); + if (!sessClass) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLSession " + "class reference in NativeRsaPssSignCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* lookup WolfSSLContext private member fieldID */ + ctxFid = (*jenv)->GetFieldID(jenv, sessClass, "ctx", + "Lcom/wolfssl/WolfSSLContext;"); + if (!ctxFid) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, + "Can't get native WolfSSLContext field ID in NativeRsaPssSignCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* find getContextPtr() method */ + getCtxMethodId = (*jenv)->GetMethodID(jenv, sessClass, + "getAssociatedContextPtr", "()Lcom/wolfssl/WolfSSLContext;"); + if (!getCtxMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, "Can't get getAssociatedContextPtr() " + "method ID in NativeRsaPssSignCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* get WolfSSLContext ctx object from Java land */ + ctxRef = (*jenv)->CallObjectMethod(jenv, (jobject)(*g_cachedSSLObj), + getCtxMethodId); + CheckException(jenv); + if (!ctxRef) { + (*jenv)->ThrowNew(jenv, excClass, + "Can't get WolfSSLContext object in NativeRsaPssSignCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* get WolfSSLContext class reference from Java land */ + innerCtxClass = (*jenv)->GetObjectClass(jenv, ctxRef); + if (!innerCtxClass) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLContext " + "class reference in NativeRsaPssSignCb"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* call internal RSA-PSS sign callback */ + rsaPssSignMethodId = (*jenv)->GetMethodID(jenv, innerCtxClass, + "internalRsaPssSignCallback", "(Lcom/wolfssl/WolfSSLSession;" + "Ljava/nio/ByteBuffer;J" "Ljava/nio/ByteBuffer;[III" + "Ljava/nio/ByteBuffer;J)I"); + + if (!rsaPssSignMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, + "Error getting internalRsaPssSignCallback method from JNI"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'in' */ + inBB = (*jenv)->NewDirectByteBuffer(jenv, (void*)in, inSz); + if (!inBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaPssSign in ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'out' */ + outBB = (*jenv)->NewDirectByteBuffer(jenv, (void*)out, *outSz); + if (!outBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaPssSign out ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'keyDer' */ + keyDerBB = (*jenv)->NewDirectByteBuffer(jenv, (void*)keyDer, keySz); + if (!keyDerBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaPssSign keyDer ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create jintArray to hold outSz, used as an output parameter from Java, + * only needs to have 1 element */ + j_outSz = (*jenv)->NewIntArray(jenv, 1); + if (!j_outSz) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create result intArray in RsaPssSignCb"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* copy outSz into j_outSz */ + (*jenv)->SetIntArrayRegion(jenv, j_outSz, 0, 1, (jint*)outSz); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->ThrowNew(jenv, excClass, + "Failed to set j_outSz intArray in RsaPssSignCb"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + (*jenv)->DeleteLocalRef(jenv, j_outSz); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* call Java callback, java layer handles adding CTX ref */ + retval = (*jenv)->CallIntMethod(jenv, ctxRef, rsaPssSignMethodId, + (jobject)(*g_cachedSSLObj), inBB, (jlong)inSz, outBB, j_outSz, hash, + mgf, keyDerBB, (jlong)keySz); + + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + (*jenv)->DeleteLocalRef(jenv, j_outSz); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + if (retval == 0) { + /* copy j_outSz into outSz */ + (*jenv)->GetIntArrayRegion(jenv, j_outSz, 0, 1, &tmpVal); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + (*jenv)->DeleteLocalRef(jenv, j_outSz); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + *outSz = tmpVal; + } + + /* delete local refs */ + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, inBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + (*jenv)->DeleteLocalRef(jenv, j_outSz); + + /* detach JNIEnv from thread */ + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + + return retval; +} + +#endif /* HAVE_PK_CALLBACKS && WC_RSA_PSS && !NO_RSA */ + JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaVerifyCb (JNIEnv* jenv, jobject jcl, jlong ctxPtr) { @@ -4399,13 +4727,533 @@ int NativeRsaVerifyCb(WOLFSSL* ssl, unsigned char* sig, unsigned int sigSz, (*jenv)->DeleteLocalRef(jenv, keyDerBB); /* detach JNIEnv from thread */ - (*g_vm)->DetachCurrentThread(g_vm); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } return retval; } #endif /* HAVE_PK_CALLBACKS */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaSignCheckCb + (JNIEnv* jenv, jobject jcl, jlong ctxPtr) +{ + WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr; + jclass excClass = NULL; + (void)jcl; + + /* find exception class */ + excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException"); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + return; + } + +#if defined(HAVE_PK_CALLBACKS) && !defined(NO_RSA) + if (ctx != NULL) { + /* set RSA sign check callback */ + wolfSSL_CTX_SetRsaSignCheckCb(ctx, NativeRsaSignCheckCb); + } else { + (*jenv)->ThrowNew(jenv, excClass, + "Input WolfSSLContext object was null when setting RsaSignCheckCb"); + } +#else + (void)ctx; + (*jenv)->ThrowNew(jenv, excClass, + "wolfSSL not compiled with PK Callback and/or RSA support"); +#endif +} + +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaPssSignCheckCb + (JNIEnv* jenv, jobject jcl, jlong ctxPtr) +{ + WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr; + jclass excClass = NULL; + (void)jcl; + + /* find exception class */ + excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLJNIException"); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + return; + } + +#if defined(HAVE_PK_CALLBACKS) && defined(WC_RSA_PSS) && !defined(NO_RSA) + if (ctx != NULL) { + /* set RSA-PSS sign check callback */ + wolfSSL_CTX_SetRsaPssSignCheckCb(ctx, NativeRsaPssSignCheckCb); + } else { + (*jenv)->ThrowNew(jenv, excClass, "Input WolfSSLContext object was " + "null when setting RsaPssSignCheckCb"); + } +#else + (void)ctx; + (*jenv)->ThrowNew(jenv, excClass, + "wolfSSL not compiled with PK Callback and/or RSA-PSS support"); +#endif +} + +#if defined(HAVE_PK_CALLBACKS) && !defined(NO_RSA) + +int NativeRsaSignCheckCb(WOLFSSL* ssl, unsigned char* sig, unsigned int sigSz, + unsigned char** out, const unsigned char* keyDer, unsigned int keySz, + void* ctx) +{ + jint retval = 0; + jint vmret = 0; + + JNIEnv* jenv; + jclass excClass; + int needsDetach = 0; + + static jobject* g_cachedSSLObj; + jclass sessClass; + jfieldID ctxFid; + jmethodID getCtxMethodId; + + jobject ctxRef; + jclass innerCtxClass; + jmethodID rsaSignCheckMethodId; + + jobject sigBB = NULL; + jobject outBB = NULL; + jobject keyDerBB = NULL; + + (void)ctx; + + if (!g_vm || !ssl || !sig || !out || !keyDer) { + return -1; + } + + /* 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 jobject */ + g_cachedSSLObj = (jobject*) wolfSSL_get_jobject((WOLFSSL*)ssl); + if (!g_cachedSSLObj) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLSession " + "object reference in NativeRsaSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* lookup WolfSSLSession class from object */ + sessClass = (*jenv)->GetObjectClass(jenv, (jobject)(*g_cachedSSLObj)); + if (!sessClass) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLSession " + "class reference in NativeRsaSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* lookup WolfSSLContext private member fieldID */ + ctxFid = (*jenv)->GetFieldID(jenv, sessClass, "ctx", + "Lcom/wolfssl/WolfSSLContext;"); + if (!ctxFid) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLContext " + "field ID in NativeRsaSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* find getContextPtr() method */ + getCtxMethodId = (*jenv)->GetMethodID(jenv, sessClass, + "getAssociatedContextPtr", "()Lcom/wolfssl/WolfSSLContext;"); + if (!getCtxMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, "Can't get getAssociatedContextPtr() " + "method ID in NativeRsaSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* get WolfSSLContext ctx object from Java land */ + ctxRef = (*jenv)->CallObjectMethod(jenv, (jobject)(*g_cachedSSLObj), + getCtxMethodId); + CheckException(jenv); + if (!ctxRef) { + (*jenv)->ThrowNew(jenv, excClass, + "Can't get WolfSSLContext object in NativeRsaSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* get WolfSSLContext class reference */ + innerCtxClass = (*jenv)->GetObjectClass(jenv, ctxRef); + if (!innerCtxClass) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLContext " + "class reference in NativeRsaSignCheckCb"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* call internal RSA sign check callback */ + rsaSignCheckMethodId = (*jenv)->GetMethodID(jenv, innerCtxClass, + "internalRsaSignCheckCallback", "(Lcom/wolfssl/WolfSSLSession;" + "Ljava/nio/ByteBuffer;JLjava/nio/ByteBuffer;J" + "Ljava/nio/ByteBuffer;J)I"); + + if (!rsaSignCheckMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, + "Error getting internalRsaSignCheckCallback method from JNI"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'sig' */ + sigBB = (*jenv)->NewDirectByteBuffer(jenv, sig, sigSz); + if (!sigBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaSignCheck sig ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'out', since doing this inline, outBB points + * to the same address as sigBB */ + outBB = (*jenv)->NewDirectByteBuffer(jenv, sig, sigSz); + if (!outBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaSignCheck out ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, sigBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'keyDer' */ + keyDerBB = (*jenv)->NewDirectByteBuffer(jenv, (void*)keyDer, keySz); + if (!keyDerBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaSignCheck keyDer ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, sigBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* call Java callback, java layer handles adding CTX ref */ + retval = (*jenv)->CallIntMethod(jenv, ctxRef, rsaSignCheckMethodId, + (jobject)(*g_cachedSSLObj), sigBB, (jlong)sigSz, outBB, (jlong)sigSz, + keyDerBB, (jlong)keySz); + + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + + /* point out* to the beginning of decrypted buffer */ + if (retval > 0) { + *out = sig; + } + + /* delete local refs */ + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, sigBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + + /* detach JNIEnv from thread */ + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + + return retval; +} + +#endif /* HAVE_PK_CALLBACKS && !NO_RSA */ + +#if defined(HAVE_PK_CALLBACKS) && defined(WC_RSA_PSS) && !defined(NO_RSA) + +int NativeRsaPssSignCheckCb(WOLFSSL* ssl, unsigned char* sig, + unsigned int sigSz, unsigned char** out, int hash, int mgf, + const unsigned char* keyDer, unsigned int keySz, void* ctx) +{ + jint retval = 0; + jint vmret = 0; + + JNIEnv* jenv; + jclass excClass; + int needsDetach = 0; + + static jobject* g_cachedSSLObj; + jclass sessClass; + jfieldID ctxFid; + jmethodID getCtxMethodId; + + jobject ctxRef; + jclass innerCtxClass; + jmethodID rsaPssSignCheckMethodId; + + jobject sigBB = NULL; + jobject outBB = NULL; + jobject keyDerBB = NULL; + + (void)ctx; + + if (!g_vm || !ssl || !sig || !out || !keyDer) { + return -1; + } + + /* 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 jobject */ + g_cachedSSLObj = (jobject*) wolfSSL_get_jobject((WOLFSSL*)ssl); + if (!g_cachedSSLObj) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLSession " + "object reference in NativeRsaPssSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* lookup WolfSSLSession class from object */ + sessClass = (*jenv)->GetObjectClass(jenv, (jobject)(*g_cachedSSLObj)); + if (!sessClass) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLSession " + "class reference in NativeRsaPssSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* lookup WolfSSLContext private member fieldID */ + ctxFid = (*jenv)->GetFieldID(jenv, sessClass, "ctx", + "Lcom/wolfssl/WolfSSLContext;"); + if (!ctxFid) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLContext " + "field ID in NativeRsaPssSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* find getContextPtr() method */ + getCtxMethodId = (*jenv)->GetMethodID(jenv, sessClass, + "getAssociatedContextPtr", "()Lcom/wolfssl/WolfSSLContext;"); + if (!getCtxMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, "Can't get getAssociatedContextPtr() " + "method ID in NativeRsaPssSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* get WolfSSLContext ctx object from Java land */ + ctxRef = (*jenv)->CallObjectMethod(jenv, (jobject)(*g_cachedSSLObj), + getCtxMethodId); + CheckException(jenv); + if (!ctxRef) { + (*jenv)->ThrowNew(jenv, excClass, + "Can't get WolfSSLContext object in NativeRsaPssSignCheckCb"); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* get WolfSSLContext class reference */ + innerCtxClass = (*jenv)->GetObjectClass(jenv, ctxRef); + if (!innerCtxClass) { + (*jenv)->ThrowNew(jenv, excClass, "Can't get native WolfSSLContext " + "class reference in NativeRsaPssSignCheckCb"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* call internal RSA-PSS sign check callback */ + rsaPssSignCheckMethodId = (*jenv)->GetMethodID(jenv, + innerCtxClass, "internalRsaPssSignCheckCallback", + "(Lcom/wolfssl/WolfSSLSession;Ljava/nio/ByteBuffer;J" + "Ljava/nio/ByteBuffer;JIILjava/nio/ByteBuffer;J)I"); + + if (!rsaPssSignCheckMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, + "Error getting internalRsaPssSignCheckCallback method from JNI"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'sig' */ + sigBB = (*jenv)->NewDirectByteBuffer(jenv, sig, sigSz); + if (!sigBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaPssSignCheck sig ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'out', since doing inline, outBB points to + * the same address as sigBB */ + outBB = (*jenv)->NewDirectByteBuffer(jenv, sig, sigSz); + if (!outBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaPssSignCheck out ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, sigBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* create ByteBuffer to wrap 'keyDer' */ + keyDerBB = (*jenv)->NewDirectByteBuffer(jenv, (void*)keyDer, keySz); + if (!keyDerBB) { + (*jenv)->ThrowNew(jenv, excClass, + "Failed to create rsaPssSignCheck keyDer ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, sigBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + return -1; + } + + /* call Java callback, java layer handles adding CTX ref */ + retval = (*jenv)->CallIntMethod(jenv, ctxRef, rsaPssSignCheckMethodId, + (jobject)(*g_cachedSSLObj), sigBB, (jlong)sigSz, outBB, (jlong)sigSz, + hash, mgf, keyDerBB, (jlong)keySz); + + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + + /* point out* to the beginning of decrypted buffer */ + if (retval > 0) { + *out = sig; + } + + /* delete local refs */ + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, sigBB); + (*jenv)->DeleteLocalRef(jenv, outBB); + (*jenv)->DeleteLocalRef(jenv, keyDerBB); + + /* detach JNIEnv from thread */ + if (needsDetach) { + (*g_vm)->DetachCurrentThread(g_vm); + } + + return retval; +} + +#endif /* HAVE_PK_CALLBACKS && WC_RSA_PSS && !NO_RSA */ + JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaEncCb (JNIEnv* jenv, jobject jcl, jlong ctxPtr) { diff --git a/native/com_wolfssl_WolfSSLContext.h b/native/com_wolfssl_WolfSSLContext.h index 9398350..0524943 100644 --- a/native/com_wolfssl_WolfSSLContext.h +++ b/native/com_wolfssl_WolfSSLContext.h @@ -327,6 +327,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setEccSharedSecretCb JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaSignCb (JNIEnv *, jobject, jlong); +/* + * Class: com_wolfssl_WolfSSLContext + * Method: setRsaPssSignCb + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaPssSignCb + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLContext * Method: setRsaVerifyCb @@ -335,6 +343,22 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaSignCb JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaVerifyCb (JNIEnv *, jobject, jlong); +/* + * Class: com_wolfssl_WolfSSLContext + * Method: setRsaSignCheckCb + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaSignCheckCb + (JNIEnv *, jobject, jlong); + +/* + * Class: com_wolfssl_WolfSSLContext + * Method: setRsaPssSignCheckCb + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setRsaPssSignCheckCb + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLContext * Method: setRsaEncCb diff --git a/scripts/infer.sh b/scripts/infer.sh index 6c6c92d..5523506 100755 --- a/scripts/infer.sh +++ b/scripts/infer.sh @@ -64,6 +64,8 @@ infer --fail-on-issue run -- javac \ src/java/com/wolfssl/WolfSSLPskServerCallback.java \ src/java/com/wolfssl/WolfSSLRsaDecCallback.java \ src/java/com/wolfssl/WolfSSLRsaEncCallback.java \ + src/java/com/wolfssl/WolfSSLRsaPssSignCallback.java \ + src/java/com/wolfssl/WolfSSLRsaPssVerifyCallback.java \ src/java/com/wolfssl/WolfSSLRsaSignCallback.java \ src/java/com/wolfssl/WolfSSLRsaVerifyCallback.java \ src/java/com/wolfssl/WolfSSLSession.java \ diff --git a/src/java/com/wolfssl/WolfCryptRSA.java b/src/java/com/wolfssl/WolfCryptRSA.java index a9047d4..0c334ae 100644 --- a/src/java/com/wolfssl/WolfCryptRSA.java +++ b/src/java/com/wolfssl/WolfCryptRSA.java @@ -102,5 +102,42 @@ public class WolfCryptRSA { */ public native int doDec(ByteBuffer in, long inSz, ByteBuffer out, long outSz, ByteBuffer keyDer, long keySz); + + /** + * RSA-PSS sign, wraps native wolfCrypt operation. + * + * @param in input buffer to be signed + * @param inSz size of input buffer, bytes + * @param out output for generated signature + * @param outSz [IN/OUT] size of output buffer on input, size of + * generated signature on output + * @param hash hash algorithm type + * @param mgf mask generation function identifier + * @param key DER formatted RSA key to be used for signing + * @param keySz size of key, bytes + * + * @return 0 on success, negative on error. + */ + public native int doPssSign(ByteBuffer in, long inSz, + ByteBuffer out, int[] outSz, int hash, int mgf, + ByteBuffer key, long keySz); + + /** + * RSA-PSS verify, wraps native wolfCrypt operation. + * + * @param sig input signature to verify + * @param sigSz size of input signature, bytes + * @param out output buffer to place verified data + * @param outSz size of output buffer, bytes + * @param hash hash algorithm type + * @param mgf mask generation function identifier + * @param keyDer public key used for verify, DER formatted + * @param keySz size of public key, bytes + * + * @return size of returned data on success, negative on error. + */ + public native int doPssVerify(ByteBuffer sig, long sigSz, + ByteBuffer out, long outSz, int hash, int mgf, + ByteBuffer keyDer, long keySz); } diff --git a/src/java/com/wolfssl/WolfSSLContext.java b/src/java/com/wolfssl/WolfSSLContext.java index 2b226a0..3e1c903 100644 --- a/src/java/com/wolfssl/WolfSSLContext.java +++ b/src/java/com/wolfssl/WolfSSLContext.java @@ -59,6 +59,11 @@ public class WolfSSLContext { /* user-registered RSA sign/verify callbacks */ private WolfSSLRsaSignCallback internRsaSignCb = null; private WolfSSLRsaVerifyCallback internRsaVerifyCb = null; + private WolfSSLRsaVerifyCallback internRsaSignCheckCb = null; + + /* user-registered RSA-PSS sign/verify callbacks */ + private WolfSSLRsaPssSignCallback internRsaPssSignCb = null; + private WolfSSLRsaPssVerifyCallback internRsaPssSignCheckCb = null; /* user-registered RSA enc/dec callbacks */ private WolfSSLRsaEncCallback internRsaEncCb = null; @@ -280,14 +285,53 @@ public class WolfSSLContext { } private int internalRsaVerifyCallback(WolfSSLSession ssl, ByteBuffer sig, - long sigSz, ByteBuffer out, long outSz, ByteBuffer keyDer, - long keySz) + long sigSz, ByteBuffer out, long outSz, ByteBuffer keyDer, + long keySz) { int ret; /* call user-registered rsa verify method */ - ret = internRsaVerifyCb.rsaVerifyCallback(ssl, sig, sigSz, out, - outSz, keyDer, keySz, ssl.getRsaVerifyCtx()); + ret = internRsaVerifyCb.rsaVerifyCallback(ssl, sig, sigSz, out, outSz, + keyDer, keySz, ssl.getRsaVerifyCtx()); + + return ret; + } + + private int internalRsaSignCheckCallback(WolfSSLSession ssl, ByteBuffer sig, + long sigSz, ByteBuffer out, long outSz, ByteBuffer keyDer, + long keySz) + { + int ret; + + /* call user-registered rsa sign check method */ + ret = internRsaSignCheckCb.rsaVerifyCallback(ssl, sig, sigSz, out, + outSz, keyDer, keySz, ssl.getRsaVerifyCtx()); + + return ret; + } + + private int internalRsaPssSignCallback(WolfSSLSession ssl, ByteBuffer in, + long inSz, ByteBuffer out, int[] outSz, int hash, int mgf, + ByteBuffer keyDer, long keySz) + { + int ret; + + /* call user-registered rsa pss sign method */ + ret = internRsaPssSignCb.rsaPssSignCallback(ssl, in, inSz, out, outSz, + hash, mgf, keyDer, keySz, ssl.getRsaSignCtx()); + + return ret; + } + + private int internalRsaPssSignCheckCallback(WolfSSLSession ssl, + ByteBuffer sig, long sigSz, ByteBuffer out, long outSz, int hash, + int mgf, ByteBuffer keyDer, long keySz) + { + int ret; + + /* call user-registered rsa pss verify method */ + ret = internRsaPssSignCheckCb.rsaPssVerifyCallback(ssl, sig, sigSz, + out, outSz, hash, mgf, keyDer, keySz, ssl.getRsaVerifyCtx()); return ret; } @@ -406,7 +450,10 @@ public class WolfSSLContext { private native void setEccVerifyCb(long ctx); private native void setEccSharedSecretCb(long ctx); private native void setRsaSignCb(long ctx); + private native void setRsaPssSignCb(long ctx); private native void setRsaVerifyCb(long ctx); + private native void setRsaSignCheckCb(long ctx); + private native void setRsaPssSignCheckCb(long ctx); private native void setRsaEncCb(long ctx); private native void setRsaDecCb(long ctx); private native void setPskClientCb(long ctx); @@ -1933,23 +1980,128 @@ public class WolfSSLContext { } /** - * Allows caller to set the Public Key Callback for RSA Public Encrypt. - * The callback should return 0 for success or negative value for an - * error. The ssl and ctx objects are available for - * the users convenience. in is the input buffer to encrypt while - * inSz denotes the length of the input. out is the output - * buffer where the result of the encryption should be stored. outSz - * is an input/output variable that specifies the size of the output - * buffer upon invocation and the actual size of the encryption should be - * stored there before returning. keyDer is the RSA Public key in - * ASN1 format and keySz is the length of the key in bytes. An - * example callback can be found in examples/MyRsaEncCallback.java. + * Allows caller to set the Public Key Callback for RSA Sign Check (verify + * a signature that was just created). Uses the same callback signature as + * RSA Verify. The callback should return the number of plaintext bytes + * for success or a negative value for an error. * - * @param callback object to be registered as the RSA public encrypt - * callback for the WolfSSL context. The signature of - * this object and corresponding method must match that - * as shown in WolfSSLRsaEncCallback.java, inside - * rsaEncCallback(). + * @param callback object to be registered as the RSA sign check callback + * for the WolfSSL context. The signature of this object + * and corresponding method must match that as shown in + * WolfSSLRsaVerifyCallback.java, inside + * rsaVerifyCallback(). + * @throws IllegalStateException WolfSSLContext has been freed + * @throws WolfSSLJNIException Internal JNI error + * @see WolfSSLSession#setRsaVerifyCtx(Object) + */ + public synchronized void setRsaSignCheckCb( + WolfSSLRsaVerifyCallback callback) throws IllegalStateException, + WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (ctxLock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, getContextPtr(), + () -> "entered setRsaSignCheckCb(" + callback + ")"); + + /* set rsa sign check callback */ + internRsaSignCheckCb = callback; + + /* register internal callback with native lib */ + setRsaSignCheckCb(getContextPtr()); + } + } + + /** + * Allows caller to set the Public Key Callback for RSA-PSS Signing. The + * callback should return 0 for success or a negative value for an error. + * in is the input buffer to sign and inSz denotes the length + * of the input. out is the output buffer where the result of the + * signature should be stored. outSz is an input/output variable that + * specifies the size of the output buffer upon invocation. hash and + * mgf specify the hash algorithm and mask generation function. + * keyDer is the RSA Private key in ASN1 format and keySz is + * the length of the key in bytes. + * + * @param callback object to be registered as the RSA-PSS sign callback for + * the WolfSSL context. The signature of this object and + * corresponding method must match that as shown in + * WolfSSLRsaPssSignCallback.java. + * @throws IllegalStateException WolfSSLContext has been freed + * @throws WolfSSLJNIException Internal JNI error + * @see WolfSSLSession#setRsaSignCtx(Object) + */ + public synchronized void setRsaPssSignCb(WolfSSLRsaPssSignCallback callback) + throws IllegalStateException, WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (ctxLock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, getContextPtr(), + () -> "entered setRsaPssSignCb(" + callback + ")"); + + /* set rsa pss sign callback */ + internRsaPssSignCb = callback; + + /* register internal callback with native lib */ + setRsaPssSignCb(getContextPtr()); + } + } + + /** + * Allows caller to set the Public Key Callback for RSA-PSS Sign Check + * (verify a PSS signature that was just created). The callback should + * return the number of plaintext bytes for success or a negative value + * for an error. hash and mgf specify the hash algorithm and + * mask generation function. + * + * @param callback object to be registered as the RSA-PSS sign check + * callback for the WolfSSL context. The signature of + * this object and corresponding method must match that as + * shown in WolfSSLRsaPssVerifyCallback.java. + * @throws IllegalStateException WolfSSLContext has been freed + * @throws WolfSSLJNIException Internal JNI error + * @see WolfSSLSession#setRsaVerifyCtx(Object) + */ + public synchronized void setRsaPssSignCheckCb( + WolfSSLRsaPssVerifyCallback callback) throws IllegalStateException, + WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (ctxLock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, getContextPtr(), + () -> "entered setRsaPssSignCheckCb(" + callback + ")"); + + /* set rsa pss sign check callback */ + internRsaPssSignCheckCb = callback; + + /* register internal callback with native lib */ + setRsaPssSignCheckCb(getContextPtr()); + } + } + + /** + * Allows caller to set the Public Key Callback for RSA Public Encrypt. + * The callback should return 0 for success or negative value for an error. + * The ssl and ctx objects are available for the users + * convenience. in is the input buffer to encrypt while inSz + * denotes the length of the input. out is the output buffer where + * the result of the encryption should be stored. outSz is an + * input/output variable that specifies the size of the output buffer upon + * invocation and the actual size of the encryption should be stored there + * before returning. keyDer is the RSA Public key in ASN1 format + * and keySz is the length of the key in bytes. An example callback + * can be found in examples/MyRsaEncCallback.java. + * + * @param callback object to be registered as the RSA public encrypt + * callback for the WolfSSL context. The signature of + * this object and corresponding method must match that as + * shown in WolfSSLRsaEncCallback.java, inside + * rsaEncCallback(). * @throws IllegalStateException WolfSSLContext has been freed * @throws WolfSSLJNIException Internal JNI exception * @see WolfSSLSession#setRsaEncCtx(Object) diff --git a/src/java/com/wolfssl/WolfSSLRsaPssSignCallback.java b/src/java/com/wolfssl/WolfSSLRsaPssSignCallback.java new file mode 100644 index 0000000..1fbfb18 --- /dev/null +++ b/src/java/com/wolfssl/WolfSSLRsaPssSignCallback.java @@ -0,0 +1,66 @@ +/* WolfSSLRsaPssSignCallback.java + * + * Copyright (C) 2006-2026 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 RSA-PSS Signing Callback Interface. + * This interface specifies how applications should implement the RSA-PSS + * signing callback class to be used by wolfSSL. + *

+ * After implementing this interface, it should be passed as a parameter to the + * {@link WolfSSLContext#setRsaPssSignCb(WolfSSLRsaPssSignCallback) + * WolfSSLContext.setRsaPssSignCb()} method to be registered with the native + * wolfSSL library. + * + * @author wolfSSL + */ +public interface WolfSSLRsaPssSignCallback { + + /** + * RSA-PSS signing callback method. + * This method acts as RSA-PSS signing callback. + * + * @param ssl the current SSL session object from which the callback was + * initiated. + * @param in input buffer to sign + * @param inSz length of the input, in + * @param out output buffer where the result of the signature should be + * stored. + * @param outSz input/output variable that specifies the size of the output + * buffer upon invocation. The actual size of the signature + * should be stored there before returning. Use the first + * element of the array for storage. + * @param hash hash algorithm type + * @param mgf mask generation function + * @param keyDer RSA Private key in ASN1 format + * @param keySz length of the key, keyDer, in bytes + * @param ctx custom user-registered RSA-PSS signing context + * @return 0 upon success, otherwise a negative + * value on error. + */ + public int rsaPssSignCallback(WolfSSLSession ssl, ByteBuffer in, long inSz, + ByteBuffer out, int[] outSz, int hash, int mgf, ByteBuffer keyDer, + long keySz, Object ctx); +} + diff --git a/src/java/com/wolfssl/WolfSSLRsaPssVerifyCallback.java b/src/java/com/wolfssl/WolfSSLRsaPssVerifyCallback.java new file mode 100644 index 0000000..a8fa2a7 --- /dev/null +++ b/src/java/com/wolfssl/WolfSSLRsaPssVerifyCallback.java @@ -0,0 +1,63 @@ +/* WolfSSLRsaPssVerifyCallback.java + * + * Copyright (C) 2006-2026 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 RSA-PSS Verification Callback Interface. + * This interface specifies how applications should implement the RSA-PSS + * verification callback class to be used by wolfSSL. + *

+ * After implementing this interface, it should be passed as a parameter to the + * {@link WolfSSLContext#setRsaPssSignCheckCb(WolfSSLRsaPssVerifyCallback) + * WolfSSLContext.setRsaPssSignCheckCb()} method to be registered with the + * native wolfSSL library. + * + * @author wolfSSL + */ +public interface WolfSSLRsaPssVerifyCallback { + + /** + * RSA-PSS verification callback method. + * This method acts as RSA-PSS verification callback. + * + * @param ssl the current SSL session object from which the callback + * was initiated. + * @param sig the signature to verify + * @param sigSz length of the signature, sig + * @param out the verification/output buffer after the decryption + * process and padding. + * @param outSz size of the output buffer, out + * @param hash hash algorithm type + * @param mgf mask generation function + * @param keyDer the RSA Public key in ASN1 format + * @param keySz the length of the key, keyDer, in bytes + * @param ctx custom user-registered RSA-PSS verify context + * @return the number of plaintext bytes on success, otherwise a + * negative value on error. + */ + public int rsaPssVerifyCallback(WolfSSLSession ssl, ByteBuffer sig, + long sigSz, ByteBuffer out, long outSz, int hash, int mgf, + ByteBuffer keyDer, long keySz, Object ctx); +} + diff --git a/src/test/com/wolfssl/test/WolfSSLContextTest.java b/src/test/com/wolfssl/test/WolfSSLContextTest.java index b8ee456..6f7316b 100644 --- a/src/test/com/wolfssl/test/WolfSSLContextTest.java +++ b/src/test/com/wolfssl/test/WolfSSLContextTest.java @@ -25,6 +25,18 @@ import org.junit.Test; import org.junit.BeforeClass; import static org.junit.Assert.*; +import java.io.IOException; +import java.net.Socket; +import java.net.ServerSocket; +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLContext; import com.wolfssl.WolfSSLException; @@ -32,6 +44,11 @@ import com.wolfssl.WolfSSLJNIException; import com.wolfssl.WolfSSLPskClientCallback; import com.wolfssl.WolfSSLPskServerCallback; import com.wolfssl.WolfSSLSession; +import com.wolfssl.WolfSSLRsaSignCallback; +import com.wolfssl.WolfSSLRsaVerifyCallback; +import com.wolfssl.WolfSSLRsaPssSignCallback; +import com.wolfssl.WolfSSLRsaPssVerifyCallback; +import com.wolfssl.WolfCryptRSA; public class WolfSSLContextTest { @@ -40,6 +57,8 @@ public class WolfSSLContextTest { public static String cliCert = "examples/certs/client-cert.pem"; public static String cliKey = "examples/certs/client-key.pem"; + public static String svrCert = "examples/certs/server-cert.pem"; + public static String svrKey = "examples/certs/server-key.pem"; public static String svrCertEcc = "examples/certs/server-ecc.pem"; public static String caCert = "examples/certs/ca-cert.pem"; public static String dhParams = "examples/certs/dh2048.pem"; @@ -63,6 +82,8 @@ public class WolfSSLContextTest { cliCert = WolfSSLTestCommon.getPath(cliCert); cliKey = WolfSSLTestCommon.getPath(cliKey); + svrCert = WolfSSLTestCommon.getPath(svrCert); + svrKey = WolfSSLTestCommon.getPath(svrKey); svrCertEcc = WolfSSLTestCommon.getPath(svrCertEcc); caCert = WolfSSLTestCommon.getPath(caCert); dhParams = WolfSSLTestCommon.getPath(dhParams); @@ -80,6 +101,7 @@ public class WolfSSLContextTest { test_WolfSSLContext_set1SigAlgsList(); test_WolfSSLContext_setMinRSAKeySize(); test_WolfSSLContext_setMinECCKeySize(); + test_WolfSSLContext_rsaCbHandshake(); test_WolfSSLContext_free(); } @@ -735,6 +757,442 @@ public class WolfSSLContextTest { System.out.println("\t\t... passed"); } + /* Context object shared between RSA sign/verify callbacks, tracks whether + * callback was invoked during handshake */ + class TestRsaCbCtx + { + public boolean called = false; + } + + class TestRsaSignCb implements WolfSSLRsaSignCallback + { + public int rsaSignCallback(WolfSSLSession ssl, ByteBuffer in, long inSz, + ByteBuffer out, int[] outSz, ByteBuffer keyDer, long keySz, + Object ctx) { + + TestRsaCbCtx myCtx = (TestRsaCbCtx)ctx; + myCtx.called = true; + + WolfCryptRSA rsa = new WolfCryptRSA(); + return rsa.doSign(in, inSz, out, outSz, keyDer, keySz); + } + } + + class TestRsaVerifyCb implements WolfSSLRsaVerifyCallback + { + public int rsaVerifyCallback(WolfSSLSession ssl, ByteBuffer sig, + long sigSz, ByteBuffer out, long outSz, ByteBuffer keyDer, + long keySz, Object ctx) { + + TestRsaCbCtx myCtx = (TestRsaCbCtx)ctx; + myCtx.called = true; + + WolfCryptRSA rsa = new WolfCryptRSA(); + return rsa.doVerify(sig, sigSz, out, outSz, keyDer, keySz); + } + } + + class TestRsaPssSignCb implements WolfSSLRsaPssSignCallback + { + public int rsaPssSignCallback(WolfSSLSession ssl, ByteBuffer in, + long inSz, ByteBuffer out, int[] outSz, int hash, int mgf, + ByteBuffer keyDer, long keySz, Object ctx) { + + TestRsaCbCtx myCtx = (TestRsaCbCtx)ctx; + myCtx.called = true; + + WolfCryptRSA rsa = new WolfCryptRSA(); + return rsa.doPssSign(in, inSz, out, outSz, hash, mgf, keyDer, + keySz); + } + } + + class TestRsaPssVerifyCb implements WolfSSLRsaPssVerifyCallback + { + public int rsaPssVerifyCallback(WolfSSLSession ssl, ByteBuffer sig, + long sigSz, ByteBuffer out, long outSz, int hash, int mgf, + ByteBuffer keyDer, long keySz, Object ctx) { + + TestRsaCbCtx myCtx = (TestRsaCbCtx)ctx; + myCtx.called = true; + + WolfCryptRSA rsa = new WolfCryptRSA(); + return rsa.doPssVerify(sig, sigSz, out, outSz, hash, mgf, keyDer, + keySz); + } + } + + /** + * Helper to create and configure a WolfSSLContext with cert, key, and CA + * for handshake tests. + */ + private WolfSSLContext createCtx(String certPath, String keyPath, + String caPath, long method) throws Exception { + + int ret; + WolfSSLContext c = new WolfSSLContext(method); + + ret = c.useCertificateChainFile(certPath); + if (ret != WolfSSL.SSL_SUCCESS) { + c.free(); + throw new Exception("Failed to load cert: " + certPath); + } + + ret = c.usePrivateKeyFile(keyPath, WolfSSL.SSL_FILETYPE_PEM); + if (ret != WolfSSL.SSL_SUCCESS) { + c.free(); + throw new Exception("Failed to load key: " + keyPath); + } + + ret = c.loadVerifyLocations(caPath, null); + if (ret != WolfSSL.SSL_SUCCESS) { + c.free(); + throw new Exception("Failed to load CA: " + caPath); + } + + return c; + } + + public void test_WolfSSLContext_rsaCbHandshake() { + + System.out.print("\trsaCbHandshake()"); + + if (!WolfSSL.RsaEnabled() || !WolfSSL.FileSystemEnabled()) { + System.out.println("\t\t... skipped"); + return; + } + + /* TLS 1.2 handshake with RSA PK callbacks */ + rsaCbHandshakeTls12(); + + /* TLS 1.3 handshake with RSA-PSS PK callbacks */ + if (WolfSSL.TLSv13Enabled() && WolfSSL.RsaPssEnabled()) { + rsaCbHandshakeTls13(); + } + + System.out.println("\t\t... passed"); + } + + private void rsaCbHandshakeTls12() { + + WolfSSLContext srvCtx = null; + WolfSSLContext cliCtx = null; + ServerSocket srvSocket = null; + ExecutorService es = null; + Socket cliSock = null; + WolfSSLSession cliSes = null; + + try { + srvCtx = createCtx(svrCert, svrKey, caCert, + WolfSSL.TLSv1_2_ServerMethod()); + cliCtx = createCtx(cliCert, cliKey, caCert, + WolfSSL.TLSv1_2_ClientMethod()); + + /* Register server-side RSA sign + sign check */ + TestRsaSignCb signCb = new TestRsaSignCb(); + TestRsaVerifyCb signCheckCb = new TestRsaVerifyCb(); + srvCtx.setRsaSignCb(signCb); + srvCtx.setRsaSignCheckCb(signCheckCb); + + /* Register client-side RSA verify */ + TestRsaVerifyCb verifyCb = new TestRsaVerifyCb(); + cliCtx.setRsaVerifyCb(verifyCb); + + /* Register RSA-PSS callbacks in case rsa_pss_sa_algo is used + * as sig algo in TLS 1.2 */ + if (WolfSSL.RsaPssEnabled()) { + TestRsaPssSignCb pssSignCb = new TestRsaPssSignCb(); + TestRsaPssVerifyCb pssSrvChk = new TestRsaPssVerifyCb(); + TestRsaPssVerifyCb pssCliChk = new TestRsaPssVerifyCb(); + srvCtx.setRsaPssSignCb(pssSignCb); + srvCtx.setRsaPssSignCheckCb(pssSrvChk); + cliCtx.setRsaPssSignCheckCb(pssCliChk); + } + + /* Context objects to track invocation */ + final TestRsaCbCtx srvSignCtx = new TestRsaCbCtx(); + final TestRsaCbCtx srvVerifyCtx = new TestRsaCbCtx(); + final TestRsaCbCtx cliVerifyCtx = new TestRsaCbCtx(); + + srvSocket = new ServerSocket(0); + srvSocket.setSoTimeout(10000); + final int port = srvSocket.getLocalPort(); + final ServerSocket fSrvSock = srvSocket; + final WolfSSLContext fSrvCtx = srvCtx; + + final CountDownLatch ready = new CountDownLatch(1); + + es = Executors.newSingleThreadExecutor(); + Future srvFuture = es.submit(new Callable() { + @Override + public Void call() throws Exception { + int ret; + int err; + Socket srv = null; + WolfSSLSession srvSes = null; + try { + ready.countDown(); + srv = fSrvSock.accept(); + srvSes = new WolfSSLSession(fSrvCtx); + srvSes.setRsaSignCtx(srvSignCtx); + srvSes.setRsaVerifyCtx(srvVerifyCtx); + ret = srvSes.setFd(srv); + if (ret != WolfSSL.SSL_SUCCESS) { + throw new Exception("srv setFd fail: " + ret); + } + 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("srv accept fail: " + ret); + } + srvSes.shutdownSSL(); + + } finally { + if (srvSes != null) { + srvSes.freeSSL(); + } + if (srv != null) { + srv.close(); + } + fSrvSock.close(); + } + return null; + } + }); + + if (!ready.await(2, TimeUnit.SECONDS)) { + fail("Server did not become ready within timeout"); + } + + cliSock = new Socket("localhost", port); + cliSes = new WolfSSLSession(cliCtx); + cliSes.setRsaVerifyCtx(cliVerifyCtx); + + int ret = cliSes.setFd(cliSock); + if (ret != WolfSSL.SSL_SUCCESS) { + fail("cli setFd fail: " + ret); + } + + int err; + 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) { + fail("TLS 1.2 RSA CB connect fail: " + ret); + } + + cliSes.shutdownSSL(); + cliSes.freeSSL(); + cliSock.close(); + + /* Check server thread for errors */ + es.shutdown(); + srvFuture.get(5, TimeUnit.SECONDS); + + /* Verify callbacks were invoked */ + assertTrue("RSA sign cb not called", srvSignCtx.called); + assertTrue("RSA sign check cb not called", srvVerifyCtx.called); + if (!WolfSSL.RsaPssEnabled()) { + assertTrue("RSA verify (cli) cb not called", + cliVerifyCtx.called); + } + + } catch (WolfSSLJNIException e) { + /* PK callbacks may not be compiled in */ + if (e.getMessage() != null && + e.getMessage().contains("PK Callback")) { + return; + } + System.out.println("\t\t... failed"); + fail("TLS 1.2 RSA CB handshake: " + e.getMessage()); + + } catch (ExecutionException e) { + System.out.println("\t\t... failed"); + fail("TLS 1.2 RSA CB server: " + e.getCause().getMessage()); + + } catch (Exception e) { + System.out.println("\t\t... failed"); + fail("TLS 1.2 RSA CB handshake: " + e.getMessage()); + + } finally { + if (cliSes != null) { + try { cliSes.freeSSL(); } + catch (Exception e) { /* ignore */ } + } + if (cliSock != null) { + try { cliSock.close(); } + catch (IOException e) { /* ignore */ } + } + if (srvSocket != null && !srvSocket.isClosed()) { + try { srvSocket.close(); } + catch (IOException e) { /* ignore */ } + } + if (cliCtx != null) cliCtx.free(); + if (srvCtx != null) srvCtx.free(); + if (es != null) { + es.shutdownNow(); + } + } + } + + private void rsaCbHandshakeTls13() { + + WolfSSLContext srvCtx = null; + WolfSSLContext cliCtx = null; + ServerSocket srvSocket = null; + ExecutorService es = null; + Socket cliSock = null; + WolfSSLSession cliSes = null; + + try { + srvCtx = createCtx(svrCert, svrKey, caCert, + WolfSSL.TLSv1_3_ServerMethod()); + cliCtx = createCtx(cliCert, cliKey, caCert, + WolfSSL.TLSv1_3_ClientMethod()); + + /* Server: RSA-PSS sign + sign check */ + TestRsaPssSignCb pssSignCb = new TestRsaPssSignCb(); + TestRsaPssVerifyCb pssSrvChkCb = new TestRsaPssVerifyCb(); + srvCtx.setRsaPssSignCb(pssSignCb); + srvCtx.setRsaPssSignCheckCb(pssSrvChkCb); + + /* Context objects to track invocation */ + final TestRsaCbCtx srvSignCtx = new TestRsaCbCtx(); + final TestRsaCbCtx srvVerifyCtx = new TestRsaCbCtx(); + + srvSocket = new ServerSocket(0); + srvSocket.setSoTimeout(10000); + final int port = srvSocket.getLocalPort(); + final ServerSocket fSrvSock = srvSocket; + final WolfSSLContext fSrvCtx = srvCtx; + + final CountDownLatch ready = new CountDownLatch(1); + + es = Executors.newSingleThreadExecutor(); + Future srvFuture = es.submit(new Callable() { + @Override + public Void call() throws Exception { + int ret; + int err; + Socket srv = null; + WolfSSLSession srvSes = null; + try { + ready.countDown(); + srv = fSrvSock.accept(); + srvSes = new WolfSSLSession(fSrvCtx); + srvSes.setRsaSignCtx(srvSignCtx); + srvSes.setRsaVerifyCtx(srvVerifyCtx); + ret = srvSes.setFd(srv); + if (ret != WolfSSL.SSL_SUCCESS) { + throw new Exception("srv setFd fail: " + ret); + } + 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("srv accept fail: " + ret); + } + srvSes.shutdownSSL(); + + } finally { + if (srvSes != null) { + srvSes.freeSSL(); + } + if (srv != null) { + srv.close(); + } + fSrvSock.close(); + } + return null; + } + }); + + if (!ready.await(2, TimeUnit.SECONDS)) { + fail("Server did not become ready within timeout"); + } + + cliSock = new Socket("localhost", port); + cliSes = new WolfSSLSession(cliCtx); + + int ret = cliSes.setFd(cliSock); + if (ret != WolfSSL.SSL_SUCCESS) { + fail("cli setFd fail: " + ret); + } + + int err; + 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) { + fail("TLS 1.3 PSS CB connect fail: " + ret); + } + + cliSes.shutdownSSL(); + cliSes.freeSSL(); + cliSock.close(); + + /* Check server thread for errors */ + es.shutdown(); + srvFuture.get(5, TimeUnit.SECONDS); + + /* Verify server-side callbacks were invoked. Client-side PSS peer + * verify uses internal wolfSSL code (no setRsaPssVerifyCb in + * JNI yet). */ + assertTrue("PSS sign cb not called", srvSignCtx.called); + assertTrue("PSS sign check cb not called", srvVerifyCtx.called); + + } catch (WolfSSLJNIException e) { + /* PK callbacks may not be compiled in */ + if (e.getMessage() != null && + e.getMessage().contains("PK Callback")) { + return; + } + System.out.println("\t\t... failed"); + fail("TLS 1.3 PSS CB handshake: " + e.getMessage()); + + } catch (ExecutionException e) { + System.out.println("\t\t... failed"); + fail("TLS 1.3 PSS CB server: " + e.getCause().getMessage()); + + } catch (Exception e) { + System.out.println("\t\t... failed"); + fail("TLS 1.3 PSS CB handshake: " + e.getMessage()); + + } finally { + if (cliSes != null) { + try { cliSes.freeSSL(); } + catch (Exception e) { /* ignore */ } + } + if (cliSock != null) { + try { cliSock.close(); } + catch (IOException e) { /* ignore */ } + } + if (srvSocket != null && !srvSocket.isClosed()) { + try { srvSocket.close(); } + catch (IOException e) { /* ignore */ } + } + if (cliCtx != null) cliCtx.free(); + if (srvCtx != null) srvCtx.free(); + if (es != null) { + es.shutdownNow(); + } + } + } + public void test_WolfSSLContext_free() { System.out.print("\tfree()");