Merge pull request #256 from cconlon/fenrirAug13

Fenrir fixes for AES-GCM/GMAC and RSA-PSS JNI wrappers
pull/278/head
Ruby Martin 2026-08-18 15:49:24 -05:00 committed by GitHub
commit a969ccbeea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 96 additions and 6 deletions

View File

@ -200,6 +200,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmEncrypt
/* in may be null, users might only pass in AAD to generate tag */
if (authTagSz > AES_BLOCK_SIZE || iv == NULL || ivSz == 0 ||
(inLen != 0 && in == NULL) ||
((authTagSz > 0) && (authTag == NULL)) ||
((authInSz > 0) && (authIn == NULL))) {
ret = BAD_FUNC_ARG;

View File

@ -219,7 +219,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_wc_1Gmac(
byte* iv = NULL;
byte* authIn = NULL;
byte* authTag = NULL;
word32 keySz, ivSz, authInSz, authTagSz;
word32 keySz = 0, ivSz = 0, authInSz = 0, authTagSz = 0;
key = getByteArray(env, key_object);
iv = getByteArray(env, iv_object);
@ -294,7 +294,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_wc_1GmacVerify(
byte* iv = NULL;
byte* authIn = NULL;
byte* authTag = NULL;
word32 keySz, ivSz, authInSz, authTagSz;
word32 keySz = 0, ivSz = 0, authInSz = 0, authTagSz = 0;
key = getByteArray(env, key_object);
iv = getByteArray(env, iv_object);

View File

@ -1725,6 +1725,10 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPSS_1VerifyCheck(
}
}
if (ret < 0) {
throwWolfCryptExceptionFromError(env, ret);
}
if (ret == 0) {
XMEMSET(output, 0, outputSz);
@ -1737,6 +1741,11 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPSS_1VerifyCheck(
if (ret > 0) {
result = JNI_TRUE;
}
/* 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 {
/* Non-default salt length */
@ -1749,6 +1758,12 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPSS_1VerifyCheck(
if (ret == 0) {
result = JNI_TRUE;
}
else if (ret == MEMORY_E) {
throwWolfCryptExceptionFromError(env, ret);
}
}
else if (ret == MEMORY_E) {
throwWolfCryptExceptionFromError(env, ret);
}
}
}

View File

@ -206,15 +206,21 @@ public class AesGmac extends NativeStruct {
* @param authIn data that was authenticated
* @param authTag authentication tag to verify
*
* @return true if verification succeeds, false otherwise
* @return true if verification succeeds, false if the authentication
* tag does not match
*
* @throws WolfCryptException if native operation fails
* @throws WolfCryptException if the native operation fails for any
* reason other than authentication tag mismatch
*/
public static synchronized boolean verify(byte[] key, byte[] iv,
byte[] authIn, byte[] authTag) throws WolfCryptException {
int ret = wc_GmacVerify(key, iv, authIn, authTag);
if ((ret != 0) && (ret != WolfCryptError.AES_GCM_AUTH_E.getCode())) {
throw new WolfCryptException(ret);
}
return (ret == 0);
}

View File

@ -921,9 +921,11 @@ public class Rsa extends NativeStruct {
* @param mgf mask generation function (WC_MGF1SHA256 for MGF1 with SHA-256)
* @param saltLen salt length in bytes, or special value
*
* @return true if signature is valid, false otherwise
* @return true if signature is valid, false if the signature does not
* verify against the provided digest
*
* @throws WolfCryptException if native operation fails
* @throws WolfCryptException on invalid arguments or memory
* allocation failure
* @throws IllegalStateException if public key has not been set, if object
* fails to initialize, or if releaseNativeStruct() has been
* called and object has been released.

View File

@ -133,6 +133,34 @@ public class AesGmacTest {
assertFalse(shouldFail);
}
@Test
public void testVerifyInvalidArgsShouldThrow() {
if (!FeatureDetect.AesGmacEnabled()) {
/* skip test if AES-GMAC is not compiled in native wolfCrypt */
return;
}
byte[] iv = new byte[12];
byte[] authIn = new byte[16];
byte[] authTag = new byte[Aes.BLOCK_SIZE];
/* null key is an operational error, not a tag mismatch */
try {
AesGmac.verify(null, iv, authIn, authTag);
fail("verify() should have thrown for null key");
} catch (WolfCryptException e) {
/* expected */
}
/* invalid key length is an operational error, not a tag mismatch */
try {
AesGmac.verify(new byte[5], iv, authIn, authTag);
fail("verify() should have thrown for invalid key length");
} catch (WolfCryptException e) {
/* expected */
}
}
@Test
public void testAes128GmacTestVector2() {
if (!FeatureDetect.AesGmacEnabled()) {

View File

@ -1379,6 +1379,44 @@ public class RsaTest {
assertTrue("RSA-PSS check verification failed with " +
"RSA_PSS_SALT_LEN_DEFAULT", verified);
/* Operational errors must throw, not report false */
try {
key.rsaPssVerifyWithDigest(null, message, digest,
WolfCrypt.WC_HASH_TYPE_SHA256, Rsa.WC_MGF1SHA256, 32);
fail("rsaPssVerifyWithDigest should have thrown for " +
"null signature");
} catch (WolfCryptException e) {
/* expected */
}
/* Corrupted signatures must report false, not throw. PSS
* decode of garbage fails with position dependent error
* codes, exercise first, middle and last bytes */
signature = key.rsaPssSign(digest,
WolfCrypt.WC_HASH_TYPE_SHA256, Rsa.WC_MGF1SHA256, 32, rng);
int[] flipPos = new int[] { 0, signature.length / 2,
signature.length - 1 };
for (int pos : flipPos) {
byte[] badSig = signature.clone();
badSig[pos] ^= (byte)0x80;
verified = key.rsaPssVerifyWithDigest(badSig, message,
digest, WolfCrypt.WC_HASH_TYPE_SHA256,
Rsa.WC_MGF1SHA256, 32);
assertFalse("corrupted RSA-PSS signature verified, " +
"flipped byte " + pos, verified);
}
/* Mismatched digest must report false, not throw */
signature = key.rsaPssSign(digest,
WolfCrypt.WC_HASH_TYPE_SHA256, Rsa.WC_MGF1SHA256, 32, rng);
byte[] wrongDigest = digest.clone();
wrongDigest[0] ^= 1;
verified = key.rsaPssVerifyWithDigest(signature, message,
wrongDigest, WolfCrypt.WC_HASH_TYPE_SHA256,
Rsa.WC_MGF1SHA256, 32);
assertFalse("RSA-PSS signature verified with wrong digest",
verified);
} catch (Exception e) {
fail("RSA-PSS check verification test failed: " + e.getMessage());
}