From 1b44d7adffa52c70936f0a5987a4807b4484a35e Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 30 Jan 2025 15:45:21 -0700 Subject: [PATCH] JNI: wrap Atomic Record VerifyDecrypt callback --- examples/Client.java | 11 + examples/MyDecryptVerifyCallback.java | 34 +- examples/MyMacEncryptCallback.java | 29 +- examples/MyVerifyDecryptCallback.java | 163 ++++++++++ examples/Server.java | 13 +- native/com_wolfssl_WolfSSL.c | 58 ++++ native/com_wolfssl_WolfSSL.h | 58 +++- native/com_wolfssl_WolfSSLContext.c | 296 +++++++++++++++++- native/com_wolfssl_WolfSSLContext.h | 8 + src/java/com/wolfssl/WolfSSL.java | 39 ++- src/java/com/wolfssl/WolfSSLContext.java | 70 ++++- src/java/com/wolfssl/WolfSSLSession.java | 29 ++ .../wolfssl/WolfSSLVerifyDecryptCallback.java | 67 ++++ 13 files changed, 820 insertions(+), 55 deletions(-) create mode 100644 examples/MyVerifyDecryptCallback.java create mode 100644 src/java/com/wolfssl/WolfSSLVerifyDecryptCallback.java diff --git a/examples/Client.java b/examples/Client.java index a802f739..e2f32ed8 100644 --- a/examples/Client.java +++ b/examples/Client.java @@ -362,6 +362,12 @@ public void run(String[] args) { MyDecryptVerifyCallback dvcb = new MyDecryptVerifyCallback(); sslCtx.setMacEncryptCb(mecb); sslCtx.setDecryptVerifyCb(dvcb); + + if (WolfSSL.encryptThenMacEnabled()) { + MyVerifyDecryptCallback vdcb = + new MyVerifyDecryptCallback(); + sslCtx.setVerifyDecryptCb(vdcb); + } } /* register public key callbacks, ctx setup is later */ @@ -521,6 +527,11 @@ public void run(String[] args) { MyAtomicDecCtx decCtx = new MyAtomicDecCtx(); ssl.setMacEncryptCtx(encCtx); ssl.setDecryptVerifyCtx(decCtx); + + if (WolfSSL.encryptThenMacEnabled()) { + MyAtomicDecCtx vdCtx = new MyAtomicDecCtx(); + ssl.setVerifyDecryptCtx(vdCtx); + } } if (pkCallbacks == 1) { diff --git a/examples/MyDecryptVerifyCallback.java b/examples/MyDecryptVerifyCallback.java index 3ce3cbe4..2e30593f 100644 --- a/examples/MyDecryptVerifyCallback.java +++ b/examples/MyDecryptVerifyCallback.java @@ -30,6 +30,11 @@ import java.nio.ByteBuffer; import com.wolfssl.*; +/* + * Example Decrypt Verify callback implementation. + * NOTE: if native HAVE_ENCRYPT_THEN_MAC is defined, the VerifyDecrypt + * callback needs to be used. + */ class MyDecryptVerifyCallback implements WolfSSLDecryptVerifyCallback { public int decryptVerifyCallback(WolfSSLSession ssl, ByteBuffer decOut, @@ -115,23 +120,18 @@ public int decryptVerifyCallback(WolfSSLSession ssl, ByteBuffer decOut, ssl.setTlsHmacInner(myInner, macInSz, macContent, macVerify); int hmacType = ssl.getHmacType(); - switch (hmacType) { - case WolfSSL.SHA: - hmacString = "HmacSHA1"; - break; - case WolfSSL.SHA256: - hmacString = "HmacSHA256"; - break; - case WolfSSL.SHA384: - hmacString = "HmacSHA384"; - break; - case WolfSSL.SHA512: - hmacString = "HmacSHA512"; - break; - default: - System.out.println("Unsupported HMAC hash type in " + - "MyDecryptVerifyCallback"); - return -1; + if (hmacType == WolfSSL.SHA) { + hmacString = "HmacSHA1"; + } else if (hmacType == WolfSSL.SHA256) { + hmacString = "HmacSHA256"; + } else if (hmacType == WolfSSL.SHA384) { + hmacString = "HmacSHA384"; + } else if (hmacType == WolfSSL.SHA512) { + hmacString = "HmacSHA512"; + } else { + System.out.println("Unsupported HMAC hash type in " + + "MyDecryptVerifyCallback: " + hmacType); + return -1; } /* get Hmac SHA-1 key */ diff --git a/examples/MyMacEncryptCallback.java b/examples/MyMacEncryptCallback.java index 76928816..80c4d7e0 100644 --- a/examples/MyMacEncryptCallback.java +++ b/examples/MyMacEncryptCallback.java @@ -57,23 +57,18 @@ public int macEncryptCallback(WolfSSLSession ssl, ByteBuffer macOut, } int hmacType = ssl.getHmacType(); - switch (hmacType) { - case WolfSSL.SHA: - hmacString = "HmacSHA1"; - break; - case WolfSSL.SHA256: - hmacString = "HmacSHA256"; - break; - case WolfSSL.SHA384: - hmacString = "HmacSHA384"; - break; - case WolfSSL.SHA512: - hmacString = "HmacSHA512"; - break; - default: - System.out.println("Unsupported HMAC hash type in " + - "MyMacEncryptCallback"); - return -1; + if (hmacType == WolfSSL.SHA) { + hmacString = "HmacSHA1"; + } else if (hmacType == WolfSSL.SHA256) { + hmacString = "HmacSHA256"; + } else if (hmacType == WolfSSL.SHA384) { + hmacString = "HmacSHA384"; + } else if (hmacType == WolfSSL.SHA512) { + hmacString = "HmacSHA512"; + } else { + System.out.println("Unsupported HMAC hash type in " + + "MyMacEncryptCallback"); + return -1; } /* hmac, not needed if aead mode */ diff --git a/examples/MyVerifyDecryptCallback.java b/examples/MyVerifyDecryptCallback.java new file mode 100644 index 00000000..0389f52e --- /dev/null +++ b/examples/MyVerifyDecryptCallback.java @@ -0,0 +1,163 @@ +/* MyVerifyDecryptCallback.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 + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.util.*; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.Cipher; +import java.nio.ByteBuffer; +import com.wolfssl.*; + +/* + * Example Verify/Decrypt callback implementation. For use when + * HAVE_ENCRYPT_THEN_MAC is defined, which can be tested from Java using + * WolfSSL.encryptThenMacEnabled(). + * + * This example callback has been modeled directly after the native wolfSSL + * example callback (myVerifyDecryptCb()) in wolfssl/test.h. + * + * NOTE: if native HAVE_ENCRYPT_THEN_MAC is not defined, the DecryptVerify + * callback needs to be set and used. + */ +class MyVerifyDecryptCallback implements WolfSSLVerifyDecryptCallback +{ + public int verifyDecryptCallback(WolfSSLSession ssl, ByteBuffer decOut, + byte[] decIn, long decSz, int macContent, int macVerify, + long[] padSz, Object ctx) { + + int hmacType = ssl.getHmacType(); + int digestSz = ssl.getHmacSize(); + byte[] myInner = new byte[WolfSSL.WOLFSSL_TLS_HMAC_INNER_SZ]; + byte[] verify = null; + byte[] keyBytes = null; + byte[] ivBytes = null; + String hmacString; + String tlsStr = "TLS"; + + Cipher cipher = null; + MyAtomicDecCtx decCtx = (MyAtomicDecCtx) ctx; + + /* example supports (d)tls AES */ + if (ssl.getBulkCipher() != WolfSSL.wolfssl_aes) { + System.out.println("MyVerifyDecryptCallback not using AES"); + return -1; + } + + try { + if (!ssl.getVersion().contains(tlsStr)) { + System.out.println("MyVerifyDecryptCallback not using (D)TLS"); + return -1; + } + + ssl.setTlsHmacInner(myInner, decSz, macContent, macVerify); + + if (hmacType == WolfSSL.SHA) { + hmacString = "HmacSHA1"; + } else if (hmacType == WolfSSL.SHA256) { + hmacString = "HmacSHA256"; + } else if (hmacType == WolfSSL.SHA384) { + hmacString = "HmacSHA384"; + } else if (hmacType == WolfSSL.SHA512) { + hmacString = "HmacSHA512"; + } else { + System.out.println("Unsupported HMAC hash type in " + + "MyVerifyDecryptCallback: " + hmacType); + return -1; + } + + /* construct HMAC key */ + SecretKeySpec hmacKey = new SecretKeySpec( + ssl.getMacSecret(macVerify), hmacString); + + /* get Mac instance, initialize with key, compute */ + Mac mac = Mac.getInstance(hmacString); + mac.init(hmacKey); + mac.update(myInner, 0, myInner.length); + mac.update(decIn, 0, (int)decSz); + verify = mac.doFinal(); + + /* Get MAC (digestSz bytes) off end of decOut for comparison */ + byte[] verifyMac = new byte[digestSz]; + int tmpPos = decOut.position(); + decOut.position(decOut.limit() - digestSz); + decOut.get(verifyMac); + decOut.position(tmpPos); + + if (verifyMac.length != verify.length) { + System.out.println("MyVerifyDecryptCallback verifyMac length " + + "different than calculated MAC length"); + return -1; + } + + if (!Arrays.equals(verify, verifyMac)) { + System.out.println("MyVerifyDecryptCallback MAC " + + "comparison failed"); + return -1; + } + + /* Setup AES for decrypt */ + if(!decCtx.isCipherSetup()) { + int keyLen = ssl.getKeySize(); + SecretKeySpec key = null; + cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE"); + + /* Decrypt is from other side (peer) */ + if (ssl.getSide() == WolfSSL.WOLFSSL_SERVER_END) { + keyBytes = ssl.getClientWriteKey(); + ivBytes = ssl.getClientWriteIV(); + } else { + keyBytes = ssl.getServerWriteKey(); + ivBytes = ssl.getServerWriteIV(); + } + + key = new SecretKeySpec(keyBytes, "AES"); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivBytes)); + decCtx.setCipher(cipher); + decCtx.isCipherSetup(true); + } else { + cipher = decCtx.getCipher(); + + if (cipher == null) { + System.out.println("Cipher was not previously set up"); + return -1; + } + } + + /* Decrypt */ + decOut.position(0); + decOut.put(cipher.doFinal(decIn, 0, (int)decSz)); + decOut.flip(); + + byte padVal = decOut.get((int)decSz - 1); + padSz[0] = (long)padVal + 1; + + } catch (Exception e) { + e.printStackTrace(); + } + + return 0; + } +} + diff --git a/examples/Server.java b/examples/Server.java index 87274c3a..cfe36b2e 100644 --- a/examples/Server.java +++ b/examples/Server.java @@ -362,6 +362,12 @@ public void run(String[] args) { new MyDecryptVerifyCallback(); sslCtx.setMacEncryptCb(mecb); sslCtx.setDecryptVerifyCb(dvcb); + + if (WolfSSL.encryptThenMacEnabled()) { + MyVerifyDecryptCallback vdc = + new MyVerifyDecryptCallback(); + sslCtx.setVerifyDecryptCb(vdc); + } } /* register public key callbacks, ctx setup later */ @@ -515,6 +521,11 @@ public void run(String[] args) { MyAtomicDecCtx decCtx = new MyAtomicDecCtx(); ssl.setMacEncryptCtx(encCtx); ssl.setDecryptVerifyCtx(decCtx); + + if (WolfSSL.encryptThenMacEnabled()) { + MyAtomicDecCtx vdCtx = new MyAtomicDecCtx(); + ssl.setVerifyDecryptCtx(vdCtx); + } } if (pkCallbacks == 1) { @@ -566,7 +577,7 @@ public void run(String[] args) { (err == WolfSSL.SSL_ERROR_WANT_READ || err == WolfSSL.SSL_ERROR_WANT_WRITE)); - if (input.length > 0) { + if (insz > 0) { String cliMsg = new String(input, 0, insz); System.out.println("client says: " + cliMsg); } else { diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c index bab8685a..c0ac139c 100644 --- a/native/com_wolfssl_WolfSSL.c +++ b/native/com_wolfssl_WolfSSL.c @@ -342,6 +342,51 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getBulkCipherAlgorithmEnumCAMELL return wolfssl_camellia; } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumMD5 + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + + return WC_HASH_TYPE_MD5; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA1 + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + + return WC_HASH_TYPE_SHA; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA256 + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + + return WC_HASH_TYPE_SHA256; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA384 + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + + return WC_HASH_TYPE_SHA384; +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA512 + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + + return WC_HASH_TYPE_SHA512; +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getTls13SecretEnum_1CLIENT_1EARLY_1TRAFFIC_1SECRET (JNIEnv* jenv, jclass jcl) { @@ -680,6 +725,19 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_secretCallbackEnabled #endif } +JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_encryptThenMacEnabled + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifdef HAVE_ENCRYPT_THEN_MAC + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSL_SSLv3_1ServerMethod (JNIEnv* jenv, jclass jcl) { diff --git a/native/com_wolfssl_WolfSSL.h b/native/com_wolfssl_WolfSSL.h index 7d14e41d..3c774fff 100644 --- a/native/com_wolfssl_WolfSSL.h +++ b/native/com_wolfssl_WolfSSL.h @@ -203,16 +203,6 @@ extern "C" { #define com_wolfssl_WolfSSL_NO_PASSWORD -176L #undef com_wolfssl_WolfSSL_TLS13_SECRET_CB_E #define com_wolfssl_WolfSSL_TLS13_SECRET_CB_E -438L -#undef com_wolfssl_WolfSSL_MD5 -#define com_wolfssl_WolfSSL_MD5 0L -#undef com_wolfssl_WolfSSL_SHA -#define com_wolfssl_WolfSSL_SHA 1L -#undef com_wolfssl_WolfSSL_SHA256 -#define com_wolfssl_WolfSSL_SHA256 2L -#undef com_wolfssl_WolfSSL_SHA512 -#define com_wolfssl_WolfSSL_SHA512 4L -#undef com_wolfssl_WolfSSL_SHA384 -#define com_wolfssl_WolfSSL_SHA384 5L #undef com_wolfssl_WolfSSL_DSAk #define com_wolfssl_WolfSSL_DSAk 515L #undef com_wolfssl_WolfSSL_RSAk @@ -439,6 +429,46 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getBulkCipherAlgorithmEnumCHACHA JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getBulkCipherAlgorithmEnumCAMELLIA (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: getHmacEnumMD5 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumMD5 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getHmacEnumSHA1 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA1 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getHmacEnumSHA256 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA256 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getHmacEnumSHA384 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA384 + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getHmacEnumSHA512 + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getHmacEnumSHA512 + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: getTls13SecretEnum_CLIENT_EARLY_TRAFFIC_SECRET @@ -687,6 +717,14 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_sessionTicketEnabled JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_secretCallbackEnabled (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: encryptThenMacEnabled + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_wolfssl_WolfSSL_encryptThenMacEnabled + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: SSLv3_ServerMethod diff --git a/native/com_wolfssl_WolfSSLContext.c b/native/com_wolfssl_WolfSSLContext.c index 6adaad7d..8b0ae9c1 100644 --- a/native/com_wolfssl_WolfSSLContext.c +++ b/native/com_wolfssl_WolfSSLContext.c @@ -54,6 +54,9 @@ int NativeMacEncryptCb(WOLFSSL* ssl, unsigned char* macOut, int NativeDecryptVerifyCb(WOLFSSL* ssl, unsigned char* decOut, const unsigned char* decIn, unsigned int decSz, int content, int verify, unsigned int* padSz, void* ctx); +int NativeVerifyDecryptCb(WOLFSSL* ssl, unsigned char* decOut, + const unsigned char* decIn, unsigned int decSz, int content, + int macVerify, unsigned int* padSz, void* ctx); int NativeEccSignCb(WOLFSSL* ssl, const unsigned char* in, unsigned int inSz, unsigned char* out, unsigned int* outSz, const unsigned char* keyDer, unsigned int keySz, void* ctx); @@ -1979,7 +1982,38 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setDecryptVerifyCb } else { (*jenv)->ThrowNew(jenv, excClass, "Input WolfSSLContext object was null when " - "setting MacDecrypt"); + "setting DecryptVerify"); + } +#else + (*jenv)->ThrowNew(jenv, excClass, + "wolfSSL not compiled with ATOMIC_USER"); +#endif /* ATOMIC_USER */ +} + +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setVerifyDecryptCb + (JNIEnv* jenv, jobject jcl, jlong ctx) +{ + 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; + } + +#ifdef ATOMIC_USER + if(ctx) { + /* set verify/decrypt callback */ + wolfSSL_CTX_SetVerifyDecryptCb((WOLFSSL_CTX*)(uintptr_t)ctx, + NativeVerifyDecryptCb); + + } else { + (*jenv)->ThrowNew(jenv, excClass, + "Input WolfSSLContext object was null when " + "setting VerifyDecrypt"); } #else (*jenv)->ThrowNew(jenv, excClass, @@ -2509,6 +2543,266 @@ int NativeDecryptVerifyCb(WOLFSSL* ssl, unsigned char* decOut, return retval; } +int NativeVerifyDecryptCb(WOLFSSL* ssl, unsigned char* decOut, + const unsigned char* decIn, unsigned int decSz, int content, + int macVerify, unsigned int* padSz, void* ctx) +{ + jint retval = 0; + jint vmret = 0; + + JNIEnv* jenv; /* JNI environment */ + jclass excClass; /* WolfSSLJNIException class */ + int needsDetach = 0; /* Should we explicitly detach? */ + int hmacSize = 0; /* WOLFSSL HMAC digest size */ + + static jobject* g_cachedSSLObj; /* WolfSSLSession cached object */ + jclass sessClass; /* WolfSSLSession class */ + jfieldID ctxFid; /* WolfSSLSession->ctx FieldID */ + jmethodID getCtxMethodId; /* WolfSSLSession->getAssCtxPtr() ID */ + + jobject ctxRef; /* WolfSSLContext object */ + jclass innerCtxClass; /* WolfSSLContext class */ + jmethodID verifyDecryptMethodId; + + jbyteArray j_decIn; + jlongArray j_padSz; + + jobject decOutBB = NULL; + jlong tmpVal = 0; + + (void)ctx; + + if (!g_vm || !ssl || !decOut || !decIn || !padSz) { + 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 " + "NativeVerifyDecryptCb"); + 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 " + "NativeVerifyDecryptCb"); + 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 NativeVerifyDecryptCb"); + 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 NativeVerifyDecryptCb"); + 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 NativeVerifyDecryptCb"); + 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 NativeVerifyDecryptCb"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + /* call internal verify/decrypt callback */ + verifyDecryptMethodId = (*jenv)->GetMethodID(jenv, innerCtxClass, + "internalVerifyDecryptCallback", + "(Lcom/wolfssl/WolfSSLSession;Ljava/nio/ByteBuffer;[BJII[J)I"); + + if (!verifyDecryptMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->ThrowNew(jenv, excClass, + "Error getting internalVerifyDecryptCallback method " + "from JNI"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + if (retval == 0) { + + /* Get WOLFSSL HMAC digest size, decOut holds decSz + hmacSize */ + hmacSize = wolfSSL_GetHmacSize((WOLFSSL*)ssl); + + /* create ByteBuffer to wrap decOut */ + decOutBB = (*jenv)->NewDirectByteBuffer(jenv, decOut, decSz + hmacSize); + if (!decOutBB) { + (*jenv)->ThrowNew(jenv, excClass, + "failed to create decOut ByteBuffer"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + /* create jbyteArray to hold decIn */ + j_decIn = (*jenv)->NewByteArray(jenv, decSz); + if (!j_decIn) { + (*jenv)->ThrowNew(jenv, excClass, + "failed to create decIn ByteArray"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, decOutBB); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + (*jenv)->SetByteArrayRegion(jenv, j_decIn, 0, decSz, (jbyte*)decIn); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, decOutBB); + (*jenv)->DeleteLocalRef(jenv, j_decIn); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + /* create jlongArray to hold padSz, since we need to use it as + * an OUTPUT parameter from Java. Only needs to have 1 element */ + j_padSz = (*jenv)->NewLongArray(jenv, 1); + if (!j_padSz) { + (*jenv)->ThrowNew(jenv, excClass, + "failed to create padSz longArray"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, decOutBB); + (*jenv)->DeleteLocalRef(jenv, j_decIn); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + /* call Java verify/decrypt callback, java layer handles + * adding verify/decrypt CTX reference */ + retval = (*jenv)->CallIntMethod(jenv, ctxRef, verifyDecryptMethodId, + (jobject)(*g_cachedSSLObj), decOutBB, j_decIn, (jlong)decSz, + content, macVerify, j_padSz); + + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, decOutBB); + (*jenv)->DeleteLocalRef(jenv, j_decIn); + (*jenv)->DeleteLocalRef(jenv, j_padSz); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + + if (retval == 0) { + /* copy j_padSz into padSz */ + (*jenv)->GetLongArrayRegion(jenv, j_padSz, 0, 1, &tmpVal); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + (*jenv)->DeleteLocalRef(jenv, ctxRef); + (*jenv)->DeleteLocalRef(jenv, decOutBB); + (*jenv)->DeleteLocalRef(jenv, j_decIn); + (*jenv)->DeleteLocalRef(jenv, j_padSz); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + return -1; + } + *padSz = (unsigned int)tmpVal; + } + + /* delete local refs */ + (*jenv)->DeleteLocalRef(jenv, decOutBB); + (*jenv)->DeleteLocalRef(jenv, j_decIn); + (*jenv)->DeleteLocalRef(jenv, j_padSz); + } + + /* delete local refs, detach JNIEnv from thread */ + (*jenv)->DeleteLocalRef(jenv, ctxRef); + if (needsDetach) + (*g_vm)->DetachCurrentThread(g_vm); + + return retval; +} + #endif /* ATOMIC_USER */ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setEccSignCb diff --git a/native/com_wolfssl_WolfSSLContext.h b/native/com_wolfssl_WolfSSLContext.h index b6522d7e..9398350d 100644 --- a/native/com_wolfssl_WolfSSLContext.h +++ b/native/com_wolfssl_WolfSSLContext.h @@ -287,6 +287,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setMacEncryptCb JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setDecryptVerifyCb (JNIEnv *, jobject, jlong); +/* + * Class: com_wolfssl_WolfSSLContext + * Method: setVerifyDecryptCb + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setVerifyDecryptCb + (JNIEnv *, jobject, jlong); + /* * Class: com_wolfssl_WolfSSLContext * Method: setEccSignCb diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index c0c8380e..98adadab 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -394,17 +394,19 @@ public enum TLS_VERSION { /** TLS 1.3 secret callback function failure */ public static final int TLS13_SECRET_CB_E = -438; - /* hmac codes, from wolfssl/wolfcrypt/hmac.h */ + /* HMAC codes, from wolfssl/wolfcrypt/hmac.h. These values + * are set via JNI calls in static class block since they can change + * depending on if wolfSSL is a FIPS or non-FIPS build. */ /** Md5 HMAC type */ - public static final int MD5 = 0; + public static int MD5; /** SHA-1 HMAC type */ - public static final int SHA = 1; + public static int SHA; /** SHA2-256 HMAC type */ - public static final int SHA256 = 2; + public static int SHA256; /** SHA2-512 HMAC type */ - public static final int SHA512 = 4; + public static int SHA512; /** SHA2-384 HMAC type */ - public static final int SHA384 = 5; + public static int SHA384; /* key types */ /** DSA key type */ @@ -579,7 +581,7 @@ public WolfSSL() throws WolfSSLException { + ret); } - /* initialize enum values */ + /* initialize cipher enum values */ wolfssl_aes = getBulkCipherAlgorithmEnumAES(); wolfssl_cipher_null = getBulkCipherAlgorithmEnumNULL(); wolfssl_rc4 = getBulkCipherAlgorithmEnumRC4(); @@ -590,6 +592,13 @@ public WolfSSL() throws WolfSSLException { wolfssl_aes_gcm = getBulkCipherAlgorithmEnumAESGCM(); wolfssl_aes_ccm = getBulkCipherAlgorithmEnumAESCCM(); + /* initialize cipher enum values */ + MD5 = getHmacEnumMD5(); + SHA = getHmacEnumSHA1(); + SHA256 = getHmacEnumSHA256(); + SHA384 = getHmacEnumSHA384(); + SHA512 = getHmacEnumSHA512(); + /* initialize TLS 1.3 secret callback ID enums */ CLIENT_EARLY_TRAFFIC_SECRET = getTls13SecretEnum_CLIENT_EARLY_TRAFFIC_SECRET(); @@ -631,6 +640,12 @@ public WolfSSL() throws WolfSSLException { static native int getBulkCipherAlgorithmEnumCHACHA(); static native int getBulkCipherAlgorithmEnumCAMELLIA(); + static native int getHmacEnumMD5(); + static native int getHmacEnumSHA1(); + static native int getHmacEnumSHA256(); + static native int getHmacEnumSHA384(); + static native int getHmacEnumSHA512(); + static native int getTls13SecretEnum_CLIENT_EARLY_TRAFFIC_SECRET(); static native int getTls13SecretEnum_CLIENT_HANDSHAKE_TRAFFIC_SECRET(); static native int getTls13SecretEnum_SERVER_HANDSHAKE_TRAFFIC_SECRET(); @@ -931,7 +946,7 @@ protected static byte[] fileToBytes(File file) /** * Tests if native wolfSSL has been compiled with HAVE_SECRET_CALLBACK - * default. If defined, will compile in APIs to support SSL/TLS secret + * If defined, will compile in APIs to support SSL/TLS secret * callback support. * * @return true if enabled, otherwise false if HAVE_SECRET_CALLBACK @@ -939,6 +954,14 @@ protected static byte[] fileToBytes(File file) */ public static native boolean secretCallbackEnabled(); + /** + * Tests if native wolfSSL has been compiled with HAVE_ENCRYPT_THEN_MAC. + * + * @return true if enabled, otherwise false if HAVE_ENCRYPT_THEN_MAC + * has not been defined. + */ + public static native boolean encryptThenMacEnabled(); + /* ---------------- native SSL/TLS version functions ---------------- */ /** diff --git a/src/java/com/wolfssl/WolfSSLContext.java b/src/java/com/wolfssl/WolfSSLContext.java index b24fb758..de7cfd33 100644 --- a/src/java/com/wolfssl/WolfSSLContext.java +++ b/src/java/com/wolfssl/WolfSSLContext.java @@ -48,9 +48,10 @@ public class WolfSSLContext { /* user-registered DTLS cookie generation callback */ private WolfSSLGenCookieCallback internCookieCb = null; - /* user-registered MAC/encrypt and decrypt/verify callbacks */ + /* user-registered MAC/encrypt, dec/verify, verify/dec callbacks */ private WolfSSLMacEncryptCallback internMacEncryptCb = null; private WolfSSLDecryptVerifyCallback internDecryptVerifyCb = null; + private WolfSSLVerifyDecryptCallback internVerifyDecryptCb = null; /* user-registered ECC sign/verify callbacks */ private WolfSSLEccSignCallback internEccSignCb = null; @@ -147,6 +148,11 @@ synchronized WolfSSLDecryptVerifyCallback getInternDecryptVerifyCb() { return internDecryptVerifyCb; } + /* used by JNI native verify/decrypt Cb */ + synchronized WolfSSLVerifyDecryptCallback getInternVerifyDecryptCb() { + return internVerifyDecryptCb; + } + /* this will be registered with native wolfSSL library */ private int internalIORecvCallback(WolfSSLSession ssl, byte[] buf, int sz) { @@ -210,6 +216,20 @@ private int internalDecryptVerifyCallback(WolfSSLSession ssl, return ret; } + private int internalVerifyDecryptCallback(WolfSSLSession ssl, + ByteBuffer decOut, byte[] decIn, long decSz, int content, + int macVerify, long[] padSz) + { + int ret; + + /* call user-registered verify/decrypt method */ + ret = internVerifyDecryptCb.verifyDecryptCallback(ssl, decOut, + decIn, decSz, content, macVerify, padSz, + ssl.getVerifyDecryptCtx()); + + return ret; + } + private int internalEccSignCallback(WolfSSLSession ssl, ByteBuffer in, long inSz, ByteBuffer out, long[] outSz, ByteBuffer keyDer, long keySz) @@ -385,6 +405,7 @@ private native int useCertificateChainBufferFormat(long ctx, byte[] in, private native int setOCSPOverrideUrl(long ctx, String url); private native void setMacEncryptCb(long ctx); private native void setDecryptVerifyCb(long ctx); + private native void setVerifyDecryptCb(long ctx); private native void setEccSignCb(long ctx); private native void setEccVerifyCb(long ctx); private native void setEccSharedSecretCb(long ctx); @@ -1583,6 +1604,7 @@ WolfSSLDebug.INFO, getContextPtr(), * @throws IllegalStateException WolfSSLContext has been freed * @throws WolfSSLJNIException Internal JNI error * @see #setDecryptVerifyCb(WolfSSLDecryptVerifyCallback) + * @see #setVerifyDecryptCb(WolfSSLVerifyDecryptCallback) */ public synchronized void setMacEncryptCb(WolfSSLMacEncryptCallback callback) throws IllegalStateException, WolfSSLJNIException { @@ -1626,6 +1648,7 @@ WolfSSLDebug.INFO, getContextPtr(), * @throws IllegalStateException WolfSSLContext has been freed * @throws WolfSSLJNIException Internal JNI error * @see #setMacEncryptCb(WolfSSLMacEncryptCallback) + * @see #setVerifyDecryptCb(WolfSSLVerifyDecryptCallback) */ public synchronized void setDecryptVerifyCb( WolfSSLDecryptVerifyCallback callback) @@ -1646,6 +1669,51 @@ WolfSSLDebug.INFO, getContextPtr(), } } + /** + * Allows caller to set the Atomic Record Processing Verify/Decrypt + * Callback. + * The callback should return 0 for success, or a negative value for + * an error. The ssl and ctx pointers are available + * for the users convenience. decOut is the output buffer + * where the result of the decryption should be stored. decIn + * is the encrypted input buffer and decInSz notes the size of the + * buffer. context and macVerify are needed for + * setTlsHmacInner() and can be passed along as-is. padSz is + * an output variable, where the first element in the array should be set + * with the total value of the padding. That is, the mac size plus any + * padding and pad bytes. An example callback can be found in + * examples/MyVerifyDecryptCallback.java. + * + * @param callback object to be registered as the verify/decrypt + * callback for the WolfSSL context. The signature of + * this object and corresponding method must match that + * as shown in + * WolfSSLVerifyDecryptCallback.java, inside + * verifyDecryptCallback(). + * @throws IllegalStateException WolfSSLContext has been freed + * @throws WolfSSLJNIException Internal JNI error + * @see #setMacEncryptCb(WolfSSLMacEncryptCallback) + * @see #setDecryptVerifyCb(WolfSSLDecryptVerifyCallback) + */ + public synchronized void setVerifyDecryptCb( + WolfSSLVerifyDecryptCallback callback) + throws IllegalStateException, WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (ctxLock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, getContextPtr(), + "entered setVerifyDecryptCb(" + callback + ")"); + + /* set verify/decrypt callback */ + internVerifyDecryptCb = callback; + + /* register internal callback with native library */ + setVerifyDecryptCb(getContextPtr()); + } + } + /** * Allows caller to set the Public Key Callback for ECC Signing. * The callback should return 0 for success or a negative value for an diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java index da2f8db1..987a29c8 100644 --- a/src/java/com/wolfssl/WolfSSLSession.java +++ b/src/java/com/wolfssl/WolfSSLSession.java @@ -52,6 +52,7 @@ public class WolfSSLSession { private Object genCookieCtx; private Object macEncryptCtx; private Object decryptVerifyCtx; + private Object verifyDecryptCtx; private Object eccSignCtx; private Object eccVerifyCtx; private Object eccSharedSecretCtx; @@ -199,6 +200,10 @@ synchronized Object getDecryptVerifyCtx() { return this.decryptVerifyCtx; } + synchronized Object getVerifyDecryptCtx() { + return this.verifyDecryptCtx; + } + synchronized Object getEccSignCtx() { return this.eccSignCtx; } @@ -3579,6 +3584,30 @@ public void setDecryptVerifyCtx(Object ctx) } } + /** + * Allows caller to set the Atomic User Record Processing Verify/Decrypt + * Callback Context. + * + * @param ctx context object to be registered with the SSL session's + * verify/decrypt method. + * @throws IllegalStateException WolfSSLContext has been freed + * @throws WolfSSLJNIException Internal JNI error + * @see WolfSSLContext#setVerifyDecryptCb(WolfSSLVerifyDecryptCallback) + */ + public void setVerifyDecryptCtx(Object ctx) + throws IllegalStateException, WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.sslPtr, + "entered setVerifyDecryptCtx(" + ctx + ")"); + + verifyDecryptCtx = ctx; + } + } + /** * Allows caller to set the Public Key ECC Signing Callback Context. * diff --git a/src/java/com/wolfssl/WolfSSLVerifyDecryptCallback.java b/src/java/com/wolfssl/WolfSSLVerifyDecryptCallback.java new file mode 100644 index 00000000..87015c42 --- /dev/null +++ b/src/java/com/wolfssl/WolfSSLVerifyDecryptCallback.java @@ -0,0 +1,67 @@ +/* WolfSSLVerifyDecryptCallback.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 Verify/Decrypt callback interface. + * This interface specifies how applicaitons should implement the verify/decrypt + * callback class to be used by wolfSSL when using atomic record layer callbacks. + * Note that this is different than the decrypt/verify callback. For that, see + * WolfSSLDecryptVerifyCallback. + *

+ * After implementing this interface, it should be passed as a parameter + * to the {@link WolfSSLContext#setVerifyDecryptCb(WolfSSLVerifyDecryptCallback) + * WolfSSLContext.setVerifyDecryptCb()} method to be registered with the + * native wolfSSL library. + * + * @author wolfSSL + */ +public interface WolfSSLVerifyDecryptCallback { + + /** + * Atomic record layer verify/decrypt callback method. + * This method acts as the verify/decrypt callback to be used with + * the wolfSSL atomic record layer processing. + * + * @param ssl the current SSL session object from which the + * callback was initiated. + * @param decOut output buffer where the result of the decryption + * should be stored. + * @param decIn the encrypted input buffer + * @param decSz the size of the input buffer, decIn + * @param content used with setTlsHmacInner(), the type of message + * @param macVerify used with setTlsHmacInner(), specifies whether this + * is a verification of a peer message. + * @param padSz output variable that should be set with the total + * value of the padding. When setting this, the first + * element of the the array should be used. + * @param ctx user-registered decrypt/verify context + * @return 0 upon success, + * otherwise a negative value on failure. + */ + public int verifyDecryptCallback(WolfSSLSession ssl, ByteBuffer decOut, + byte[] decIn, long decSz, int content, int macVerify, long[] padSz, + Object ctx); +} +