From c4f15cd8e6dd7fde09f00dd472f506f350f56300 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 14 Oct 2026 15:30:26 -0600 Subject: [PATCH 1/5] F-15081: size DH shared secret buffer to fit p in wc_DhAgree JNI wrapper --- jni/jni_dh.c | 8 +++++ .../com/wolfssl/wolfcrypt/test/DhTest.java | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/jni/jni_dh.c b/jni/jni_dh.c index eb1ac954..5f323ed9 100644 --- a/jni/jni_dh.c +++ b/jni/jni_dh.c @@ -446,6 +446,7 @@ Java_com_wolfssl_wolfcrypt_Dh_wc_1DhAgree( byte* pub = NULL; byte* secret = NULL; word32 privSz = 0, pubSz = 0, secretSz = 0; + word32 pSz = 0, qSz = 0, gSz = 0; jboolean privIsCopy = JNI_FALSE; key = (DhKey*) getNativeStruct(env, this); @@ -463,6 +464,13 @@ Java_com_wolfssl_wolfcrypt_Dh_wc_1DhAgree( * DH_MAX_SIZE is in bits, so convert to bytes and round up if needed. */ secretSz = (DH_MAX_SIZE + 7) / 8; + /* wc_DhAgree() does not bound its output by secretSz, and heap math + * allows p larger than DH_MAX_SIZE, so grow the buffer to fit p */ + if ((key != NULL) && (wc_DhExportParamsRaw(key, NULL, &pSz, NULL, &qSz, + NULL, &gSz) == LENGTH_ONLY_E) && (pSz > secretSz)) { + secretSz = pSz; + } + secret = XMALLOC(secretSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (secret == NULL) { throwOutOfMemoryException( diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java index a5bb26fc..97c90a8a 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/DhTest.java @@ -269,6 +269,37 @@ public void generateDhParamsShouldReturnUsableParams() { } } + /** ffdhe8192 exceeds the 4096-bit DH_MAX_SIZE of heap math builds */ + @Test + public void sharedSecretShouldMatchFfdhe8192() { + Dh alice = new Dh(); + Dh bob = new Dh(); + + try { + try { + byte[][] params = Dh.getNamedDhParams(Dh.WC_FFDHE_8192); + alice.setParams(params[0], params[1]); + bob.setParams(params[0], params[1]); + synchronized (rngLock) { + alice.makeKey(rng); + bob.makeKey(rng); + } + } catch (WolfCryptException e) { + Assume.assumeTrue("8192-bit DH not supported: " + + e.getMessage(), false); + } + + byte[] sharedSecretA = alice.makeSharedSecret(bob); + byte[] sharedSecretB = bob.makeSharedSecret(alice); + + assertNotNull(sharedSecretA); + assertArrayEquals(sharedSecretA, sharedSecretB); + } finally { + alice.releaseNativeStruct(); + bob.releaseNativeStruct(); + } + } + @Test public void exportParamsShouldReturnPAndGWhenKeyHasNoQ() { byte[] p = Util.h2b(DH_P_2048_HEX); From 552d44e5d6eca674c821b397a7053171834013ff Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 14 Oct 2026 15:53:53 -0600 Subject: [PATCH 2/5] F-15030: free native digest contexts on re-init and release via MessageDigest.native_free() --- jni/include/com_wolfssl_wolfcrypt_Md5.h | 8 + jni/include/com_wolfssl_wolfcrypt_Sha.h | 8 + jni/include/com_wolfssl_wolfcrypt_Sha224.h | 8 + jni/include/com_wolfssl_wolfcrypt_Sha256.h | 8 + jni/include/com_wolfssl_wolfcrypt_Sha3.h | 8 + jni/include/com_wolfssl_wolfcrypt_Sha384.h | 8 + jni/include/com_wolfssl_wolfcrypt_Sha512.h | 8 + jni/jni_md5.c | 27 +++ jni/jni_sha.c | 182 ++++++++++++++++++ src/main/java/com/wolfssl/wolfcrypt/Md5.java | 11 ++ .../com/wolfssl/wolfcrypt/MessageDigest.java | 22 ++- src/main/java/com/wolfssl/wolfcrypt/Sha.java | 11 ++ .../java/com/wolfssl/wolfcrypt/Sha224.java | 11 ++ .../java/com/wolfssl/wolfcrypt/Sha256.java | 11 ++ src/main/java/com/wolfssl/wolfcrypt/Sha3.java | 11 ++ .../java/com/wolfssl/wolfcrypt/Sha384.java | 11 ++ .../java/com/wolfssl/wolfcrypt/Sha512.java | 11 ++ .../java/com/wolfssl/wolfcrypt/Shake.java | 23 +-- .../com/wolfssl/wolfcrypt/test/Md5Test.java | 32 +++ .../wolfssl/wolfcrypt/test/Sha224Test.java | 31 +++ .../wolfssl/wolfcrypt/test/Sha256Test.java | 34 ++++ .../wolfssl/wolfcrypt/test/Sha384Test.java | 36 ++++ .../com/wolfssl/wolfcrypt/test/Sha3Test.java | 44 +++++ .../wolfssl/wolfcrypt/test/Sha512Test.java | 38 ++++ .../com/wolfssl/wolfcrypt/test/ShaTest.java | 34 ++++ 25 files changed, 622 insertions(+), 14 deletions(-) diff --git a/jni/include/com_wolfssl_wolfcrypt_Md5.h b/jni/include/com_wolfssl_wolfcrypt_Md5.h index 987e2a0d..3446292b 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Md5.h +++ b/jni/include/com_wolfssl_wolfcrypt_Md5.h @@ -69,6 +69,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Md5_native_1final_1internal__L JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Md5_native_1final_1internal___3B (JNIEnv *, jobject, jbyteArray); +/* + * Class: com_wolfssl_wolfcrypt_Md5 + * Method: native_free_internal + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Md5_native_1free_1internal + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/jni/include/com_wolfssl_wolfcrypt_Sha.h b/jni/include/com_wolfssl_wolfcrypt_Sha.h index c36eb250..bde0027d 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Sha.h +++ b/jni/include/com_wolfssl_wolfcrypt_Sha.h @@ -69,6 +69,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha_native_1final_1internal__L JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha_native_1final_1internal___3B (JNIEnv *, jobject, jbyteArray); +/* + * Class: com_wolfssl_wolfcrypt_Sha + * Method: native_free_internal + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha_native_1free_1internal + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/jni/include/com_wolfssl_wolfcrypt_Sha224.h b/jni/include/com_wolfssl_wolfcrypt_Sha224.h index 8f3deee5..5675b1cd 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Sha224.h +++ b/jni/include/com_wolfssl_wolfcrypt_Sha224.h @@ -69,6 +69,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1final_1internal JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1final_1internal___3B (JNIEnv *, jobject, jbyteArray); +/* + * Class: com_wolfssl_wolfcrypt_Sha224 + * Method: native_free_internal + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1free_1internal + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/jni/include/com_wolfssl_wolfcrypt_Sha256.h b/jni/include/com_wolfssl_wolfcrypt_Sha256.h index 3d1f1a1e..d67ed6db 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Sha256.h +++ b/jni/include/com_wolfssl_wolfcrypt_Sha256.h @@ -69,6 +69,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha256_native_1final_1internal JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha256_native_1final_1internal___3B (JNIEnv *, jobject, jbyteArray); +/* + * Class: com_wolfssl_wolfcrypt_Sha256 + * Method: native_free_internal + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha256_native_1free_1internal + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/jni/include/com_wolfssl_wolfcrypt_Sha3.h b/jni/include/com_wolfssl_wolfcrypt_Sha3.h index 11f9f02a..f07fe27b 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Sha3.h +++ b/jni/include/com_wolfssl_wolfcrypt_Sha3.h @@ -81,6 +81,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1final_1internal__ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1final_1internal___3BI (JNIEnv *, jobject, jbyteArray, jint); +/* + * Class: com_wolfssl_wolfcrypt_Sha3 + * Method: native_free_internal + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1free_1internal + (JNIEnv *, jobject, jint); + #ifdef __cplusplus } #endif diff --git a/jni/include/com_wolfssl_wolfcrypt_Sha384.h b/jni/include/com_wolfssl_wolfcrypt_Sha384.h index 03d5483c..9d2d72bd 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Sha384.h +++ b/jni/include/com_wolfssl_wolfcrypt_Sha384.h @@ -69,6 +69,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha384_native_1final_1internal JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha384_native_1final_1internal___3B (JNIEnv *, jobject, jbyteArray); +/* + * Class: com_wolfssl_wolfcrypt_Sha384 + * Method: native_free_internal + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha384_native_1free_1internal + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/jni/include/com_wolfssl_wolfcrypt_Sha512.h b/jni/include/com_wolfssl_wolfcrypt_Sha512.h index 14c5052e..1b4d2cc6 100644 --- a/jni/include/com_wolfssl_wolfcrypt_Sha512.h +++ b/jni/include/com_wolfssl_wolfcrypt_Sha512.h @@ -69,6 +69,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha512_native_1final_1internal JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha512_native_1final_1internal___3B (JNIEnv *, jobject, jbyteArray); +/* + * Class: com_wolfssl_wolfcrypt_Sha512 + * Method: native_free_internal + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha512_native_1free_1internal + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/jni/jni_md5.c b/jni/jni_md5.c index c7ebfde6..3fcc38de 100644 --- a/jni/jni_md5.c +++ b/jni/jni_md5.c @@ -293,3 +293,30 @@ Java_com_wolfssl_wolfcrypt_Md5_native_1final_1internal___3B( #endif } +JNIEXPORT void JNICALL +Java_com_wolfssl_wolfcrypt_Md5_native_1free_1internal( + JNIEnv* env, jobject this) +{ +#ifndef NO_MD5 + Md5* md5 = (Md5*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (md5 != NULL) { + wc_Md5Free(md5); + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(md5, sizeof(Md5)); + #else + XMEMSET(md5, 0, sizeof(Md5)); + #endif + } + + LogStr("wc_Md5Free(md5=%p)\n", md5); +#else + throwNotCompiledInException(env); +#endif +} + diff --git a/jni/jni_sha.c b/jni/jni_sha.c index 84742a8f..d284946b 100644 --- a/jni/jni_sha.c +++ b/jni/jni_sha.c @@ -418,6 +418,33 @@ Java_com_wolfssl_wolfcrypt_Sha_native_1final_1internal___3B( #endif } +JNIEXPORT void JNICALL +Java_com_wolfssl_wolfcrypt_Sha_native_1free_1internal( + JNIEnv* env, jobject this) +{ +#ifndef NO_SHA + Sha* sha = (Sha*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (sha != NULL) { + wc_ShaFree(sha); + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(sha, sizeof(Sha)); + #else + XMEMSET(sha, 0, sizeof(Sha)); + #endif + } + + LogStr("wc_ShaFree(sha=%p)\n", sha); +#else + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1init_1internal (JNIEnv* env, jobject this) { @@ -664,6 +691,33 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1final_1internal #endif } +JNIEXPORT void JNICALL +Java_com_wolfssl_wolfcrypt_Sha224_native_1free_1internal( + JNIEnv* env, jobject this) +{ +#ifdef WOLFSSL_SHA224 + Sha224* sha = (Sha224*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (sha != NULL) { + wc_Sha224Free(sha); + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(sha, sizeof(Sha224)); + #else + XMEMSET(sha, 0, sizeof(Sha224)); + #endif + } + + LogStr("wc_Sha224Free(sha=%p)\n", sha); +#else + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha256_native_1init_1internal( @@ -884,6 +938,33 @@ Java_com_wolfssl_wolfcrypt_Sha256_native_1final_1internal___3B( #endif } +JNIEXPORT void JNICALL +Java_com_wolfssl_wolfcrypt_Sha256_native_1free_1internal( + JNIEnv* env, jobject this) +{ +#ifndef NO_SHA256 + Sha256* sha = (Sha256*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (sha != NULL) { + wc_Sha256Free(sha); + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(sha, sizeof(Sha256)); + #else + XMEMSET(sha, 0, sizeof(Sha256)); + #endif + } + + LogStr("wc_Sha256Free(sha=%p)\n", sha); +#else + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha384_native_1init_1internal( JNIEnv* env, jobject this) @@ -1102,6 +1183,33 @@ Java_com_wolfssl_wolfcrypt_Sha384_native_1final_1internal___3B( #endif } +JNIEXPORT void JNICALL +Java_com_wolfssl_wolfcrypt_Sha384_native_1free_1internal( + JNIEnv* env, jobject this) +{ +#ifdef WOLFSSL_SHA384 + Sha384* sha = (Sha384*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (sha != NULL) { + wc_Sha384Free(sha); + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(sha, sizeof(Sha384)); + #else + XMEMSET(sha, 0, sizeof(Sha384)); + #endif + } + + LogStr("wc_Sha384Free(sha=%p)\n", sha); +#else + throwNotCompiledInException(env); +#endif +} + JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha512_native_1init_1internal( JNIEnv* env, jobject this) @@ -1321,6 +1429,33 @@ Java_com_wolfssl_wolfcrypt_Sha512_native_1final_1internal___3B( #endif } +JNIEXPORT void JNICALL +Java_com_wolfssl_wolfcrypt_Sha512_native_1free_1internal( + JNIEnv* env, jobject this) +{ +#ifdef WOLFSSL_SHA512 + Sha512* sha = (Sha512*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (sha != NULL) { + wc_Sha512Free(sha); + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(sha, sizeof(Sha512)); + #else + XMEMSET(sha, 0, sizeof(Sha512)); + #endif + } + + LogStr("wc_Sha512Free(sha=%p)\n", sha); +#else + throwNotCompiledInException(env); +#endif +} + JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Sha3_mallocNativeStruct_1internal (JNIEnv* env, jobject this) { @@ -1740,3 +1875,50 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1final_1internal__ #endif } +JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1free_1internal + (JNIEnv* env, jobject this, jint hashType) +{ +#ifdef WOLFSSL_SHA3 + wc_Sha3* sha = NULL; + + sha = (wc_Sha3*) getNativeStruct(env, this); + if ((*env)->ExceptionOccurred(env)) { + /* getNativeStruct may throw exception, prevent throwing another */ + return; + } + + if (sha != NULL) { + switch (hashType) { + case WC_HASH_TYPE_SHA3_224: + wc_Sha3_224_Free(sha); + break; + case WC_HASH_TYPE_SHA3_256: + wc_Sha3_256_Free(sha); + break; + case WC_HASH_TYPE_SHA3_384: + wc_Sha3_384_Free(sha); + break; + case WC_HASH_TYPE_SHA3_512: + wc_Sha3_512_Free(sha); + break; + default: + break; + } + + #if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \ + !defined(WOLFSSL_NO_FORCE_ZERO) + wc_ForceZero(sha, sizeof(wc_Sha3)); + #else + XMEMSET(sha, 0, sizeof(wc_Sha3)); + #endif + } + + LogStr("wc_Sha3_Free(sha=%p)\n", sha); +#else + (void)env; + (void)this; + (void)hashType; + throwNotCompiledInException(env); +#endif +} + diff --git a/src/main/java/com/wolfssl/wolfcrypt/Md5.java b/src/main/java/com/wolfssl/wolfcrypt/Md5.java index 3e12bdd8..6f46b551 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Md5.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Md5.java @@ -48,6 +48,7 @@ private native void native_update_internal(byte[] data, int offset, int len); private native void native_final_internal(ByteBuffer hash, int offset); private native void native_final_internal(byte[] hash); + private native void native_free_internal(); /** * Malloc native JNI Md5 structure @@ -83,6 +84,16 @@ protected void native_init() } } + /** + * Free native WC_MD5 struct resources using wc_Md5Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(); + } + } + /** * Copy existing native WC_MD5 struct (Md5 object) into this one. * Copies structure state using wc_Md5Copy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java b/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java index aba9d0c2..87eb03a8 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java +++ b/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java @@ -39,6 +39,13 @@ public MessageDigest() { } */ protected abstract void native_init(); + /** + * Free resources held by an initialized native structure. + * Does nothing by default, subclasses holding native resources override. + */ + protected void native_free() { + } + /** * Native update * @@ -92,6 +99,13 @@ protected abstract void native_update(ByteBuffer data, int offset, * Initialize object */ public synchronized void init() { + state = WolfCryptState.UNINITIALIZED; + + /* Free resources from a previous init, e.g. after digest() */ + if (getNativeStruct() != NativeStruct.NULL) { + native_free(); + } + /* Allocate native struct pointer from NativeStruct */ initNativeStruct(); @@ -288,7 +302,13 @@ public synchronized void releaseNativeStruct() { /* reset state first, then free */ state = WolfCryptState.UNINITIALIZED; - super.releaseNativeStruct(); + + synchronized (pointerLock) { + if (getNativeStruct() != NativeStruct.NULL) { + native_free(); + } + super.releaseNativeStruct(); + } } } diff --git a/src/main/java/com/wolfssl/wolfcrypt/Sha.java b/src/main/java/com/wolfssl/wolfcrypt/Sha.java index 97ba0f42..18e4a9fe 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Sha.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Sha.java @@ -48,6 +48,7 @@ private native void native_update_internal(byte[] data, int offset, int len); private native void native_final_internal(ByteBuffer hash, int offset); private native void native_final_internal(byte[] hash); + private native void native_free_internal(); /** * Malloc native JNI Sha structure @@ -83,6 +84,16 @@ protected void native_init() } } + /** + * Free native WC_SHA struct resources using wc_ShaFree() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(); + } + } + /** * Copy existing native WC_SHA struct (Sha object) into this one. * Copies structure state using wc_ShaCopy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/Sha224.java b/src/main/java/com/wolfssl/wolfcrypt/Sha224.java index 543f20f6..7df7d5e5 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Sha224.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Sha224.java @@ -48,6 +48,7 @@ private native void native_update_internal(byte[] data, int offset, int len); private native void native_final_internal(ByteBuffer hash, int offset); private native void native_final_internal(byte[] hash); + private native void native_free_internal(); /** * Malloc native JNI Sha224 structure @@ -83,6 +84,16 @@ protected void native_init() } } + /** + * Free native WC_SHA224 struct resources using wc_Sha224Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(); + } + } + /** * Copy existing native WC_SHA224 struct (Sha224 object) into this one. * Copies structure state using wc_Sha224Copy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/Sha256.java b/src/main/java/com/wolfssl/wolfcrypt/Sha256.java index f3ac5397..6b01a7d3 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Sha256.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Sha256.java @@ -48,6 +48,7 @@ private native void native_update_internal(byte[] data, int offset, int len); private native void native_final_internal(ByteBuffer hash, int offset); private native void native_final_internal(byte[] hash); + private native void native_free_internal(); /** * Malloc native JNI Sha256 structure @@ -83,6 +84,16 @@ protected void native_init() } } + /** + * Free native WC_SHA256 struct resources using wc_Sha256Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(); + } + } + /** * Copy existing native WC_SHA256 struct (Sha256 object) into this one. * Copies structure state using wc_Sha256Copy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/Sha3.java b/src/main/java/com/wolfssl/wolfcrypt/Sha3.java index 63244da9..5d0c674d 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Sha3.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Sha3.java @@ -69,6 +69,7 @@ private native void native_update_internal(byte[] data, int offset, private native void native_final_internal(ByteBuffer hash, int offset, int hashType); private native void native_final_internal(byte[] hash, int hashType); + private native void native_free_internal(int hashType); /** * Get the hash type of this Sha3 object @@ -150,6 +151,16 @@ protected void native_init() } } + /** + * Free native wc_Sha3 struct resources using wc_Sha3_XXX_Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(this.hashType); + } + } + /** * Copy existing native wc_Sha3 struct (Sha3 object) into this one. * Copies structure state using wc_Sha3_XXX_Copy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/Sha384.java b/src/main/java/com/wolfssl/wolfcrypt/Sha384.java index c29fc012..8b9f5b32 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Sha384.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Sha384.java @@ -48,6 +48,7 @@ private native void native_update_internal(byte[] data, int offset, int len); private native void native_final_internal(ByteBuffer hash, int offset); private native void native_final_internal(byte[] hash); + private native void native_free_internal(); /** * Malloc native JNI Sha384 structure @@ -83,6 +84,16 @@ protected void native_init() } } + /** + * Free native WC_SHA384 struct resources using wc_Sha384Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(); + } + } + /** * Copy existing native WC_SHA384 struct (Sha384 object) into this one. * Copies structure state using wc_Sha384Copy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/Sha512.java b/src/main/java/com/wolfssl/wolfcrypt/Sha512.java index 484d1457..e5db10be 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Sha512.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Sha512.java @@ -48,6 +48,7 @@ private native void native_update_internal(byte[] data, int offset, int len); private native void native_final_internal(ByteBuffer hash, int offset); private native void native_final_internal(byte[] hash); + private native void native_free_internal(); /** * Malloc native JNI Sha512 structure @@ -83,6 +84,16 @@ protected void native_init() } } + /** + * Free native WC_SHA512 struct resources using wc_Sha512Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(); + } + } + /** * Copy existing native WC_SHA512 struct (Sha512 object) into this one. * Copies structure state using wc_Sha512Copy(). diff --git a/src/main/java/com/wolfssl/wolfcrypt/Shake.java b/src/main/java/com/wolfssl/wolfcrypt/Shake.java index ba8c312c..03561170 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/Shake.java +++ b/src/main/java/com/wolfssl/wolfcrypt/Shake.java @@ -143,6 +143,16 @@ protected void native_init() } } + /** + * Free native wc_Shake struct resources using wc_Shake128/256_Free() + */ + protected void native_free() { + + synchronized (pointerLock) { + native_free_internal(this.hashType); + } + } + /** * Copy existing native wc_Shake struct (Shake object) into this one. * Copies structure state using wc_Shake128/256_Copy(). @@ -286,19 +296,6 @@ public int digestSize() { return this.digestSize; } - @Override - public synchronized void releaseNativeStruct() { - - synchronized (pointerLock) { - /* Zeroize native state, may hold sensitive absorbed data */ - if (getNativeStruct() != NativeStruct.NULL) { - native_free_internal(this.hashType); - } - - super.releaseNativeStruct(); - } - } - /* synchronized so a concurrent releaseNativeStruct() cannot free * the source native struct while native_copy() reads from it */ @Override diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java index 7ab5da56..f09fa99f 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Md5Test.java @@ -243,6 +243,38 @@ public void reuseObject() { md5.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + + Md5 md5 = new Md5(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] data2 = new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }; + byte[] expected = Util.h2b("D05374DC381D9B52806446A71C8E79B1"); + + md5.update(data2); + md5.init(); + md5.update(data); + assertArrayEquals(expected, md5.digest()); + + md5.releaseNativeStruct(); + } + + @Test + public void reuseObjectAfterRelease() { + + Md5 md5 = new Md5(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] expected = Util.h2b("D05374DC381D9B52806446A71C8E79B1"); + + md5.update(data); + assertArrayEquals(expected, md5.digest()); + md5.releaseNativeStruct(); + + md5.update(data); + assertArrayEquals(expected, md5.digest()); + md5.releaseNativeStruct(); + } + @Test public void copyObject() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java index 0e953f9f..43192fa8 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha224Test.java @@ -212,6 +212,37 @@ public void reuseObject() { sha.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + + Sha224 sha = new Sha224(); + byte[] data = dataVector[1].getBytes(); + byte[] expected = Util.h2b(hashVector[1]); + + sha.update(dataVector[2].getBytes()); + sha.init(); + sha.update(data); + assertArrayEquals(expected, sha.digest()); + + sha.releaseNativeStruct(); + } + + @Test + public void reuseObjectAfterRelease() { + + Sha224 sha = new Sha224(); + byte[] data = dataVector[1].getBytes(); + byte[] expected = Util.h2b(hashVector[1]); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + } + @Test public void copyObject() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java index f5b0598a..f9529ba4 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha256Test.java @@ -226,6 +226,40 @@ public void reuseObject() { sha.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + + Sha256 sha = new Sha256(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] data2 = new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }; + byte[] expected = Util.h2b("08BB5E5D6EAAC1049EDE0893D30ED022" + + "B1A4D9B5B48DB414871F51C9CB35283D"); + + sha.update(data2); + sha.init(); + sha.update(data); + assertArrayEquals(expected, sha.digest()); + + sha.releaseNativeStruct(); + } + + @Test + public void reuseObjectAfterRelease() { + + Sha256 sha = new Sha256(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] expected = Util.h2b("08BB5E5D6EAAC1049EDE0893D30ED022" + + "B1A4D9B5B48DB414871F51C9CB35283D"); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + } + @Test public void copyObject() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java index a68ee564..3bc73d33 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha384Test.java @@ -237,6 +237,42 @@ public void reuseObject() { sha.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + + Sha384 sha = new Sha384(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] data2 = new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }; + byte[] expected = Util.h2b("561C16404A1B592406301780C0C2DF6A" + + "A0555F504F35BFBEAC810AE36A343B77" + + "6858C5E0DE56BB79607A34D2F67108F2"); + + sha.update(data2); + sha.init(); + sha.update(data); + assertArrayEquals(expected, sha.digest()); + + sha.releaseNativeStruct(); + } + + @Test + public void reuseObjectAfterRelease() { + + Sha384 sha = new Sha384(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] expected = Util.h2b("561C16404A1B592406301780C0C2DF6A" + + "A0555F504F35BFBEAC810AE36A343B77" + + "6858C5E0DE56BB79607A34D2F67108F2"); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + } + @Test public void copyObject() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java index a3900c23..3c26c3cf 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha3Test.java @@ -53,6 +53,20 @@ public class Sha3Test { private ByteBuffer result = ByteBuffer.allocateDirect(Sha3.DIGEST_SIZE_512); private ByteBuffer expected = ByteBuffer.allocateDirect(Sha3.DIGEST_SIZE_512); + /* NIST FIPS 202 A.1 "abc" digests, indexed like sha3Types */ + private static final int[] sha3Types = new int[] { + Sha3.TYPE_SHA3_224, Sha3.TYPE_SHA3_256, + Sha3.TYPE_SHA3_384, Sha3.TYPE_SHA3_512 + }; + private static final String[] sha3AbcVector = new String[] { + "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b2" + + "98d88cea927ac7f539f1edf228376d25", + "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e" + + "10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0" + }; + @Rule(order = Integer.MIN_VALUE) public TestRule watcher = TimedTestWatcher.create(); @@ -280,6 +294,36 @@ public void reuseObject() { sha.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + byte[] data = Util.h2b("616263"); + + for (int i = 0; i < sha3Types.length; i++) { + Sha3 sha = new Sha3(sha3Types[i]); + sha.update(new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }); + sha.init(); + sha.update(data); + assertArrayEquals(Util.h2b(sha3AbcVector[i]), sha.digest()); + sha.releaseNativeStruct(); + } + } + + @Test + public void reuseObjectAfterRelease() { + byte[] data = Util.h2b("616263"); + + for (int i = 0; i < sha3Types.length; i++) { + Sha3 sha = new Sha3(sha3Types[i]); + sha.update(data); + assertArrayEquals(Util.h2b(sha3AbcVector[i]), sha.digest()); + sha.releaseNativeStruct(); + + sha.update(data); + assertArrayEquals(Util.h2b(sha3AbcVector[i]), sha.digest()); + sha.releaseNativeStruct(); + } + } + @Test public void copyObject() { Sha3 sha = new Sha3(Sha3.TYPE_SHA3_256); diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java b/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java index 0027315b..e7d43538 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/Sha512Test.java @@ -240,6 +240,44 @@ public void reuseObject() { sha.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + + Sha512 sha = new Sha512(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] data2 = new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }; + byte[] expected = Util.h2b("B7B70A0B14D7FA213C6CCD3CBFFC8BB8" + + "F8E11A85F1113B0EB26A00208F2B9B3A" + + "1DD4AAF39962861E16AB062274342A1C" + + "E1F9DBA3654F36FC338245589F296C28"); + + sha.update(data2); + sha.init(); + sha.update(data); + assertArrayEquals(expected, sha.digest()); + + sha.releaseNativeStruct(); + } + + @Test + public void reuseObjectAfterRelease() { + + Sha512 sha = new Sha512(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] expected = Util.h2b("B7B70A0B14D7FA213C6CCD3CBFFC8BB8" + + "F8E11A85F1113B0EB26A00208F2B9B3A" + + "1DD4AAF39962861E16AB062274342A1C" + + "E1F9DBA3654F36FC338245589F296C28"); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + } + @Test public void copyObject() { diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java index 851bf04a..68a3ea67 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/ShaTest.java @@ -225,6 +225,40 @@ public void reuseObject() { sha.releaseNativeStruct(); } + @Test + public void initResetsObjectMidStream() { + + Sha sha = new Sha(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] data2 = new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }; + byte[] expected = Util.h2b("1CF251472D59F8FADEB3AB258E90999D" + + "8491BE19"); + + sha.update(data2); + sha.init(); + sha.update(data); + assertArrayEquals(expected, sha.digest()); + + sha.releaseNativeStruct(); + } + + @Test + public void reuseObjectAfterRelease() { + + Sha sha = new Sha(); + byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }; + byte[] expected = Util.h2b("1CF251472D59F8FADEB3AB258E90999D" + + "8491BE19"); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + + sha.update(data); + assertArrayEquals(expected, sha.digest()); + sha.releaseNativeStruct(); + } + @Test public void copyObject() { From b502e60b83e262d83139250c52e063651201e492 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 14 Oct 2026 16:07:20 -0600 Subject: [PATCH 3/5] F-15031: reset AesCmac with the stored key after doFinal() frees the native context --- .../java/com/wolfssl/wolfcrypt/AesCmac.java | 46 +++++++++++++++---- .../wolfssl/wolfcrypt/test/AesCmacTest.java | 27 +++++++++++ 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/wolfssl/wolfcrypt/AesCmac.java b/src/main/java/com/wolfssl/wolfcrypt/AesCmac.java index 67d8102b..4c95d04e 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/AesCmac.java +++ b/src/main/java/com/wolfssl/wolfcrypt/AesCmac.java @@ -37,6 +37,9 @@ public class AesCmac extends NativeStruct { /** Stored key for reset functionality */ private byte[] key = null; + /** True once doFinal() has freed the native context, until re-keyed */ + private boolean finalized = false; + /** Lock around object state */ protected final Object stateLock = new Object(); @@ -109,6 +112,7 @@ public synchronized void setKey(byte[] key) synchronized (pointerLock) { wc_CmacSetKey(key); } + this.finalized = false; /* Store key for reset, zeroize any prior key clone */ zeroizeKey(); @@ -130,7 +134,7 @@ public synchronized void setKey(byte[] key) public synchronized void update(byte data) throws WolfCryptException, IllegalStateException { - throwIfKeyNotLoaded(); + checkKeyAndRekeyIfFinalized(); synchronized (pointerLock) { wc_CmacUpdate(data); @@ -148,7 +152,7 @@ public synchronized void update(byte data) public synchronized void update(byte[] data) throws WolfCryptException, IllegalStateException { - throwIfKeyNotLoaded(); + checkKeyAndRekeyIfFinalized(); synchronized (pointerLock) { wc_CmacUpdate(data, 0, data.length); @@ -168,7 +172,7 @@ public synchronized void update(byte[] data) public synchronized void update(byte[] data, int offset, int length) throws WolfCryptException, IllegalStateException { - throwIfKeyNotLoaded(); + checkKeyAndRekeyIfFinalized(); synchronized (pointerLock) { wc_CmacUpdate(data, offset, length); @@ -189,7 +193,7 @@ public synchronized void update(ByteBuffer data) int offset = data.position(); int length = data.remaining(); - throwIfKeyNotLoaded(); + checkKeyAndRekeyIfFinalized(); synchronized (pointerLock) { wc_CmacUpdate(data, offset, length); @@ -201,6 +205,9 @@ public synchronized void update(ByteBuffer data) /** * Calculate final AES-CMAC * + * The object can then be reused, it is re-keyed with the stored key on + * the next update() or doFinal(). + * * @return AES-CMAC result as byte array * * @throws WolfCryptException if native operation fails @@ -209,9 +216,11 @@ public synchronized void update(ByteBuffer data) public synchronized byte[] doFinal() throws WolfCryptException, IllegalStateException { - throwIfKeyNotLoaded(); + checkKeyAndRekeyIfFinalized(); synchronized (pointerLock) { + /* wc_CmacFinal() frees the native context, even on error */ + this.finalized = true; return wc_CmacFinal(); } } @@ -219,6 +228,9 @@ public synchronized byte[] doFinal() /** * Calculate final AES-CMAC after processing additional supplied data * + * The object can then be reused, it is re-keyed with the stored key on + * the next update() or doFinal(). + * * @param data input data to update AES-CMAC with * * @return AES-CMAC result as byte array @@ -233,9 +245,7 @@ public synchronized byte[] doFinal(byte[] data) update(data); - synchronized (pointerLock) { - return wc_CmacFinal(); - } + return doFinal(); } /** @@ -258,6 +268,7 @@ public synchronized void reset() /* Re-set the key using the existing stored key */ wc_CmacSetKey(key); } + this.finalized = false; } } @@ -362,6 +373,25 @@ private void throwIfKeyNotLoaded() throws IllegalStateException { } } + /** + * Throw if no key is loaded, then re-key the native context with the + * stored key if doFinal() freed it. + */ + private void checkKeyAndRekeyIfFinalized() + throws WolfCryptException, IllegalStateException { + + synchronized (stateLock) { + throwIfKeyNotLoaded(); + + if (this.finalized) { + synchronized (pointerLock) { + wc_CmacSetKey(this.key); + } + this.finalized = false; + } + } + } + /** * Zeroize stored key. */ diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java index e0ed8b15..6eafacac 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/AesCmacTest.java @@ -270,6 +270,33 @@ public void aesCmacResetAndRekeyMidStreamShouldWork() { assertArrayEquals(expectedBytes, cmac.doFinal(dataBytes)); } + @Test + public void aesCmacShouldBeReusableAfterDoFinal() { + byte[] keyBytes = Util.h2b("2b7e151628aed2a6abf7158809cf4f3c"); + byte[] dataBytes = Util.h2b( + "6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51" + + "30c81c46a35ce411"); + byte[] expectedBytes = Util.h2b("dfa66747de9ae63030ca32611497c827"); + byte[] expectedEmpty = Util.h2b("bb1d6929e95937287fa37d129b756746"); + + AesCmac cmac = new AesCmac(); + cmac.setKey(keyBytes); + cmac.update(dataBytes); + assertArrayEquals(expectedBytes, cmac.doFinal()); + + cmac.update(dataBytes); + assertArrayEquals(expectedBytes, cmac.doFinal()); + + assertArrayEquals(expectedBytes, cmac.doFinal(dataBytes)); + assertArrayEquals(expectedEmpty, cmac.doFinal()); + + cmac.reset(); + assertArrayEquals(expectedBytes, cmac.doFinal(dataBytes)); + + cmac.releaseNativeStruct(); + } + @Test public void aesCmacAlgorithmInfoShouldWork() { try { From 517e77fb1c3a5ecf3148fc6b336bece6b2a0f5b9 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 14 Oct 2026 16:22:52 -0600 Subject: [PATCH 4/5] F-15032: verify default-salt RSA-PSS through wc_RsaPSS_VerifyCheck() so the modulus bits set the salt length --- jni/jni_rsa.c | 67 ++++++++++++------- .../com/wolfssl/wolfcrypt/test/RsaTest.java | 66 ++++++++++++++++++ 2 files changed, 109 insertions(+), 24 deletions(-) diff --git a/jni/jni_rsa.c b/jni/jni_rsa.c index 8fa19d59..40c8ebc6 100644 --- a/jni/jni_rsa.c +++ b/jni/jni_rsa.c @@ -1808,24 +1808,31 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPSS_1Verify( if (ret == 0) { XMEMSET(output, 0, outputSz); - ret = wc_RsaPSS_Verify_ex(signature, signatureSz, output, outputSz, - (enum wc_HashType)hashType, mgf, - saltLen, key); - if (ret > 0) { - /* Now verify the PSS padding with the provided data */ - ret = wc_RsaPSS_CheckPadding_ex(data, dataSz, output, ret, - (enum wc_HashType)hashType, - saltLen, 0); - if (ret == 0) { + if (saltLen == RSA_PSS_SALT_LEN_DEFAULT) { + /* VerifyCheck() sizes the default salt from the modulus bits */ + ret = wc_RsaPSS_VerifyCheck(signature, signatureSz, output, + outputSz, data, dataSz, (enum wc_HashType)hashType, mgf, key); + if (ret > 0) { result = JNI_TRUE; + ret = 0; } - /* PSS decode of a bad signature fails with data dependent error - * codes, all map to false, only MEMORY_E throws */ - else if (ret == MEMORY_E) { - throwWolfCryptExceptionFromError(env, ret); + } + else { + ret = wc_RsaPSS_Verify_ex(signature, signatureSz, output, + outputSz, (enum wc_HashType)hashType, mgf, saltLen, key); + if (ret > 0) { + /* Now verify the PSS padding with the provided data */ + ret = wc_RsaPSS_CheckPadding_ex(data, dataSz, output, ret, + (enum wc_HashType)hashType, saltLen, 0); + if (ret == 0) { + result = JNI_TRUE; + } } } - else if (ret == MEMORY_E) { + + /* PSS decode of a bad signature fails with data dependent error codes, + * all map to false, only MEMORY_E throws */ + if (ret == MEMORY_E) { throwWolfCryptExceptionFromError(env, ret); } } @@ -2041,17 +2048,29 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPSS_1CheckPadding( if (ret == 0) { XMEMSET(pssData, 0, pssDataSz); - /* First decrypt the signature to get PSS padded data */ - ret = wc_RsaPSS_Verify_ex(signature, signatureSz, pssData, pssDataSz, - (enum wc_HashType)hashType, mgf, saltLen, key); - - if (ret > 0) { - pssDataSz = ret; - /* Now check the PSS padding against the digest */ - ret = wc_RsaPSS_CheckPadding_ex(digest, digestSz, pssData, - pssDataSz, (enum wc_HashType)hashType, saltLen, 0); - if (ret == 0) { + if (saltLen == RSA_PSS_SALT_LEN_DEFAULT) { + /* VerifyCheck() sizes the default salt from the modulus bits */ + ret = wc_RsaPSS_VerifyCheck(signature, signatureSz, pssData, + pssDataSz, digest, digestSz, (enum wc_HashType)hashType, mgf, + key); + if (ret > 0) { result = JNI_TRUE; + ret = 0; + } + } + else { + /* First decrypt the signature to get PSS padded data */ + ret = wc_RsaPSS_Verify_ex(signature, signatureSz, pssData, + pssDataSz, (enum wc_HashType)hashType, mgf, saltLen, key); + + if (ret > 0) { + pssDataSz = ret; + /* Now check the PSS padding against the digest */ + ret = wc_RsaPSS_CheckPadding_ex(digest, digestSz, pssData, + pssDataSz, (enum wc_HashType)hashType, saltLen, 0); + if (ret == 0) { + result = JNI_TRUE; + } } } diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java index 84a69d1b..aaa0c62a 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/RsaTest.java @@ -1286,6 +1286,72 @@ public void testRsaPssSignVerify() { key.releaseNativeStruct(); } + @Test + public void testRsaPssDefaultSaltSha512With1024BitKey() { + + Assume.assumeTrue(FeatureDetect.RsaPssEnabled() && + FeatureDetect.Sha512Enabled()); + + int hash = WolfCrypt.WC_HASH_TYPE_SHA512; + int mgf = Rsa.WC_MGF1SHA512; + int saltLen = Rsa.RSA_PSS_SALT_LEN_DEFAULT; + byte[] signature = null; + + Rsa key = new Rsa(); + Rng rng = new Rng(); + rng.init(); + + Sha512 sha512 = new Sha512(); + sha512.update("Everyone gets Friday off.".getBytes()); + byte[] digest = sha512.digest(); + + try { + /* 1024-bit key, wolfSSL certs/1024/client-key.der */ + key.decodePrivateKey(Util + .h2b("3082025c02010002818100bc730ea849f374a2a9ef18a5da559921f9" + + "c8ecb36d48e53535757737ecd161905f3ed9e4d5df94cac1a9d719da" + + "86c9e84dc4613682feabad7e7725bb8d11a5bc623aa838cc39a20466" + + "b4f7f7f3aada4d020ebb5e8d6948dc77c9280e22e96ba426ba4ce8c1" + + "fd4a6f2b1fef8aaef69062e5641eeb2b3c67c8dc2700f6916865a902" + + "030100010281801397eae8387825a25c04ce0d407c31e5c470cd9b82" + + "3b5809863b665fdc3190f14fd5db15ddded73b95933118310e5ea3d6" + + "a21a716e81481c4bcfdb8e7a866132dcfb55c1166d279224458bf1b8" + + "48b14b1dacdedadd8e2fc291fba5a96ef83a6af1fd5018ef9fe7c3ca" + + "78ea56d3d3725b96dd4e064e3ac3d9be72b66507074c01024100fa47" + + "d47a7c923c55ef81f041302da3cf8f1ce6872705700ddf9835d6f18b" + + "382f24b5d084b6794f7129945af0646aace772c6ed4d59983e673af3" + + "742cf9611769024100c0c1820d0cebc62fdc92f99d821a31e9e9f74b" + + "f282871cee166ad11d188270f3c0b62ff6f3f71df18623c84eeb8f56" + + "8e8ff5bff1f72bb5cc3dc657390c1b54410241009d7e05deedf4b7b2" + + "fbfc304b551de32f0147966905cd0e2e2cbd8363b6ab7cb76dca5b64" + + "a7cebe86df3b53de61d21eeba5f637edacab78d94ce755fbd71199c1" + + "024018981829e61e2739702168ac0a2fa172c121869538c65890a057" + + "9cbae3a7b115c8def61bc2612376efb09d1c44be1343396717c89dca" + + "fbf545648b38822cf28102403989e59c195530bab7488c48140ef49f" + + "7e779743e1b419353123759c3b44ad691256ee0061641666d37c742b" + + "15b4a2febf086b1a5d3f9012b105863129dbd9e2")); + key.setRng(rng); + signature = key.rsaPssSign(digest, hash, mgf, saltLen, rng); + } catch (WolfCryptException e) { + Assume.assumeTrue("1024-bit RSA-PSS signing not supported: " + + e.getMessage(), false); + } + + assertTrue(key.rsaPssVerify(signature, digest, hash, mgf, saltLen)); + assertTrue(key.rsaPssCheckPadding(signature, digest, hash, mgf, + saltLen)); + assertTrue(key.rsaPssVerifyWithDigest(signature, digest, digest, + hash, mgf, saltLen)); + + digest[0] ^= 0x01; + assertFalse(key.rsaPssVerify(signature, digest, hash, mgf, saltLen)); + assertFalse(key.rsaPssCheckPadding(signature, digest, hash, mgf, + saltLen)); + + rng.free(); + key.releaseNativeStruct(); + } + @Test public void testRsaPssSaltLengthDiscovery() throws WolfCryptException { From 26132ef22f1e82e27e1433b6830de0c35bd580b1 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Wed, 14 Oct 2026 16:28:14 -0600 Subject: [PATCH 5/5] F-15033: stop PBKDF2 JNI wrapper on GetByteArrayElements failure and keep the pending exception --- jni/jni_pwdbased.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/jni/jni_pwdbased.c b/jni/jni_pwdbased.c index 796d4a80..d33db19f 100644 --- a/jni/jni_pwdbased.c +++ b/jni/jni_pwdbased.c @@ -195,16 +195,24 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 if ((passBuf != NULL) && (passBufLen > 0)) { pass = (byte*)(*env)->GetByteArrayElements(env, passBuf, &passIsCopy); + if ((*env)->ExceptionOccurred(env)) { + ret = MEMORY_E; + } } - if (saltBuf != NULL) { + if ((ret == 0) && (saltBuf != NULL)) { salt = (byte*)(*env)->GetByteArrayElements(env, saltBuf, NULL); + if ((*env)->ExceptionOccurred(env)) { + ret = MEMORY_E; + } } - PRIVATE_KEY_UNLOCK(); - ret = wc_PBKDF2(outKey, pass, passBufLen, salt, sBufLen, - iterations, kLen, hashType); - PRIVATE_KEY_LOCK(); + if (ret == 0) { + PRIVATE_KEY_UNLOCK(); + ret = wc_PBKDF2(outKey, pass, passBufLen, salt, sBufLen, + iterations, kLen, hashType); + PRIVATE_KEY_LOCK(); + } if (ret == 0) { result = (*env)->NewByteArray(env, kLen); if (result != NULL) { @@ -242,6 +250,11 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_Pwdbased_wc_1PBKDF2 (*env)->ReleaseByteArrayElements(env, saltBuf, (jbyte*)salt, JNI_ABORT); } + if ((*env)->ExceptionOccurred(env)) { + /* Leave the pending OutOfMemoryError in place */ + return NULL; + } + if (ret != 0) { throwWolfCryptExceptionFromError(env, ret); return NULL;