From 98cefc55fd034ff0c6f814e9d04c5ed3d739dbab Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 15:38:07 -0600 Subject: [PATCH 01/10] F-6904: return false for malformed LMS signatures in verify wrapper --- jni/jni_lms.c | 3 ++- .../jce/test/WolfCryptLmsSignatureTest.java | 14 ++++++++++++++ .../java/com/wolfssl/wolfcrypt/test/LmsTest.java | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/jni/jni_lms.c b/jni/jni_lms.c index dcfbf2e7..a310ecc4 100644 --- a/jni/jni_lms.c +++ b/jni/jni_lms.c @@ -229,7 +229,8 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_Lms_wc_1LmsKey_1verify if (ret == 0) { result = JNI_TRUE; } - else if (ret != SIG_VERIFY_E) { + else if (ret != SIG_VERIFY_E && ret != SIG_TYPE_E && ret != BUFFER_E) { + /* Treat these returns as failed verification */ throwWolfCryptExceptionFromError(env, ret); } diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptLmsSignatureTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptLmsSignatureTest.java index c24510c3..8eb75c9e 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptLmsSignatureTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptLmsSignatureTest.java @@ -302,6 +302,20 @@ public void rfc8554TestCase1JceVerify() throws Exception { tampered[tampered.length / 2] ^= (byte) 0xFF; assertFalse("RFC 8554 TC1 tampered signature", verify(pub, tampered, RFC8554_TC1_MSG)); + + /* Wrong length signature must report false, not throw */ + byte[] truncated = new byte[RFC8554_TC1_SIG.length - 1]; + System.arraycopy(RFC8554_TC1_SIG, 0, truncated, 0, truncated.length); + assertFalse("RFC 8554 TC1 truncated signature", + verify(pub, truncated, RFC8554_TC1_MSG)); + + /* Corrupted embedded OTS type field must report false, not throw. + * Type word sits after the 4 byte levels and 4 byte q fields for + * every parameter set. */ + byte[] badType = RFC8554_TC1_SIG.clone(); + badType[8] ^= (byte)0x01; + assertFalse("RFC 8554 TC1 corrupted type signature", + verify(pub, badType, RFC8554_TC1_MSG)); } @Test diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/LmsTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/LmsTest.java index 694d5a7e..b832efce 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/LmsTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/LmsTest.java @@ -395,6 +395,20 @@ private void assertRfcVerifyKat(String name, byte[] pub, byte[] msg, byte[] tampered = sig.clone(); tampered[tampered.length / 2] ^= (byte) 0xFF; assertFalse(name + " tampered signature", v.verify(tampered, msg)); + + /* Wrong length signature must report false, not throw */ + byte[] truncated = new byte[sig.length - 1]; + System.arraycopy(sig, 0, truncated, 0, truncated.length); + assertFalse(name + " truncated signature", + v.verify(truncated, msg)); + + /* Corrupted embedded OTS type field must report false, not throw. + * Type word sits after the 4 byte levels and 4 byte q fields for + * every parameter set. */ + byte[] badType = sig.clone(); + badType[8] ^= (byte)0x01; + assertFalse(name + " corrupted type signature", + v.verify(badType, msg)); } finally { v.releaseNativeStruct(); } From 90f7e095461ebc2ab8f92b6477f0784734ac7c6f Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 15:58:17 -0600 Subject: [PATCH 02/10] F-8197: normalize X509_STORE_add_cert failure codes to negative --- jni/jni_wolfssl_x509_store_ctx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jni/jni_wolfssl_x509_store_ctx.c b/jni/jni_wolfssl_x509_store_ctx.c index 98e88f28..fb9fd4d3 100644 --- a/jni/jni_wolfssl_x509_store_ctx.c +++ b/jni/jni_wolfssl_x509_store_ctx.c @@ -287,6 +287,10 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLX509StoreCtx_wolfSSL_1X if (ret != WOLFSSL_SUCCESS) { LogStr("wolfSSL_X509_STORE_add_cert() failed: %d\n", ret); wolfSSL_X509_free(x509); + /* Normalize to negative so Java does not read failure as success */ + if (ret >= 0) { + ret = BAD_STATE_E; + } return ret; } ret = 0; From 8731e5ee22e38ac3800bba6d24259a68eda021d5 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:01:17 -0600 Subject: [PATCH 03/10] F-8198: check size query result before PKCS8 buffer allocation --- jni/jni_ecc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jni/jni_ecc.c b/jni/jni_ecc.c index 6e86865d..ca7528a2 100644 --- a/jni/jni_ecc.c +++ b/jni/jni_ecc.c @@ -1120,7 +1120,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1private_1ke if (ret == LENGTH_ONLY_E) { ret = 0; } + } + if (ret == 0) { pkcs8 = (byte*)XMALLOC(pkcs8Sz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (pkcs8 == NULL) { ret = MEMORY_E; From 3c3d2a7957edc5b3a3c80e7f1e058be09ed9492f Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:05:57 -0600 Subject: [PATCH 04/10] F-8199: fix curve size sign handling in ECC import_public_raw --- jni/jni_ecc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/jni/jni_ecc.c b/jni/jni_ecc.c index ca7528a2..7ac210ce 100644 --- a/jni/jni_ecc.c +++ b/jni/jni_ecc.c @@ -1471,6 +1471,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1import_1public_1r word32 ySz = 0; const char* name = NULL; int curveId = 0; + int curveSz = 0; word32 expectedSz = 0; ecc = (ecc_key*) getNativeStruct(env, this); @@ -1498,15 +1499,18 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1import_1public_1r if (ret == 0) { curveId = wc_ecc_get_curve_id_from_name(name); /* Get expected size for curve */ - expectedSz = wc_ecc_get_curve_size_from_id(curveId); + curveSz = wc_ecc_get_curve_size_from_id(curveId); (*env)->ReleaseStringUTFChars(env, curveName, name); - if (curveId < 0 || expectedSz <= 0) { + if (curveId < 0 || curveSz <= 0) { ret = BAD_FUNC_ARG; } + else { + expectedSz = (word32)curveSz; + } } - if (xSz != expectedSz || ySz != expectedSz) { + if (ret == 0 && (xSz != expectedSz || ySz != expectedSz)) { LogStr("ECC x or y size does not match expected size for curve\n"); ret = BAD_FUNC_ARG; } From 5493116ba7b74b90cfba008b2f5d3004a3429390 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:10:51 -0600 Subject: [PATCH 05/10] F-8205: bound direct buffer write position in Md5 final --- jni/jni_md5.c | 5 ++- .../com/wolfssl/wolfcrypt/test/Md5Test.java | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/jni/jni_md5.c b/jni/jni_md5.c index 4fba9197..c6ab83c8 100644 --- a/jni/jni_md5.c +++ b/jni/jni_md5.c @@ -221,6 +221,7 @@ Java_com_wolfssl_wolfcrypt_Md5_native_1final_1internal__Ljava_nio_ByteBuffer_2I( int ret = 0; Md5* md5 = NULL; byte* hash = NULL; + jlong hashSz = 0; md5 = (Md5*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -229,8 +230,10 @@ Java_com_wolfssl_wolfcrypt_Md5_native_1final_1internal__Ljava_nio_ByteBuffer_2I( } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); - if (!md5 || !hash) { + if (!md5 || !hash || (position < 0) || + ((jlong)position + MD5_DIGEST_SIZE) > hashSz) { throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG); } else { ret = wc_Md5Final(md5, hash + position); diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java index ea11abc4..e74a48c8 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java @@ -75,6 +75,37 @@ public void constructorShouldNotInitializeNativeStruct() { assertEquals(NativeStruct.NULL, new Md5().getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedMd5 extends Md5 { + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedMd5 md5 = new ExposedMd5(); + ByteBuffer hash = ByteBuffer.allocateDirect(Md5.DIGEST_SIZE); + + /* initialize digest state */ + md5.update(new byte[4]); + + try { + md5.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + md5.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than DIGEST_SIZE bytes"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void hashShouldMatchUsingByteBuffer() throws ShortBufferException { String[] dataVector = new String[] { From 8ac4c566ab6573423b08d9a279b7652640b61570 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:20:09 -0600 Subject: [PATCH 06/10] F-6437: zeroize secret ML-KEM input copies before JNI release --- jni/include/wolfcrypt_jni_NativeStruct.h | 3 +++ jni/jni_mlkem.c | 27 ++++++++++--------- jni/jni_native_struct.c | 33 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/jni/include/wolfcrypt_jni_NativeStruct.h b/jni/include/wolfcrypt_jni_NativeStruct.h index 06eccd21..767a4a4d 100644 --- a/jni/include/wolfcrypt_jni_NativeStruct.h +++ b/jni/include/wolfcrypt_jni_NativeStruct.h @@ -38,6 +38,9 @@ void setDirectBufferLimit(JNIEnv* env, jobject buffer, jint limit); byte* getByteArray(JNIEnv* env, jbyteArray array); void releaseByteArray(JNIEnv* env, jbyteArray array, byte* elements, jint abort); word32 getByteArrayLength(JNIEnv* env, jbyteArray array); +byte* getSecretByteArray(JNIEnv* env, jbyteArray array, jboolean* isCopy); +void releaseSecretByteArray(JNIEnv* env, jbyteArray array, byte* elements, + word32 len, jboolean isCopy); void initializeNativeStruct(JNIEnv* env, jobject obj); #ifdef __cplusplus diff --git a/jni/jni_mlkem.c b/jni/jni_mlkem.c index 2f4c93c7..b7ee03c0 100644 --- a/jni/jni_mlkem.c +++ b/jni/jni_mlkem.c @@ -200,16 +200,17 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1make_1key_1fr MlKemKey* key = NULL; byte* seed = NULL; word32 seedSz = 0; + jboolean seedIsCopy = JNI_FALSE; key = (MlKemKey*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { return; } - seed = getByteArray(env, seed_object); + seed = getSecretByteArray(env, seed_object, &seedIsCopy); seedSz = getByteArrayLength(env, seed_object); - /* getByteArray() can return NULL with a pending exception */ + /* getSecretByteArray() can return NULL with a pending exception */ if (seed_object != NULL && seed == NULL) { return; } @@ -228,7 +229,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1make_1key_1fr LogStr("wc_MlKemKey_MakeKeyWithRandom(key=%p, seedSz=%u) = %d\n", key, (word32)seedSz, ret); - releaseByteArray(env, seed_object, seed, JNI_ABORT); + releaseSecretByteArray(env, seed_object, seed, seedSz, seedIsCopy); #else (void)this; (void)seed_object; @@ -324,6 +325,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random( MlKemKey* key = NULL; byte* rand = NULL; word32 randSz = 0; + jboolean randIsCopy = JNI_FALSE; byte* output = NULL; word32 ctSz = 0; word32 ssSz = 0; @@ -334,16 +336,16 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random( return NULL; } - rand = getByteArray(env, rand_object); + rand = getSecretByteArray(env, rand_object, &randIsCopy); randSz = getByteArrayLength(env, rand_object); - /* getByteArray() can return NULL with a pending exception */ + /* getSecretByteArray() can return NULL with a pending exception */ if (rand_object != NULL && rand == NULL) { return NULL; } if (key == NULL || rand == NULL) { - releaseByteArray(env, rand_object, rand, JNI_ABORT); + releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy); throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG); return NULL; } @@ -353,7 +355,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random( ret = wc_MlKemKey_SharedSecretSize(key, &ssSz); } if (ret != 0) { - releaseByteArray(env, rand_object, rand, JNI_ABORT); + releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy); throwWolfCryptExceptionFromError(env, ret); return NULL; } @@ -361,7 +363,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random( totalSz = ctSz + ssSz; output = (byte*)XMALLOC(totalSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (output == NULL) { - releaseByteArray(env, rand_object, rand, JNI_ABORT); + releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy); throwOutOfMemoryException(env, "Failed to allocate encapsulation"); return NULL; } @@ -390,7 +392,7 @@ Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1encapsulate_1with_1random( MLKEM_FORCE_ZERO(output, totalSz); XFREE(output, NULL, DYNAMIC_TYPE_TMP_BUFFER); - releaseByteArray(env, rand_object, rand, JNI_ABORT); + releaseSecretByteArray(env, rand_object, rand, randSz, randIsCopy); #else (void)this; (void)rand_object; @@ -659,16 +661,17 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1import_1priva MlKemKey* key = NULL; byte* priv = NULL; word32 privSz = 0; + jboolean privIsCopy = JNI_FALSE; key = (MlKemKey*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { return; } - priv = getByteArray(env, priv_object); + priv = getSecretByteArray(env, priv_object, &privIsCopy); privSz = getByteArrayLength(env, priv_object); - /* getByteArray() can return NULL with a pending exception */ + /* getSecretByteArray() can return NULL with a pending exception */ if (priv_object != NULL && priv == NULL) { return; } @@ -687,7 +690,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlKem_wc_1mlkem_1import_1priva LogStr("wc_MlKemKey_DecodePrivateKey(key=%p, privSz=%u) = %d\n", key, (word32)privSz, ret); - releaseByteArray(env, priv_object, priv, JNI_ABORT); + releaseSecretByteArray(env, priv_object, priv, privSz, privIsCopy); #else (void)this; (void)priv_object; diff --git a/jni/jni_native_struct.c b/jni/jni_native_struct.c index 858458a5..0c5811d2 100644 --- a/jni/jni_native_struct.c +++ b/jni/jni_native_struct.c @@ -26,6 +26,8 @@ #include #endif #include +#include +#include #include #include @@ -220,6 +222,37 @@ word32 getByteArrayLength(JNIEnv* env, jbyteArray array) return array ? (*env)->GetArrayLength(env, array) : 0; } +/* Zeroize sensitive buffer 'buf' of size 'sz' bytes, using wc_ForceZero + * when available, otherwise XMEMSET. */ +#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && !defined(WOLFSSL_NO_FORCE_ZERO) + #define WC_JNI_FORCE_ZERO(buf, sz) wc_ForceZero((buf), (sz)) +#else + #define WC_JNI_FORCE_ZERO(buf, sz) XMEMSET((buf), 0, (sz)) +#endif + +/* Pin secret input array elements, capturing if JVM returned a copy */ +byte* getSecretByteArray(JNIEnv* env, jbyteArray array, jboolean* isCopy) +{ + *isCopy = JNI_FALSE; + + if (array == NULL) { + return NULL; + } + + return (byte*)(*env)->GetByteArrayElements(env, array, isCopy); +} + +/* Zero a pinned copy of secret input, then release without copy back. */ +void releaseSecretByteArray(JNIEnv* env, jbyteArray array, + byte* elements, word32 len, jboolean isCopy) +{ + if (elements != NULL && isCopy == JNI_TRUE) { + WC_JNI_FORCE_ZERO(elements, len); + } + + releaseByteArray(env, array, elements, JNI_ABORT); +} + void initializeNativeStruct(JNIEnv* env, jobject obj) { jclass class; From 5eb47dacdf94be0015692631ab9ab9d4deab6f10 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:26:17 -0600 Subject: [PATCH 07/10] F-6902: zeroize secret SLH-DSA input copies before JNI release --- jni/jni_slhdsa.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/jni/jni_slhdsa.c b/jni/jni_slhdsa.c index 4160490c..d88e2cef 100644 --- a/jni/jni_slhdsa.c +++ b/jni/jni_slhdsa.c @@ -307,6 +307,8 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1make_1ke word32 skSeedLen = 0; word32 skPrfLen = 0; word32 pkSeedLen = 0; + jboolean skSeedIsCopy = JNI_FALSE; + jboolean skPrfIsCopy = JNI_FALSE; key = (SlhDsaKey*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -314,12 +316,12 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1make_1ke } if (skSeed_object != NULL) { - skSeed = getByteArray(env, skSeed_object); + skSeed = getSecretByteArray(env, skSeed_object, &skSeedIsCopy); skSeedLen = getByteArrayLength(env, skSeed_object); } if (skPrf_object != NULL) { - skPrf = getByteArray(env, skPrf_object); + skPrf = getSecretByteArray(env, skPrf_object, &skPrfIsCopy); skPrfLen = getByteArrayLength(env, skPrf_object); } @@ -332,12 +334,10 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1make_1ke (skPrf_object != NULL && skPrf == NULL) || (pkSeed_object != NULL && pkSeed == NULL)) { - if (skSeed != NULL) { - releaseByteArray(env, skSeed_object, skSeed, JNI_ABORT); - } - if (skPrf != NULL) { - releaseByteArray(env, skPrf_object, skPrf, JNI_ABORT); - } + releaseSecretByteArray(env, skSeed_object, skSeed, skSeedLen, + skSeedIsCopy); + releaseSecretByteArray(env, skPrf_object, skPrf, skPrfLen, + skPrfIsCopy); if (pkSeed != NULL) { releaseByteArray(env, pkSeed_object, pkSeed, JNI_ABORT); } @@ -358,12 +358,10 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1make_1ke LogStr("wc_SlhDsaKey_MakeKeyWithRandom(key=%p) = %d\n", key, ret); - if (skSeed_object != NULL) { - releaseByteArray(env, skSeed_object, skSeed, JNI_ABORT); - } - if (skPrf_object != NULL) { - releaseByteArray(env, skPrf_object, skPrf, JNI_ABORT); - } + releaseSecretByteArray(env, skSeed_object, skSeed, skSeedLen, + skSeedIsCopy); + releaseSecretByteArray(env, skPrf_object, skPrf, skPrfLen, + skPrfIsCopy); if (pkSeed_object != NULL) { releaseByteArray(env, pkSeed_object, pkSeed, JNI_ABORT); } @@ -1315,13 +1313,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1import_1 SlhDsaKey* key = NULL; byte* in = NULL; word32 inLen = 0; + jboolean inIsCopy = JNI_FALSE; key = (SlhDsaKey*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { return; } - in = getByteArray(env, in_object); + in = getSecretByteArray(env, in_object, &inIsCopy); inLen = getByteArrayLength(env, in_object); if (key == NULL || in == NULL) { @@ -1337,7 +1336,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1import_1 LogStr("wc_SlhDsaKey_ImportPrivate(key=%p) = %d\n", key, ret); - releaseByteArray(env, in_object, in, JNI_ABORT); + releaseSecretByteArray(env, in_object, in, inLen, inIsCopy); #else (void)env; (void)this; @@ -1523,13 +1522,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1PrivateK byte* derCopy = NULL; word32 derLen = 0; word32 idx = 0; + jboolean derIsCopy = JNI_FALSE; key = (SlhDsaKey*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { return; } - der = getByteArray(env, der_object); + der = getSecretByteArray(env, der_object, &derIsCopy); derLen = getByteArrayLength(env, der_object); if (key == NULL || der == NULL || derLen == 0) { @@ -1561,7 +1561,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_SlhDsa_wc_1SlhDsaKey_1PrivateK wc_ForceZero(derCopy, derLen); XFREE(derCopy, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - releaseByteArray(env, der_object, der, JNI_ABORT); + releaseSecretByteArray(env, der_object, der, derLen, derIsCopy); #else (void)env; (void)this; From f1430cc540a94ac9454d902abfd03a4612360446 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:37:28 -0600 Subject: [PATCH 08/10] F-6903: bound direct buffer write position in SHA final wrappers --- jni/jni_sha.c | 53 +++++++++++++++++-- .../wolfssl/wolfcrypt/test/Sha224Test.java | 31 +++++++++++ .../wolfssl/wolfcrypt/test/Sha256Test.java | 31 +++++++++++ .../wolfssl/wolfcrypt/test/Sha384Test.java | 31 +++++++++++ .../com/wolfssl/wolfcrypt/test/Sha3Test.java | 34 ++++++++++++ .../wolfssl/wolfcrypt/test/Sha512Test.java | 31 +++++++++++ .../com/wolfssl/wolfcrypt/test/ShaTest.java | 31 +++++++++++ 7 files changed, 237 insertions(+), 5 deletions(-) diff --git a/jni/jni_sha.c b/jni/jni_sha.c index c131d981..af4f5e2d 100644 --- a/jni/jni_sha.c +++ b/jni/jni_sha.c @@ -349,6 +349,7 @@ Java_com_wolfssl_wolfcrypt_Sha_native_1final_1internal__Ljava_nio_ByteBuffer_2I( int ret = 0; Sha* sha = NULL; byte* hash = NULL; + jlong hashSz = 0; sha = (Sha*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -357,8 +358,10 @@ Java_com_wolfssl_wolfcrypt_Sha_native_1final_1internal__Ljava_nio_ByteBuffer_2I( } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); - ret = (!sha || !hash) + ret = (!sha || !hash || position < 0 || + ((jlong)position + SHA_DIGEST_SIZE) > hashSz) ? BAD_FUNC_ARG : wc_ShaFinal(sha, hash + position); @@ -571,6 +574,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1final_1internal int ret = 0; Sha224* sha = NULL; byte* hash = NULL; + jlong hashSz = 0; sha = (Sha224*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -579,8 +583,10 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1final_1internal } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); - if (sha == NULL || hash == NULL) { + if (sha == NULL || hash == NULL || position < 0 || + ((jlong)position + SHA224_DIGEST_SIZE) > hashSz) { ret = BAD_FUNC_ARG; } else { @@ -794,6 +800,7 @@ Java_com_wolfssl_wolfcrypt_Sha256_native_1final_1internal__Ljava_nio_ByteBuffer_ int ret = 0; Sha256* sha = NULL; byte* hash = NULL; + jlong hashSz = 0; sha = (Sha256*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -802,8 +809,10 @@ Java_com_wolfssl_wolfcrypt_Sha256_native_1final_1internal__Ljava_nio_ByteBuffer_ } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); - ret = (!sha || !hash) + ret = (!sha || !hash || position < 0 || + ((jlong)position + SHA256_DIGEST_SIZE) > hashSz) ? BAD_FUNC_ARG : wc_Sha256Final(sha, hash + position); @@ -1000,6 +1009,7 @@ Java_com_wolfssl_wolfcrypt_Sha384_native_1final_1internal__Ljava_nio_ByteBuffer_ int ret = 0; Sha384* sha = NULL; byte* hash = NULL; + jlong hashSz = 0; sha = (Sha384*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -1008,8 +1018,10 @@ Java_com_wolfssl_wolfcrypt_Sha384_native_1final_1internal__Ljava_nio_ByteBuffer_ } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); - ret = (!sha || !hash) + ret = (!sha || !hash || position < 0 || + ((jlong)position + SHA384_DIGEST_SIZE) > hashSz) ? BAD_FUNC_ARG : wc_Sha384Final(sha, hash + position); @@ -1207,6 +1219,7 @@ Java_com_wolfssl_wolfcrypt_Sha512_native_1final_1internal__Ljava_nio_ByteBuffer_ int ret = 0; Sha512* sha = NULL; byte* hash = NULL; + jlong hashSz = 0; sha = (Sha512*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -1215,8 +1228,10 @@ Java_com_wolfssl_wolfcrypt_Sha512_native_1final_1internal__Ljava_nio_ByteBuffer_ } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); - ret = (!sha || !hash) + ret = (!sha || !hash || position < 0 || + ((jlong)position + SHA512_DIGEST_SIZE) > hashSz) ? BAD_FUNC_ARG : wc_Sha512Final(sha, hash + position); @@ -1524,6 +1539,8 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1final_1internal__ int ret = 0; wc_Sha3* sha = NULL; byte* hash = NULL; + jlong hashSz = 0; + word32 digestSz = 0; sha = (wc_Sha3*) getNativeStruct(env, this); if ((*env)->ExceptionOccurred(env)) { @@ -1532,11 +1549,37 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1final_1internal__ } hash = getDirectBufferAddress(env, hash_buffer); + hashSz = (*env)->GetDirectBufferCapacity(env, hash_buffer); if (sha == NULL || hash == NULL) { ret = BAD_FUNC_ARG; } + if (ret == 0) { + switch(hashType) { + case WC_HASH_TYPE_SHA3_224: + digestSz = WC_SHA3_224_DIGEST_SIZE; + break; + case WC_HASH_TYPE_SHA3_256: + digestSz = WC_SHA3_256_DIGEST_SIZE; + break; + case WC_HASH_TYPE_SHA3_384: + digestSz = WC_SHA3_384_DIGEST_SIZE; + break; + case WC_HASH_TYPE_SHA3_512: + digestSz = WC_SHA3_512_DIGEST_SIZE; + break; + default: + ret = BAD_FUNC_ARG; + break; + } + } + + if (ret == 0 && + (position < 0 || ((jlong)position + digestSz) > hashSz)) { + ret = BAD_FUNC_ARG; + } + if (ret == 0) { switch(hashType) { case WC_HASH_TYPE_SHA3_224: diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java index ad2235fa..371ccd6b 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java @@ -85,6 +85,37 @@ public void constructorShouldNotInitializeNativeStruct() { assertEquals(NativeStruct.NULL, new Sha224().getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedSha224 extends Sha224 { + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedSha224 sha = new ExposedSha224(); + ByteBuffer hash = ByteBuffer.allocateDirect(Sha224.DIGEST_SIZE); + + /* initialize digest state */ + sha.update(new byte[4]); + + try { + sha.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + sha.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than DIGEST_SIZE bytes"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void hashShouldMatchUsingByteBuffer() throws ShortBufferException { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java index e0a5cf8a..aaaae882 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java @@ -75,6 +75,37 @@ public void constructorShouldNotInitializeNativeStruct() { assertEquals(NativeStruct.NULL, new Sha256().getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedSha256 extends Sha256 { + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedSha256 sha = new ExposedSha256(); + ByteBuffer hash = ByteBuffer.allocateDirect(Sha256.DIGEST_SIZE); + + /* initialize digest state */ + sha.update(new byte[4]); + + try { + sha.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + sha.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than DIGEST_SIZE bytes"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void hashShouldMatchUsingByteBuffer() throws ShortBufferException { String[] dataVector = new String[] { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java index 99644a51..091d9e0a 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java @@ -75,6 +75,37 @@ public void constructorShouldNotInitializeNativeStruct() { assertEquals(NativeStruct.NULL, new Sha384().getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedSha384 extends Sha384 { + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedSha384 sha = new ExposedSha384(); + ByteBuffer hash = ByteBuffer.allocateDirect(Sha384.DIGEST_SIZE); + + /* initialize digest state */ + sha.update(new byte[4]); + + try { + sha.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + sha.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than DIGEST_SIZE bytes"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void hashShouldMatchUsingByteBuffer() throws ShortBufferException { String[] dataVector = new String[] { "", "c2edba56a6b82cc3", diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java index af4bbf1a..0a2d8bcb 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java @@ -75,6 +75,40 @@ public void constructorShouldNotInitializeNativeStruct() { new Sha3(Sha3.TYPE_SHA3_256).getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedSha3 extends Sha3 { + public ExposedSha3(int hashType) { + super(hashType); + } + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedSha3 sha = new ExposedSha3(Sha3.TYPE_SHA3_256); + ByteBuffer hash = ByteBuffer.allocateDirect(Sha3.DIGEST_SIZE_256); + + /* initialize digest state */ + sha.update(new byte[4]); + + try { + sha.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + sha.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than the SHA3-256 digest size"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void sha3_256HashShouldMatchUsingByteArray() { /* Test vectors from NIST FIPS 202 - SHA-3 Standard */ diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java index b6fe0fea..3d75bf6c 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java @@ -75,6 +75,37 @@ public void constructorShouldNotInitializeNativeStruct() { assertEquals(NativeStruct.NULL, new Sha512().getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedSha512 extends Sha512 { + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedSha512 sha = new ExposedSha512(); + ByteBuffer hash = ByteBuffer.allocateDirect(Sha512.DIGEST_SIZE); + + /* initialize digest state */ + sha.update(new byte[4]); + + try { + sha.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + sha.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than DIGEST_SIZE bytes"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void hashShouldMatchUsingByteBuffer() throws ShortBufferException { String[] dataVector = new String[] { "", "20580a530f01e771", diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java index 5c9c4b29..3cac3f4e 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java @@ -75,6 +75,37 @@ public void constructorShouldNotInitializeNativeStruct() { assertEquals(NativeStruct.NULL, new Sha().getNativeStruct()); } + /* Exposes protected native_final() to test its position validation */ + private static class ExposedSha extends Sha { + public void finalAt(ByteBuffer hash, int position) { + native_final(hash, position); + } + } + + @Test + public void finalWithInvalidPositionShouldThrow() { + ExposedSha sha = new ExposedSha(); + ByteBuffer hash = ByteBuffer.allocateDirect(Sha.DIGEST_SIZE); + + /* initialize digest state */ + sha.update(new byte[4]); + + try { + sha.finalAt(hash, -1); + fail("native_final() should have thrown for negative position"); + } catch (WolfCryptException e) { + /* expected */ + } + + try { + sha.finalAt(hash, 1); + fail("native_final() should have thrown for position leaving " + + "less than DIGEST_SIZE bytes"); + } catch (WolfCryptException e) { + /* expected */ + } + } + @Test public void hashShouldMatchUsingByteBuffer() throws ShortBufferException { String[] dataVector = new String[] { From 44d89f2dbcfb794d5fa0dc03fa817c3f7f69b3a4 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 13 Aug 2026 16:41:30 -0600 Subject: [PATCH 09/10] F-8206: check GetStringUTFChars result in ECC curve name paths --- jni/jni_ecc.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/jni/jni_ecc.c b/jni/jni_ecc.c index 7ac210ce..bae92f79 100644 --- a/jni/jni_ecc.c +++ b/jni/jni_ecc.c @@ -297,6 +297,12 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1import_1private if (ret == 0) { if (curveName != NULL) { name = (*env)->GetStringUTFChars(env, curveName, 0); + if (name == NULL) { + /* OutOfMemoryError pending, release arrays and return */ + releaseByteArray(env, priv_object, priv, JNI_ABORT); + releaseByteArray(env, pub_object, pub, JNI_ABORT); + return; + } ret = wc_ecc_get_curve_id_from_name(name); (*env)->ReleaseStringUTFChars(env, curveName, name); @@ -1018,8 +1024,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Ecc_wc_1ecc_1get_1curve_1size_ ret = BAD_FUNC_ARG; } else { name = (*env)->GetStringUTFChars(env, curveName, 0); - ret = wc_ecc_get_curve_size_from_name(name); - (*env)->ReleaseStringUTFChars(env, curveName, name); + if (name == NULL) { + /* OutOfMemoryError pending */ + ret = MEMORY_E; + } + else { + ret = wc_ecc_get_curve_size_from_name(name); + (*env)->ReleaseStringUTFChars(env, curveName, name); + } } #else From 9f85a7646741d607078e7cc3bce131d01e2edb27 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Fri, 14 Aug 2026 12:14:24 -0600 Subject: [PATCH 10/10] F-3561: zeroize AES and 3DES key material in releaseNativeStruct --- jni/include/com_wolfssl_wolfcrypt_Aes.h | 8 ++++++ jni/include/com_wolfssl_wolfcrypt_AesCtr.h | 8 ++++++ jni/include/com_wolfssl_wolfcrypt_AesEcb.h | 8 ++++++ jni/include/com_wolfssl_wolfcrypt_AesOfb.h | 8 ++++++ jni/include/com_wolfssl_wolfcrypt_Des3.h | 8 ++++++ jni/jni_aes.c | 28 +++++++++++++++++++ jni/jni_aesctr.c | 28 +++++++++++++++++++ jni/jni_aesecb.c | 28 +++++++++++++++++++ jni/jni_aesofb.c | 28 +++++++++++++++++++ jni/jni_des3.c | 28 +++++++++++++++++++ src/main/java/com/wolfssl/wolfcrypt/Aes.java | 6 ++++ .../java/com/wolfssl/wolfcrypt/AesCtr.java | 11 ++++++-- .../java/com/wolfssl/wolfcrypt/AesEcb.java | 6 ++++ .../java/com/wolfssl/wolfcrypt/AesOfb.java | 11 ++++++-- .../com/wolfssl/wolfcrypt/BlockCipher.java | 13 ++++++++- src/main/java/com/wolfssl/wolfcrypt/Des3.java | 6 ++++ 16 files changed, 226 insertions(+), 7 deletions(-) diff --git a/jni/include/com_wolfssl_wolfcrypt_Aes.h b/jni/include/com_wolfssl_wolfcrypt_Aes.h index a1641c82..3af824a6 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Aes.h +++ b/jni/include/com_wolfssl_wolfcrypt_Aes.h @@ -29,6 +29,14 @@ extern "C" { JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Aes_mallocNativeStruct_1internal (JNIEnv *, jobject); +/* + * Class: com_wolfssl_wolfcrypt_Aes + * Method: wc_AesFree + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Aes_wc_1AesFree + (JNIEnv *, jobject); + /* * Class: com_wolfssl_wolfcrypt_Aes * Method: native_set_key_internal diff --git a/jni/include/com_wolfssl_wolfcrypt_AesCtr.h b/jni/include/com_wolfssl_wolfcrypt_AesCtr.h index 738e4be2..09260b71 100644 --- a/jni/include/com_wolfssl_wolfcrypt_AesCtr.h +++ b/jni/include/com_wolfssl_wolfcrypt_AesCtr.h @@ -25,6 +25,14 @@ extern "C" { JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_mallocNativeStruct_1internal (JNIEnv *, jobject); +/* + * Class: com_wolfssl_wolfcrypt_AesCtr + * Method: wc_AesFree + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_wc_1AesFree + (JNIEnv *, jobject); + /* * Class: com_wolfssl_wolfcrypt_AesCtr * Method: native_set_key_internal diff --git a/jni/include/com_wolfssl_wolfcrypt_AesEcb.h b/jni/include/com_wolfssl_wolfcrypt_AesEcb.h index 2cad2689..184678d4 100644 --- a/jni/include/com_wolfssl_wolfcrypt_AesEcb.h +++ b/jni/include/com_wolfssl_wolfcrypt_AesEcb.h @@ -29,6 +29,14 @@ extern "C" { JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_mallocNativeStruct_1internal (JNIEnv *, jobject); +/* + * Class: com_wolfssl_wolfcrypt_AesEcb + * Method: wc_AesFree + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_wc_1AesFree + (JNIEnv *, jobject); + /* * Class: com_wolfssl_wolfcrypt_AesEcb * Method: native_set_key_internal diff --git a/jni/include/com_wolfssl_wolfcrypt_AesOfb.h b/jni/include/com_wolfssl_wolfcrypt_AesOfb.h index 74ab58a6..22ebed08 100644 --- a/jni/include/com_wolfssl_wolfcrypt_AesOfb.h +++ b/jni/include/com_wolfssl_wolfcrypt_AesOfb.h @@ -29,6 +29,14 @@ extern "C" { JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_mallocNativeStruct_1internal (JNIEnv *, jobject); +/* + * Class: com_wolfssl_wolfcrypt_AesOfb + * Method: wc_AesFree + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_wc_1AesFree + (JNIEnv *, jobject); + /* * Class: com_wolfssl_wolfcrypt_AesOfb * Method: native_set_key_internal diff --git a/jni/include/com_wolfssl_wolfcrypt_Des3.h b/jni/include/com_wolfssl_wolfcrypt_Des3.h index e6630eb7..76955550 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Des3.h +++ b/jni/include/com_wolfssl_wolfcrypt_Des3.h @@ -17,6 +17,14 @@ extern "C" { #define com_wolfssl_wolfcrypt_Des3_ENCRYPT_MODE 0L #undef com_wolfssl_wolfcrypt_Des3_DECRYPT_MODE #define com_wolfssl_wolfcrypt_Des3_DECRYPT_MODE 1L +/* + * Class: com_wolfssl_wolfcrypt_Des3 + * Method: wc_Des3Free + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Des3_wc_1Des3Free + (JNIEnv *, jobject); + /* * Class: com_wolfssl_wolfcrypt_Des3 * Method: native_set_key_internal diff --git a/jni/jni_aes.c b/jni/jni_aes.c index 4b501e32..ede21fd8 100644 --- a/jni/jni_aes.c +++ b/jni/jni_aes.c @@ -40,6 +40,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Aes_mallocNativeStruct_1inter { #ifndef NO_AES Aes* aes = NULL; + int ret = 0; aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (aes == NULL) { @@ -47,6 +48,12 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Aes_mallocNativeStruct_1inter } else { XMEMSET(aes, 0, sizeof(Aes)); + ret = wc_AesInit(aes, NULL, INVALID_DEVID); + if (ret != 0) { + XFREE(aes, NULL, DYNAMIC_TYPE_TMP_BUFFER); + aes = NULL; + throwWolfCryptExceptionFromError(env, ret); + } } LogStr("new Aes() = %p\n", aes); @@ -60,6 +67,27 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Aes_mallocNativeStruct_1inter #endif } +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Aes_wc_1AesFree + (JNIEnv* env, jobject this) +{ +#ifndef NO_AES + Aes* aes = NULL; + + aes = (Aes*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, if so stop and return */ + return; + } + + wc_AesFree(aes); + + LogStr("wc_AesFree(aes=%p)\n", aes); +#else + (void)this; + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Aes_native_1set_1key_1internal( JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object, diff --git a/jni/jni_aesctr.c b/jni/jni_aesctr.c index 6300d75b..30941494 100644 --- a/jni/jni_aesctr.c +++ b/jni/jni_aesctr.c @@ -40,6 +40,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_mallocNativeStruct_1in { #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) Aes* aes = NULL; + int ret = 0; aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (aes == NULL) { @@ -47,6 +48,12 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_mallocNativeStruct_1in } else { XMEMSET(aes, 0, sizeof(Aes)); + ret = wc_AesInit(aes, NULL, INVALID_DEVID); + if (ret != 0) { + XFREE(aes, NULL, DYNAMIC_TYPE_TMP_BUFFER); + aes = NULL; + throwWolfCryptExceptionFromError(env, ret); + } } LogStr("new AesCtr() = %p\n", aes); @@ -60,6 +67,27 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_mallocNativeStruct_1in #endif } +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_wc_1AesFree + (JNIEnv* env, jobject this) +{ +#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) + Aes* aes = NULL; + + aes = (Aes*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, if so stop and return */ + return; + } + + wc_AesFree(aes); + + LogStr("wc_AesFree(aes=%p)\n", aes); +#else + (void)this; + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesCtr_native_1set_1key_1internal( JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object) diff --git a/jni/jni_aesecb.c b/jni/jni_aesecb.c index 88847988..3a90d77c 100644 --- a/jni/jni_aesecb.c +++ b/jni/jni_aesecb.c @@ -40,6 +40,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_mallocNativeStruct_1in { #if !defined(NO_AES) && defined(HAVE_AES_ECB) Aes* aes = NULL; + int ret = 0; aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (aes == NULL) { @@ -47,6 +48,12 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_mallocNativeStruct_1in } else { XMEMSET(aes, 0, sizeof(Aes)); + ret = wc_AesInit(aes, NULL, INVALID_DEVID); + if (ret != 0) { + XFREE(aes, NULL, DYNAMIC_TYPE_TMP_BUFFER); + aes = NULL; + throwWolfCryptExceptionFromError(env, ret); + } } LogStr("new AesEcb() = %p\n", aes); @@ -60,6 +67,27 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_mallocNativeStruct_1in #endif } +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_wc_1AesFree + (JNIEnv* env, jobject this) +{ +#if !defined(NO_AES) && defined(HAVE_AES_ECB) + Aes* aes = NULL; + + aes = (Aes*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, if so stop and return */ + return; + } + + wc_AesFree(aes); + + LogStr("wc_AesFree(aes=%p)\n", aes); +#else + (void)this; + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesEcb_native_1set_1key_1internal( JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object, jint opmode) diff --git a/jni/jni_aesofb.c b/jni/jni_aesofb.c index a138a3f8..6379127b 100644 --- a/jni/jni_aesofb.c +++ b/jni/jni_aesofb.c @@ -40,6 +40,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_mallocNativeStruct_1in { #if !defined(NO_AES) && defined(WOLFSSL_AES_OFB) Aes* aes = NULL; + int ret = 0; aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (aes == NULL) { @@ -47,6 +48,12 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_mallocNativeStruct_1in } else { XMEMSET(aes, 0, sizeof(Aes)); + ret = wc_AesInit(aes, NULL, INVALID_DEVID); + if (ret != 0) { + XFREE(aes, NULL, DYNAMIC_TYPE_TMP_BUFFER); + aes = NULL; + throwWolfCryptExceptionFromError(env, ret); + } } LogStr("new AesOfb() = %p\n", aes); @@ -60,6 +67,27 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_mallocNativeStruct_1in #endif } +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_wc_1AesFree + (JNIEnv* env, jobject this) +{ +#if !defined(NO_AES) && defined(WOLFSSL_AES_OFB) + Aes* aes = NULL; + + aes = (Aes*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, if so stop and return */ + return; + } + + wc_AesFree(aes); + + LogStr("wc_AesFree(aes=%p)\n", aes); +#else + (void)this; + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesOfb_native_1set_1key_1internal( JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object, diff --git a/jni/jni_des3.c b/jni/jni_des3.c index 569559c8..20bda775 100644 --- a/jni/jni_des3.c +++ b/jni/jni_des3.c @@ -40,6 +40,7 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Des3_mallocNativeStruct( { #ifndef NO_DES3 Des3* des = NULL; + int ret = 0; des = (Des3*) XMALLOC(sizeof(Des3), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (des == NULL) { @@ -47,6 +48,12 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Des3_mallocNativeStruct( } else { XMEMSET(des, 0, sizeof(Des3)); + ret = wc_Des3Init(des, NULL, INVALID_DEVID); + if (ret != 0) { + XFREE(des, NULL, DYNAMIC_TYPE_TMP_BUFFER); + des = NULL; + throwWolfCryptExceptionFromError(env, ret); + } } LogStr("new Des3() = %p\n", des); @@ -59,6 +66,27 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Des3_mallocNativeStruct( #endif } +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Des3_wc_1Des3Free + (JNIEnv* env, jobject this) +{ +#ifndef NO_DES3 + Des3* des3 = NULL; + + des3 = (Des3*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, if so stop and return */ + return; + } + + wc_Des3Free(des3); + + LogStr("wc_Des3Free(des3=%p)\n", des3); +#else + (void)this; + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Des3_native_1set_1key_1internal( JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object, diff --git a/src/main/java/com/wolfssl/wolfcrypt/Aes.java b/src/main/java/com/wolfssl/wolfcrypt/Aes.java index fb57ca62..902d915f 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Aes.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Aes.java @@ -51,6 +51,7 @@ public class Aes extends BlockCipher { * NativeStruct.java. We wrap calls to these below in order to * synchronize access to native pointer between threads */ private native long mallocNativeStruct_internal() throws OutOfMemoryError; + private native void wc_AesFree(); private native void native_set_key_internal(byte[] key, byte[] iv, int opmode); private native int native_update_internal(int opmode, byte[] input, @@ -58,6 +59,11 @@ private native int native_update_internal(int opmode, byte[] input, private native int native_update_internal(int opmode, ByteBuffer input, int offset, int length, ByteBuffer output, int outputOffset); + @Override + protected void nativeFree() { + wc_AesFree(); + } + /** * Malloc native JNI AES structure * diff --git a/src/main/java/com/wolfssl/wolfcrypt/AesCtr.java b/src/main/java/com/wolfssl/wolfcrypt/AesCtr.java index bd03f2dc..57c84bfd 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/AesCtr.java +++ b/src/main/java/com/wolfssl/wolfcrypt/AesCtr.java @@ -51,6 +51,7 @@ public class AesCtr extends NativeStruct { * NativeStruct.java. We wrap calls to these below in order to * synchronize access to native pointer between threads */ private native long mallocNativeStruct_internal() throws OutOfMemoryError; + private native void wc_AesFree(); private native void native_set_key_internal(byte[] key, byte[] iv); private native int native_update_internal(byte[] input, int offset, int length, byte[] output, int outputOffset); @@ -315,10 +316,14 @@ public synchronized int update(ByteBuffer input, ByteBuffer output) @Override public synchronized void releaseNativeStruct() { synchronized (stateLock) { - if (state != WolfCryptState.RELEASED) { - super.releaseNativeStruct(); - state = WolfCryptState.RELEASED; + if ((state != WolfCryptState.UNINITIALIZED) && + (state != WolfCryptState.RELEASED)) { + synchronized (pointerLock) { + wc_AesFree(); + super.releaseNativeStruct(); + } } + state = WolfCryptState.RELEASED; } } } diff --git a/src/main/java/com/wolfssl/wolfcrypt/AesEcb.java b/src/main/java/com/wolfssl/wolfcrypt/AesEcb.java index a301700e..7d355dff 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/AesEcb.java +++ b/src/main/java/com/wolfssl/wolfcrypt/AesEcb.java @@ -52,6 +52,7 @@ public class AesEcb extends BlockCipher { * NativeStruct.java. We wrap calls to these below in order to * synchronize access to native pointer between threads */ private native long mallocNativeStruct_internal() throws OutOfMemoryError; + private native void wc_AesFree(); private native void native_set_key_internal(byte[] key, byte[] iv, int opmode); private native int native_update_internal(int opmode, byte[] input, @@ -59,6 +60,11 @@ private native int native_update_internal(int opmode, byte[] input, private native int native_update_internal(int opmode, ByteBuffer input, int offset, int length, ByteBuffer output, int outputOffset); + @Override + protected void nativeFree() { + wc_AesFree(); + } + /** * Malloc native AesEcb structure * diff --git a/src/main/java/com/wolfssl/wolfcrypt/AesOfb.java b/src/main/java/com/wolfssl/wolfcrypt/AesOfb.java index d6f0cf83..f5110ed6 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/AesOfb.java +++ b/src/main/java/com/wolfssl/wolfcrypt/AesOfb.java @@ -57,6 +57,7 @@ public class AesOfb extends NativeStruct { * NativeStruct.java. We wrap calls to these below in order to * synchronize access to native pointer between threads */ private native long mallocNativeStruct_internal() throws OutOfMemoryError; + private native void wc_AesFree(); private native void native_set_key_internal(byte[] key, byte[] iv, int opmode); private native int native_update_internal(int opmode, byte[] input, @@ -664,10 +665,14 @@ public synchronized int update(ByteBuffer input, ByteBuffer output) @Override public synchronized void releaseNativeStruct() { synchronized (stateLock) { - if (state != WolfCryptState.RELEASED) { - super.releaseNativeStruct(); - state = WolfCryptState.RELEASED; + if ((state != WolfCryptState.UNINITIALIZED) && + (state != WolfCryptState.RELEASED)) { + synchronized (pointerLock) { + wc_AesFree(); + super.releaseNativeStruct(); + } } + state = WolfCryptState.RELEASED; } } } diff --git a/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java b/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java index f7c36d0e..f6f58ddd 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java +++ b/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java @@ -262,12 +262,23 @@ public synchronized int update(ByteBuffer input, ByteBuffer output) return ret; } + /** + * Zeroize contents of the native structure. Called by releaseNativeStruct() + * before the native structure memory is freed, subclasses override to + * wipe key material. + */ + protected void nativeFree() { + } + @Override public synchronized void releaseNativeStruct() { synchronized (stateLock) { if ((state != WolfCryptState.UNINITIALIZED) && (state != WolfCryptState.RELEASED)) { - super.releaseNativeStruct(); + synchronized (pointerLock) { + nativeFree(); + super.releaseNativeStruct(); + } state = WolfCryptState.RELEASED; } } diff --git a/src/main/java/com/wolfssl/wolfcrypt/Des3.java b/src/main/java/com/wolfssl/wolfcrypt/Des3.java index 608036eb..0558ce1a 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Des3.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Des3.java @@ -43,6 +43,7 @@ public class Des3 extends BlockCipher { /* native JNI methods, internally reach back and grab/use pointer from * NativeStruct.java. We wrap calls to these below in order to * synchronize access to native pointer between threads */ + private native void wc_Des3Free(); private native void native_set_key_internal(byte[] key, byte[] iv, int opmode); private native int native_update_internal(int opmode, byte[] input, @@ -59,6 +60,11 @@ private native int native_update_internal(int opmode, ByteBuffer input, */ protected native long mallocNativeStruct() throws OutOfMemoryError; + @Override + protected void nativeFree() { + wc_Des3Free(); + } + /** * Set native Des3 key *