Merge pull request #264 from cconlon/fenrirAug20_2

wolfCrypt JNI/JCE hardening fixes
master
Ruby Martin 2026-09-17 13:53:34 -05:00 committed by GitHub
commit a1f4cfac71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 401 additions and 27 deletions

View File

@ -20,8 +20,9 @@ verify_sha256() {
elif command -v shasum >/dev/null; then
actual=$(shasum -a 256 "$file" | awk '{print $1}')
else
echo "Warning: no sha256sum or shasum available, skipping hash verification"
return 0
echo "Error: no sha256sum or shasum found, refusing unverified $file"
rm -f "$file"
return 1
fi
if [ "$actual" != "$expected" ]; then
@ -40,6 +41,13 @@ download_bc_jars() {
local lib_dir="$LIB_DIR"
local bc_url="https://repo1.maven.org/maven2/org/bouncycastle"
# Require a SHA-256 tool before any download, JARs are only added to the
# classpath after hash verification
if ! command -v sha256sum >/dev/null && ! command -v shasum >/dev/null; then
echo "failed (no sha256sum or shasum for hash verification)"
return 1
fi
echo -n "Downloading Bouncy Castle JARs (version $bc_version)... "
mkdir -p "$lib_dir" || {
echo "failed (cannot create $lib_dir)"

View File

@ -99,6 +99,7 @@ Java_com_wolfssl_wolfcrypt_Aes_native_1set_1key_1internal(
byte* key = NULL;
byte* iv = NULL;
word32 keySz = 0;
word32 ivSz = 0;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
@ -109,10 +110,15 @@ Java_com_wolfssl_wolfcrypt_Aes_native_1set_1key_1internal(
key = getByteArray(env, key_object);
iv = getByteArray(env, iv_object);
keySz = getByteArrayLength(env, key_object);
ivSz = getByteArrayLength(env, iv_object);
ret = (!aes || !key) /* iv is optional */
? BAD_FUNC_ARG
: wc_AesSetKey(aes, key, keySz, iv, opmode);
/* IV optional. If provided, reject under/oversized array. */
if (!aes || !key || (iv != NULL && ivSz != AES_BLOCK_SIZE)) {
ret = BAD_FUNC_ARG;
}
else {
ret = wc_AesSetKey(aes, key, keySz, iv, opmode);
}
if (ret != 0)
throwWolfCryptExceptionFromError(env, ret);

View File

@ -98,6 +98,7 @@ Java_com_wolfssl_wolfcrypt_AesCtr_native_1set_1key_1internal(
byte* key = NULL;
byte* iv = NULL;
word32 keySz = 0;
word32 ivSz = 0;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
@ -108,8 +109,10 @@ Java_com_wolfssl_wolfcrypt_AesCtr_native_1set_1key_1internal(
key = getByteArray(env, key_object);
iv = getByteArray(env, iv_object);
keySz = getByteArrayLength(env, key_object);
ivSz = getByteArrayLength(env, iv_object);
if (aes == NULL || key == NULL || iv == NULL) {
/* wc_AesSetKey reads AES_BLOCK_SIZE IV bytes, reject a short array */
if (aes == NULL || key == NULL || iv == NULL || ivSz != AES_BLOCK_SIZE) {
ret = BAD_FUNC_ARG;
}

View File

@ -99,6 +99,7 @@ Java_com_wolfssl_wolfcrypt_AesOfb_native_1set_1key_1internal(
byte* key = NULL;
byte* iv = NULL;
word32 keySz = 0;
word32 ivSz = 0;
(void)opmode;
aes = (Aes*) getNativeStruct(env, this);
@ -110,8 +111,10 @@ Java_com_wolfssl_wolfcrypt_AesOfb_native_1set_1key_1internal(
key = getByteArray(env, key_object);
iv = getByteArray(env, iv_object);
keySz = getByteArrayLength(env, key_object);
ivSz = getByteArrayLength(env, iv_object);
if (aes == NULL || key == NULL || iv == NULL) {
/* wc_AesSetKey reads AES_BLOCK_SIZE IV bytes, reject a short array */
if (aes == NULL || key == NULL || iv == NULL || ivSz != AES_BLOCK_SIZE) {
ret = BAD_FUNC_ARG;
}

View File

@ -72,6 +72,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Chacha_wc_1Chacha_1setIV
int ret = 0;
ChaCha* chacha = NULL;
byte* iv = NULL;
word32 ivSz = 0;
chacha = (ChaCha*)(uintptr_t)getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
@ -79,8 +80,9 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Chacha_wc_1Chacha_1setIV
return;
}
iv = getByteArray(env, iv_object);
ivSz = getByteArrayLength(env, iv_object);
if (chacha == NULL || iv == NULL) {
if (chacha == NULL || iv == NULL || ivSz != CHACHA_IV_BYTES) {
ret = BAD_FUNC_ARG;
}
@ -107,13 +109,16 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Chacha_wc_1Chacha_1setKey
ChaCha* chacha = NULL;
byte* key = NULL;
word32 keySz = 0;
jboolean keyIsCopy = JNI_FALSE;
chacha = (ChaCha*)(uintptr_t)getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, prevent throwing another */
return;
}
key = getByteArray(env, key_object);
if (key_object != NULL) {
key = (byte*)(*env)->GetByteArrayElements(env, key_object, &keyIsCopy);
}
keySz = getByteArrayLength(env, key_object);
if (chacha == NULL || key == NULL) {
@ -129,6 +134,14 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Chacha_wc_1Chacha_1setKey
LogStr("wc_Chacha_SetKey(chacha=%p) = %d\n", chacha, ret);
if (key != NULL && keyIsCopy == JNI_TRUE) {
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
!defined(WOLFSSL_NO_FORCE_ZERO)
wc_ForceZero(key, keySz);
#else
XMEMSET(key, 0, keySz);
#endif
}
releaseByteArray(env, key_object, key, JNI_ABORT);
#else
throwNotCompiledInException(env);

View File

@ -98,6 +98,7 @@ Java_com_wolfssl_wolfcrypt_Des3_native_1set_1key_1internal(
byte* key = NULL;
byte* iv = NULL;
word32 keySz = 0;
word32 ivSz = 0;
jboolean keyIsCopy = JNI_FALSE;
des = (Des3*) getNativeStruct(env, this);
@ -109,10 +110,15 @@ Java_com_wolfssl_wolfcrypt_Des3_native_1set_1key_1internal(
key = getByteArrayIsCopy(env, key_object, &keyIsCopy);
keySz = getByteArrayLength(env, key_object);
iv = getByteArray(env, iv_object);
ivSz = getByteArrayLength(env, iv_object);
ret = (!des || !key) /* iv is optional */
? BAD_FUNC_ARG
: wc_Des3_SetKey(des, key, iv, opmode);
if (!des || !key || keySz != DES3_KEY_SIZE ||
(iv != NULL && ivSz != DES_BLOCK_SIZE)) {
ret = BAD_FUNC_ARG;
}
else {
ret = wc_Des3_SetKey(des, key, iv, opmode);
}
if (ret != 0)
throwWolfCryptExceptionFromError(env, ret);

View File

@ -767,13 +767,16 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1import_1p
wc_MlDsaKey* key = NULL;
byte* in = NULL;
word32 inLen = 0;
jboolean inIsCopy = JNI_FALSE;
key = (wc_MlDsaKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
return;
}
in = getByteArray(env, in_object);
if (in_object != NULL) {
in = (byte*)(*env)->GetByteArrayElements(env, in_object, &inIsCopy);
}
inLen = getByteArrayLength(env, in_object);
if (key == NULL || in == NULL) {
@ -789,6 +792,9 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1import_1p
LogStr("wc_MlDsaKey_ImportPrivRaw(key=%p) = %d\n", key, ret);
if (in != NULL && inIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(in, inLen);
}
releaseByteArray(env, in_object, in, JNI_ABORT);
#else
(void)env;
@ -1132,13 +1138,17 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1make_1key
wc_MlDsaKey* key = NULL;
byte* seed = NULL;
word32 seedLen = 0;
jboolean seedIsCopy = JNI_FALSE;
key = (wc_MlDsaKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
return;
}
seed = getByteArray(env, seed_object);
if (seed_object != NULL) {
seed = (byte*)(*env)->GetByteArrayElements(env, seed_object,
&seedIsCopy);
}
seedLen = getByteArrayLength(env, seed_object);
/* Native API takes no seed length, seed must be exactly 32 bytes */
@ -1158,6 +1168,9 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1make_1key
LogStr("wc_MlDsaKey_MakeKeyFromSeed(key=%p) = %d\n", key, ret);
if (seed != NULL && seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
#else
(void)env;
@ -1305,6 +1318,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
word32 msgLen = 0;
word32 seedLen = 0;
word32 sigLen = 0;
jboolean seedIsCopy = JNI_FALSE;
key = (wc_MlDsaKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
@ -1328,7 +1342,8 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
}
if (seed_object != NULL) {
seed = getByteArray(env, seed_object);
seed = (byte*)(*env)->GetByteArrayElements(env, seed_object,
&seedIsCopy);
seedLen = getByteArrayLength(env, seed_object);
}
@ -1345,6 +1360,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
releaseByteArray(env, msg_object, msg, JNI_ABORT);
}
if (seed != NULL) {
if (seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
}
return NULL;
@ -1409,6 +1427,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
releaseByteArray(env, msg_object, msg, JNI_ABORT);
}
if (seed_object != NULL) {
if (seed != NULL && seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
}
#else
@ -1439,6 +1460,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
word32 hashLen = 0;
word32 seedLen = 0;
word32 sigLen = 0;
jboolean seedIsCopy = JNI_FALSE;
key = (wc_MlDsaKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
@ -1462,7 +1484,8 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
}
if (seed_object != NULL) {
seed = getByteArray(env, seed_object);
seed = (byte*)(*env)->GetByteArrayElements(env, seed_object,
&seedIsCopy);
seedLen = getByteArrayLength(env, seed_object);
}
@ -1479,6 +1502,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
releaseByteArray(env, hash_object, hash, JNI_ABORT);
}
if (seed != NULL) {
if (seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
}
return NULL;
@ -1543,6 +1569,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1sig
releaseByteArray(env, hash_object, hash, JNI_ABORT);
}
if (seed_object != NULL) {
if (seed != NULL && seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
}
#else
@ -1673,6 +1702,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1MlDsaKey_1Sign
word32 muLen = 0;
word32 seedLen = 0;
word32 sigLen = 0;
jboolean seedIsCopy = JNI_FALSE;
key = (wc_MlDsaKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
@ -1690,7 +1720,8 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1MlDsaKey_1Sign
}
if (seed_object != NULL) {
seed = getByteArray(env, seed_object);
seed = (byte*)(*env)->GetByteArrayElements(env, seed_object,
&seedIsCopy);
seedLen = getByteArrayLength(env, seed_object);
}
@ -1703,6 +1734,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1MlDsaKey_1Sign
releaseByteArray(env, mu_object, mu, JNI_ABORT);
}
if (seed != NULL) {
if (seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
}
return NULL;
@ -1758,6 +1792,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1MlDsaKey_1Sign
releaseByteArray(env, mu_object, mu, JNI_ABORT);
}
if (seed_object != NULL) {
if (seed != NULL && seedIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(seed, seedLen);
}
releaseByteArray(env, seed_object, seed, JNI_ABORT);
}
#else
@ -1859,13 +1896,17 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1import_1k
byte* pub = NULL;
word32 privLen = 0;
word32 pubLen = 0;
jboolean privIsCopy = JNI_FALSE;
key = (wc_MlDsaKey*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
return;
}
priv = getByteArray(env, priv_object);
if (priv_object != NULL) {
priv = (byte*)(*env)->GetByteArrayElements(env, priv_object,
&privIsCopy);
}
privLen = getByteArrayLength(env, priv_object);
pub = getByteArray(env, pub_object);
pubLen = getByteArrayLength(env, pub_object);
@ -1883,6 +1924,9 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_MlDsa_wc_1dilithium_1import_1k
LogStr("wc_MlDsaKey_ImportKey(key=%p) = %d\n", key, ret);
if (priv != NULL && privIsCopy == JNI_TRUE) {
MLDSA_FORCE_ZERO(priv, privLen);
}
releaseByteArray(env, priv_object, priv, JNI_ABORT);
releaseByteArray(env, pub_object, pub, JNI_ABORT);
#else

View File

@ -1016,7 +1016,7 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPrivateDecrypt(
RsaKey* key = NULL;
byte* ciphertext = NULL;
byte* output = NULL;
word32 size = 0, outputSz = 0;
word32 size = 0, outputSz = 0, outputBufSz = 0;
int encSz = 0;
key = (RsaKey*) getNativeStruct(env, this);
@ -1041,6 +1041,9 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPrivateDecrypt(
ret = BAD_FUNC_ARG;
} else {
outputSz = (word32)encSz;
/* Keep full allocation size for zeroization, outputSz shrinks
* to plaintext len after decrypt */
outputBufSz = outputSz;
}
}
@ -1081,9 +1084,9 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPrivateDecrypt(
if (output != NULL) {
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
!defined(WOLFSSL_NO_FORCE_ZERO)
wc_ForceZero(output, outputSz);
wc_ForceZero(output, outputBufSz);
#else
XMEMSET(output, 0, outputSz);
XMEMSET(output, 0, outputBufSz);
#endif
XFREE(output, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
@ -1207,7 +1210,7 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPrivateDecrypt_1ex(
RsaKey* key = NULL;
byte* ciphertext = NULL;
byte* output = NULL;
word32 size = 0, outputSz = 0;
word32 size = 0, outputSz = 0, outputBufSz = 0;
int encSz = 0;
key = (RsaKey*) getNativeStruct(env, this);
@ -1237,6 +1240,9 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPrivateDecrypt_1ex(
ret = BAD_FUNC_ARG;
} else {
outputSz = (word32)encSz;
/* Keep full allocation size for zeroization, outputSz shrinks to
* the plaintext len after decrypt */
outputBufSz = outputSz;
}
}
@ -1277,9 +1283,9 @@ Java_com_wolfssl_wolfcrypt_Rsa_wc_1RsaPrivateDecrypt_1ex(
if (output != NULL) {
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
!defined(WOLFSSL_NO_FORCE_ZERO)
wc_ForceZero(output, outputSz);
wc_ForceZero(output, outputBufSz);
#else
XMEMSET(output, 0, outputSz);
XMEMSET(output, 0, outputBufSz);
#endif
XFREE(output, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}

View File

@ -1748,8 +1748,20 @@ public class WolfCryptCipher extends CipherSpi {
tmpIn = Arrays.copyOfRange(tmpIn, 0,
tmpIn.length - this.gcmTagLen);
tmpOut = this.aesCcm.decrypt(tmpIn, this.iv, tag,
aad);
try {
tmpOut = this.aesCcm.decrypt(tmpIn, this.iv,
tag, aad);
} catch (WolfCryptException e) {
/* Convert to AEADBadTagException */
if (e.getCode() ==
WolfCryptError.AES_CCM_AUTH_E.getCode()) {
/* Authentication check fail */
throw new AEADBadTagException(
e.getMessage());
}
throw e;
}
}
}
else if (cipherMode == CipherMode.WC_ECB) {

View File

@ -295,6 +295,9 @@ public class WolfCryptDHKeyFactory extends KeyFactorySpi {
"Private key value must be positive");
}
/* Reject a prime below the minimum wolfCrypt will generate */
checkDhPrimeSize(keySpec.getP());
try {
/* Create DHParameterSpec from p and g */
DHParameterSpec paramSpec = new DHParameterSpec(
@ -311,6 +314,26 @@ public class WolfCryptDHKeyFactory extends KeyFactorySpi {
}
}
/**
* Enforce a minimum DH prime size on imported keys.
*
* wolfCrypt does not generate DH keys below DH_MIN_SIZE, so accepting a
* smaller prime on import would allow a group weaker than any wolfJCE
* produces. Reject those at the KeyFactory boundary.
*
* @param p DH prime modulus to check
*
* @throws InvalidKeySpecException if p has fewer than DH_MIN_SIZE bits
*/
private static void checkDhPrimeSize(BigInteger p)
throws InvalidKeySpecException {
if (p.bitLength() < Dh.DH_MIN_SIZE) {
throw new InvalidKeySpecException(
"DH prime must be at least " + Dh.DH_MIN_SIZE + " bits");
}
}
/**
* Private helper method for generating DHPublicKey from
* X509EncodedKeySpec.
@ -403,6 +426,9 @@ public class WolfCryptDHKeyFactory extends KeyFactorySpi {
"Public key out of valid range: must satisfy 1 < Y < p-1");
}
/* Reject a prime below the minimum wolfCrypt will generate */
checkDhPrimeSize(keySpec.getP());
try {
/* Create DHParameterSpec from p and g */
DHParameterSpec paramSpec = new DHParameterSpec(

View File

@ -207,6 +207,12 @@ public class WolfCryptDHPrivateKey implements DHPrivateKey, Destroyable {
p = new BigInteger(1, pBytes);
idx += pLen;
/* Reject a prime below the minimum wolfCrypt will generate */
if (p.bitLength() < Dh.DH_MIN_SIZE) {
throw new IllegalArgumentException(
"DH prime must be at least " + Dh.DH_MIN_SIZE + " bits");
}
/* g INTEGER */
if (derData[idx++] != 0x02) {
throw new IllegalArgumentException(

View File

@ -250,6 +250,12 @@ public class WolfCryptDHPublicKey implements DHPublicKey, Destroyable {
"must satisfy 1 < Y < p-1");
}
/* Reject a prime below the minimum wolfCrypt will generate */
if (p.bitLength() < Dh.DH_MIN_SIZE) {
throw new IllegalArgumentException(
"DH prime must be at least " + Dh.DH_MIN_SIZE + " bits");
}
/* Store extracted values */
this.publicValue = publicVal;
this.paramSpec = new DHParameterSpec(p, g);

View File

@ -182,7 +182,10 @@ public class Hmac extends NativeStruct {
this.type = type;
/* Save copy of key[] into this.key */
/* Save copy of key[] into this.key, zero old one first */
if (this.key != null) {
Arrays.fill(this.key, (byte)0);
}
this.key = new byte[key.length];
System.arraycopy(key, 0, this.key, 0, key.length);
}

View File

@ -3053,6 +3053,69 @@ public class WolfCryptCipherTest {
}
}
/**
* AES-CCM decrypt failure should throw AEADBadTagException.
*/
@Test
public void testAesCcmBadTagExceptionRegression()
throws NoSuchProviderException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, InvalidAlgorithmParameterException,
BadPaddingException {
if (!enabledJCEAlgos.contains("AES/CCM/NoPadding")) {
/* skip if AES-CCM is not enabled */
return;
}
byte[] key = new byte[] {
(byte)0x2b, (byte)0x7e, (byte)0x15, (byte)0x16,
(byte)0x28, (byte)0xae, (byte)0xd2, (byte)0xa6,
(byte)0xab, (byte)0xf7, (byte)0x15, (byte)0x88,
(byte)0x09, (byte)0xcf, (byte)0x4f, (byte)0x3c
};
byte[] nonce = new byte[] {
(byte)0x00, (byte)0x01, (byte)0x02, (byte)0x03,
(byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07,
(byte)0x08, (byte)0x09, (byte)0x0a, (byte)0x0b
};
byte[] plaintext = new byte[] {
(byte)0x48, (byte)0x65, (byte)0x6c, (byte)0x6c,
(byte)0x6f, (byte)0x20, (byte)0x57, (byte)0x6f,
(byte)0x72, (byte)0x6c, (byte)0x64, (byte)0x21
};
Cipher cipher = Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec ccmSpec = new GCMParameterSpec(128, nonce);
/* First encrypt to get valid ciphertext */
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ccmSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
/* Corrupt the authentication tag (last 16 bytes) */
byte[] corruptedCiphertext = ciphertext.clone();
int tagStart = corruptedCiphertext.length - 16;
for (int i = tagStart; i < corruptedCiphertext.length; i++) {
corruptedCiphertext[i] = (byte)0xFF;
}
/* Attempt to decrypt with corrupted tag, should throw
* AEADBadTagException */
cipher.init(Cipher.DECRYPT_MODE, keySpec, ccmSpec);
try {
cipher.doFinal(corruptedCiphertext);
fail("Expected AEADBadTagException for corrupted CCM tag");
} catch (AEADBadTagException e) {
/* Expected */
} catch (Exception e) {
fail("Expected AEADBadTagException but got: " +
e.getClass().getSimpleName() + " - " + e.getMessage());
}
}
@Test
public void testAesEcbNoPadding()
throws NoSuchProviderException, NoSuchAlgorithmException,

View File

@ -32,6 +32,7 @@ import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
@ -53,6 +54,8 @@ import org.junit.Test;
import com.wolfssl.provider.jce.WolfCryptProvider;
import java.lang.reflect.Field;
import com.wolfssl.provider.jce.WolfCryptDHPublicKey;
import com.wolfssl.provider.jce.WolfCryptDHPrivateKey;
import com.wolfssl.wolfcrypt.Dh;
import com.wolfssl.wolfcrypt.FeatureDetect;
import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
@ -443,6 +446,68 @@ public class WolfCryptDHKeyFactoryTest {
}
}
/**
* A prime below Dh.DH_MIN_SIZE, the floor wolfCrypt uses when generating
* DH keys, must be rejected on import so the KeyFactory doesn't accept a
* group weaker than one wolfJCE produces.
*/
@Test
public void testWeakPrimeRejected() throws Exception {
Assume.assumeTrue(FeatureDetect.DhEnabled());
KeyFactory kf = KeyFactory.getInstance("DH", "wolfJCE");
/* Prime one bit below the enforced minimum, this is not a real
* DH prime but only the bit length is checked at intake */
BigInteger weakP = BigInteger.ONE.shiftLeft(Dh.DH_MIN_SIZE - 2);
BigInteger g = BigInteger.valueOf(2);
BigInteger y = BigInteger.valueOf(3);
BigInteger x = BigInteger.valueOf(3);
try {
kf.generatePublic(new DHPublicKeySpec(y, weakP, g));
fail("weak DH prime should be rejected on public key import");
} catch (InvalidKeySpecException e) {
/* expected */
}
try {
kf.generatePrivate(new DHPrivateKeySpec(x, weakP, g));
fail("weak DH prime should be rejected on private key import");
} catch (InvalidKeySpecException e) {
/* expected */
}
/* Build weak-prime DER via the raw key constructors (which do not
* validate) and confirm the X.509 and PKCS#8 DER import paths and
* translateKey all reject it */
DHParameterSpec weakParams = new DHParameterSpec(weakP, g);
byte[] pubDer = new WolfCryptDHPublicKey(y, weakParams).getEncoded();
byte[] privDer = new WolfCryptDHPrivateKey(x, weakParams).getEncoded();
try {
kf.generatePublic(new X509EncodedKeySpec(pubDer));
fail("weak DH prime should be rejected on X.509 import");
} catch (InvalidKeySpecException e) {
/* expected */
}
try {
kf.generatePrivate(new PKCS8EncodedKeySpec(privDer));
fail("weak DH prime should be rejected on PKCS#8 import");
} catch (InvalidKeySpecException e) {
/* expected */
}
try {
kf.translateKey(new WolfCryptDHPublicKey(y, weakParams));
fail("weak DH prime should be rejected on translateKey");
} catch (InvalidKeyException e) {
/* expected */
}
}
@Test
public void testInvalidKeySpecs() throws Exception {

View File

@ -125,6 +125,21 @@ public class AesCtrTest {
/* test must throw */
}
/* wc_AesSetKey reads AES block size iv bytes, reject a short iv */
try {
aesCtr.setKey(KEY_128, new byte[1]);
fail("undersized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
try {
aesCtr.setKey(KEY_128, new byte[AesCtr.BLOCK_SIZE + 1]);
fail("oversized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
aesCtr.setKey(KEY_128, IV_128);
aesCtr.releaseNativeStruct();

View File

@ -135,6 +135,21 @@ public class AesOfbTest {
/* test must throw */
}
/* wc_AesSetKey reads AES block size iv bytes, reject a short iv */
try {
aesOfb.setKey(KEY_128, new byte[1]);
fail("undersized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
try {
aesOfb.setKey(KEY_128, new byte[AesOfb.BLOCK_SIZE + 1]);
fail("oversized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
aesOfb.setKey(KEY_128, IV_128);
aesOfb.releaseNativeStruct();

View File

@ -98,6 +98,22 @@ public class AesTest {
/* test must throw */
}
/* iv is optional, but a non-null iv must be the block size,
* wc_AesSetKey reads AES_BLOCK_SIZE bytes */
try {
aes.setKey(KEY, new byte[1], Aes.ENCRYPT_MODE);
fail("undersized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
try {
aes.setKey(KEY, new byte[Aes.BLOCK_SIZE + 1], Aes.ENCRYPT_MODE);
fail("oversized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
aes.setKey(KEY, IV, Aes.ENCRYPT_MODE);
aes.releaseNativeStruct();

View File

@ -132,6 +132,22 @@ public class ChachaTest {
/* test must throw */
}
/* IV shorter than 12 bytes should be rejected */
try {
chacha.setIV(new byte[4]);
fail("IV shorter than 12 bytes should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
/* An IV longer than 12 bytes is not a valid ChaCha nonce */
try {
chacha.setIV(new byte[16]);
fail("IV longer than 12 bytes should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
chacha.setIV(IV);
chacha.releaseNativeStruct();
}

View File

@ -99,6 +99,48 @@ public class Des3Test {
}
}
@Test
public void checkSetKeyParams() {
byte[] key = Util.h2b(
"e61a38548694f1fd8cef251c518cc70bb613751c1ce52aa8");
byte[] iv = Util.h2b("48a8ceb8551fd4ad");
Des3 des = new Des3();
/* wc_Des3_SetKey reads full 24 byte key, reject a short/long key */
try {
des.setKey(new byte[1], iv, Des3.ENCRYPT_MODE);
fail("undersized key should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
try {
des.setKey(new byte[Des3.KEY_SIZE + 1], iv, Des3.ENCRYPT_MODE);
fail("oversized key should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
/* IV is optional, but non-null iv must be the block size */
try {
des.setKey(key, new byte[1], Des3.ENCRYPT_MODE);
fail("undersized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
try {
des.setKey(key, new byte[Des3.BLOCK_SIZE + 1], Des3.ENCRYPT_MODE);
fail("oversized iv should be rejected.");
} catch (WolfCryptException e) {
/* test must throw */
}
des.setKey(key, iv, Des3.ENCRYPT_MODE);
des.releaseNativeStruct();
}
@Test(expected=ShortBufferException.class)
public void updateShouldMatchUsingByteByffer() throws ShortBufferException {
String[] keys = new String[] {