Conversation
…ify (F-1120 / F-1121)
…, add test (F-1125)
There was a problem hiding this comment.
Pull request overview
Fixes multiple Fenrir-reported JNI/JCE correctness and security issues across RSA-PSS, ECC private key import, digest copy JNI bindings, and PBE key destruction semantics.
Changes:
- Correct RSA-PSS size/error handling and remove an unsafe/unused “VerifyInline” JNI path.
- Ensure ECC private raw import propagates curve ID to native import and add a regression test.
- Improve safety by zeroizing encoded key material on destroy and adding missing JNI early-returns after throwing.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/java/com/wolfssl/wolfcrypt/test/EccTest.java | Adds a regression test to verify curve ID is set on raw private key import. |
| src/main/java/com/wolfssl/wolfcrypt/Rsa.java | Removes the unused native wc_RsaPSS_VerifyInline declaration. |
| src/main/java/com/wolfssl/provider/jce/WolfCryptPBEKey.java | Extends destroy() to zeroize the cached encoded key bytes and updates Javadoc. |
| jni/jni_sha.c | Adds missing return after throwing on NULL copy parameters in multiple SHA copy methods. |
| jni/jni_md5.c | Adds missing return after throwing on NULL copy parameters for MD5. |
| jni/jni_rsa.c | Fixes RSA-PSS encrypt-size error handling and removes VerifyInline JNI implementation. |
| jni/jni_ecc.c | Passes curveId into the native ECC private key import function. |
| jni/include/com_wolfssl_wolfcrypt_Rsa.h | Removes the wc_RsaPSS_VerifyInline JNI declaration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| encSz = wc_RsaEncryptSize(key); | ||
| if (encSz < 0) { | ||
| ret = encSz; | ||
| } | ||
| else { | ||
| signatureSz = (word32)encSz; | ||
| } |
There was a problem hiding this comment.
wc_RsaEncryptSize() returning 0 is currently treated as success, which can propagate a zero-length signatureSz/pssDataSz and lead to invalid allocations/behavior downstream. If 0 is not a valid RSA modulus size (typical), change the check to treat encSz <= 0 as an error (mirroring the prior intent while still avoiding the unsigned-cast bug).
| encSz = wc_RsaEncryptSize(key); | ||
| if (encSz < 0) { | ||
| ret = encSz; | ||
| } | ||
| else { | ||
| pssDataSz = (word32)encSz; | ||
| } |
There was a problem hiding this comment.
wc_RsaEncryptSize() returning 0 is currently treated as success, which can propagate a zero-length signatureSz/pssDataSz and lead to invalid allocations/behavior downstream. If 0 is not a valid RSA modulus size (typical), change the check to treat encSz <= 0 as an error (mirroring the prior intent while still avoiding the unsigned-cast bug).
| key.importPrivateRaw(prvKey, "secp256r1"); | ||
|
|
||
| /* verify curve was set correctly after import */ | ||
| assertEquals(ECC_SECP256R1, key.getCurveId()); | ||
|
|
||
| /* try invalid curve name, expect failure */ | ||
| try { | ||
| Ecc key2 = new Ecc(); | ||
| key2.importPrivateRaw(prvKey, "BADCURVE"); | ||
| fail("importPrivateRaw with invalid curve should fail"); | ||
| } catch (WolfCryptException e) { | ||
| /* expected */ | ||
| } | ||
|
|
||
| key.releaseNativeStruct(); |
There was a problem hiding this comment.
releaseNativeStruct() is only called at the end of the test, so if an assertion or the invalid-curve block fails/throws unexpectedly, the native struct may leak and destabilize the JVM/test suite. Wrap the test body in try { ... } finally { key.releaseNativeStruct(); } (and similarly ensure any secondary Ecc instances are released in a finally when created).
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(ECC_SECP256R1, key.getCurveId()); | |
| /* try invalid curve name, expect failure */ | |
| try { | |
| Ecc key2 = new Ecc(); | |
| key2.importPrivateRaw(prvKey, "BADCURVE"); | |
| fail("importPrivateRaw with invalid curve should fail"); | |
| } catch (WolfCryptException e) { | |
| /* expected */ | |
| } | |
| key.releaseNativeStruct(); | |
| try { | |
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(ECC_SECP256R1, key.getCurveId()); | |
| /* try invalid curve name, expect failure */ | |
| Ecc key2 = null; | |
| try { | |
| key2 = new Ecc(); | |
| key2.importPrivateRaw(prvKey, "BADCURVE"); | |
| fail("importPrivateRaw with invalid curve should fail"); | |
| } catch (WolfCryptException e) { | |
| /* expected */ | |
| } finally { | |
| if (key2 != null) { | |
| key2.releaseNativeStruct(); | |
| } | |
| } | |
| } finally { | |
| key.releaseNativeStruct(); | |
| } |
| key.importPrivateRaw(prvKey, "secp256r1"); | ||
|
|
||
| /* verify curve was set correctly after import */ | ||
| assertEquals(ECC_SECP256R1, key.getCurveId()); | ||
|
|
||
| /* try invalid curve name, expect failure */ | ||
| try { | ||
| Ecc key2 = new Ecc(); | ||
| key2.importPrivateRaw(prvKey, "BADCURVE"); | ||
| fail("importPrivateRaw with invalid curve should fail"); | ||
| } catch (WolfCryptException e) { | ||
| /* expected */ | ||
| } | ||
|
|
||
| key.releaseNativeStruct(); |
There was a problem hiding this comment.
releaseNativeStruct() is only called at the end of the test, so if an assertion or the invalid-curve block fails/throws unexpectedly, the native struct may leak and destabilize the JVM/test suite. Wrap the test body in try { ... } finally { key.releaseNativeStruct(); } (and similarly ensure any secondary Ecc instances are released in a finally when created).
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(ECC_SECP256R1, key.getCurveId()); | |
| /* try invalid curve name, expect failure */ | |
| try { | |
| Ecc key2 = new Ecc(); | |
| key2.importPrivateRaw(prvKey, "BADCURVE"); | |
| fail("importPrivateRaw with invalid curve should fail"); | |
| } catch (WolfCryptException e) { | |
| /* expected */ | |
| } | |
| key.releaseNativeStruct(); | |
| try { | |
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(ECC_SECP256R1, key.getCurveId()); | |
| /* try invalid curve name, expect failure */ | |
| Ecc key2 = null; | |
| try { | |
| key2 = new Ecc(); | |
| key2.importPrivateRaw(prvKey, "BADCURVE"); | |
| fail("importPrivateRaw with invalid curve should fail"); | |
| } catch (WolfCryptException e) { | |
| /* expected */ | |
| } finally { | |
| if (key2 != null) { | |
| key2.releaseNativeStruct(); | |
| } | |
| } | |
| } finally { | |
| key.releaseNativeStruct(); | |
| } |
| /* ECC_SECP256R1 curve id from wolfSSL ecc.h */ | ||
| int ECC_SECP256R1 = 7; | ||
|
|
||
| Ecc key = new Ecc(); | ||
| key.importPrivateRaw(prvKey, "secp256r1"); | ||
|
|
||
| /* verify curve was set correctly after import */ | ||
| assertEquals(ECC_SECP256R1, key.getCurveId()); |
There was a problem hiding this comment.
Hardcoding ECC_SECP256R1 = 7 makes the test fragile if the underlying native enum/IDs change. Prefer referencing a Java-side constant (if available), or derive the expected value via an existing library mapping/helper that translates curve name to ID so the test verifies behavior without duplicating native header values.
| /* ECC_SECP256R1 curve id from wolfSSL ecc.h */ | |
| int ECC_SECP256R1 = 7; | |
| Ecc key = new Ecc(); | |
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(ECC_SECP256R1, key.getCurveId()); | |
| Ecc key = new Ecc(); | |
| int initialCurveId = key.getCurveId(); | |
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertNotEquals(initialCurveId, key.getCurveId()); |
| /* ECC_SECP256R1 curve id from wolfSSL ecc.h */ | ||
| int ECC_SECP256R1 = 7; | ||
|
|
||
| Ecc key = new Ecc(); | ||
| key.importPrivateRaw(prvKey, "secp256r1"); | ||
|
|
||
| /* verify curve was set correctly after import */ | ||
| assertEquals(ECC_SECP256R1, key.getCurveId()); |
There was a problem hiding this comment.
Hardcoding ECC_SECP256R1 = 7 makes the test fragile if the underlying native enum/IDs change. Prefer referencing a Java-side constant (if available), or derive the expected value via an existing library mapping/helper that translates curve name to ID so the test verifies behavior without duplicating native header values.
| /* ECC_SECP256R1 curve id from wolfSSL ecc.h */ | |
| int ECC_SECP256R1 = 7; | |
| Ecc key = new Ecc(); | |
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(ECC_SECP256R1, key.getCurveId()); | |
| /* Expected curve id for secp256r1 from wolfSSL Ecc Java API */ | |
| int expectedCurveId = Ecc.ECC_SECP256R1; | |
| Ecc key = new Ecc(); | |
| key.importPrivateRaw(prvKey, "secp256r1"); | |
| /* verify curve was set correctly after import */ | |
| assertEquals(expectedCurveId, key.getCurveId()); |
Summary
This PR fixes 7 JNI/JCE issues identified by Fenrir.
and CheckPadding functions (F-1120 / F-1121)