From 836223f7a39c5e56d38016929f501788cd7c4e76 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Mon, 5 Jun 2023 13:12:45 -0600 Subject: [PATCH 1/3] JCE: add support for Cipher AES/CBC/PKCS5Padding support --- README_JCE.md | 1 + jni/jni_aes.c | 14 +- .../wolfssl/provider/jce/WolfCryptCipher.java | 164 +++- .../provider/jce/WolfCryptProvider.java | 2 + .../com/wolfssl/wolfcrypt/BlockCipher.java | 117 +++ .../jce/test/WolfCryptCipherTest.java | 875 ++++++++++++++++-- .../com/wolfssl/wolfcrypt/test/AesTest.java | 101 +- 7 files changed, 1153 insertions(+), 121 deletions(-) diff --git a/README_JCE.md b/README_JCE.md index ac8db06..fe1a89b 100644 --- a/README_JCE.md +++ b/README_JCE.md @@ -38,6 +38,7 @@ The JCE provider currently supports the following algorithms: Cipher Class AES/CBC/NoPadding + AES/CBC/PKCS5Padding DESede/CBC/NoPadding RSA/ECB/PKCS1Padding diff --git a/jni/jni_aes.c b/jni/jni_aes.c index 0752c26..88562ab 100644 --- a/jni/jni_aes.c +++ b/jni/jni_aes.c @@ -36,20 +36,24 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_Aes_mallocNativeStruct( JNIEnv* env, jobject this) { - jlong ret = 0; + void* ret = NULL; #ifndef NO_AES - ret = (jlong) XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER); + ret = (void*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (!ret) + if (ret == NULL) { throwOutOfMemoryException(env, "Failed to allocate Aes object"); + } + else { + XMEMSET(ret, 0, sizeof(Aes)); + } - LogStr("new Aes() = %p\n", (void*)ret); + LogStr("new Aes() = %p\n", ret); #else throwNotCompiledInException(env); #endif - return ret; + return (jlong)ret; } JNIEXPORT void JNICALL diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java index 1a2e76f..0f33aa6 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java @@ -70,7 +70,8 @@ public class WolfCryptCipher extends CipherSpi { enum PaddingType { WC_NONE, - WC_PKCS1 + WC_PKCS1, + WC_PKCS5 } enum OpMode { @@ -204,6 +205,16 @@ public class WolfCryptCipher extends CipherSpi { if (debug.DEBUG) log("set padding to PKCS1Padding"); } + + } else if (padding.equals("PKCS5Padding")) { + + if (cipherType == CipherType.WC_AES) { + paddingType = PaddingType.WC_PKCS5; + supported = 1; + + if (debug.DEBUG) + log("set padding to PKCS5Padding"); + } } if (supported == 0) { @@ -218,16 +229,44 @@ public class WolfCryptCipher extends CipherSpi { } @Override - protected int engineGetOutputSize(int inputLen) { + protected int engineGetOutputSize(int inputLen) + throws IllegalStateException { int size = 0; switch (this.cipherType) { case WC_AES: + if (paddingType == PaddingType.WC_NONE) { + /* wolfCrypt expects input to be padded by application to + * block size, thus output is same size as input */ + size = inputLen; + } + else if (paddingType == PaddingType.WC_PKCS5) { + size = buffered.length + inputLen; + size += Aes.getPKCS7PadSize(size, Aes.BLOCK_SIZE); + } + else { + throw new IllegalStateException( + "Unsupported padding mode for Cipher Aes"); + } + + break; + case WC_DES3: - /* wolfCrypt expects input to be padded by application to - * block size, thus output is same size as input */ - size = inputLen; + if (paddingType == PaddingType.WC_NONE) { + /* wolfCrypt expects input to be padded by application to + * block size, thus output is same size as input */ + size = inputLen; + } + else if (paddingType == PaddingType.WC_PKCS5) { + size = buffered.length + inputLen; + size += Des3.getPKCS7PadSize(size, Des3.BLOCK_SIZE); + } + else { + throw new IllegalStateException( + "Unsupported padding mode for Cipher Des3"); + } + break; case WC_RSA: @@ -474,8 +513,10 @@ public class WolfCryptCipher extends CipherSpi { int blocks = 0; int remaining = 0; - byte tmpOut[] = null; - byte tmpIn[] = null; + int bytesToProcess = 0; + byte[] output = null; + byte[] tmpIn = null; + byte[] tmpBuf = null; if (input == null || len < 0) throw new IllegalArgumentException("Null input buffer or len < 0"); @@ -485,48 +526,61 @@ public class WolfCryptCipher extends CipherSpi { return null; } - if ((cipherType == CipherType.WC_RSA) || - ((buffered.length + len) < blockSize)) { - /* buffer for short inputs, or RSA */ + if (len > 0) { + /* add input bytes to buffered */ tmpIn = new byte[buffered.length + len]; System.arraycopy(buffered, 0, tmpIn, 0, buffered.length); System.arraycopy(input, inputOffset, tmpIn, buffered.length, len); buffered = tmpIn; - return null; } - /* do update on block size multiples only */ - blocks = (buffered.length + len) / blockSize; - remaining = (buffered.length + len) % blockSize; + /* keep buffered data if RSA or data is less than block size */ + if (cipherType == CipherType.WC_RSA || + buffered.length < blockSize) { + return new byte[0]; + } - tmpIn = new byte[blocks * blockSize]; - System.arraycopy(buffered, 0, tmpIn, 0, buffered.length); - System.arraycopy(input, inputOffset, tmpIn, buffered.length, - len - remaining); + /* calculate blocks and partial non-block size remaining */ + blocks = buffered.length / blockSize; + remaining = buffered.length % blockSize; + bytesToProcess = blocks * blockSize; + + /* if PKCS#5/7 padding, and decrypting, hold on to last block for + * padding check in wolfCryptFinal() */ + if (paddingType == PaddingType.WC_PKCS5 && + direction == OpMode.WC_DECRYPT) { + bytesToProcess -= blockSize; + } + + /* not enough data to process yet return until more or final */ + if (bytesToProcess == 0) { + return new byte[0]; + } + + tmpIn = new byte[bytesToProcess]; + System.arraycopy(buffered, 0, tmpIn, 0, bytesToProcess); /* buffer remaining non-block size input, or reset */ - buffered = new byte[remaining]; - if (remaining > 0) { - System.arraycopy(input, inputOffset + (len - remaining), - buffered, 0, remaining); - } + tmpBuf = new byte[buffered.length - bytesToProcess]; + System.arraycopy(buffered, bytesToProcess, tmpBuf, 0, tmpBuf.length); + buffered = tmpBuf; - /* process tmp[] */ + /* process tmpIn[] */ switch (this.cipherType) { case WC_AES: - tmpOut = this.aes.update(tmpIn, 0, tmpIn.length); + output = this.aes.update(tmpIn, 0, tmpIn.length); /* truncate */ - tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length); + output = Arrays.copyOfRange(output, 0, tmpIn.length); break; case WC_DES3: - tmpOut = this.des3.update(tmpIn, 0, tmpIn.length); + output = this.des3.update(tmpIn, 0, tmpIn.length); /* truncate */ - tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length); + output = Arrays.copyOfRange(output, 0, tmpIn.length); break; @@ -534,7 +588,12 @@ public class WolfCryptCipher extends CipherSpi { throw new RuntimeException("Unsupported algorithm type"); }; - return tmpOut; + if (output == null) { + /* For interop compatibility, return empty byte array */ + output = new byte[0]; + } + + return output; } private byte[] wolfCryptFinal(byte[] input, int inputOffset, int len) @@ -546,7 +605,11 @@ public class WolfCryptCipher extends CipherSpi { totalSz = buffered.length + len; - if (isBlockCipher() && (totalSz % blockSize != 0)) { + if (isBlockCipher() && + (this.direction == OpMode.WC_DECRYPT || + (this.direction == OpMode.WC_ENCRYPT && + this.paddingType != PaddingType.WC_PKCS5)) && + (totalSz % blockSize != 0)) { throw new IllegalBlockSizeException( "Input length not multiple of " + blockSize + " bytes"); } @@ -554,8 +617,21 @@ public class WolfCryptCipher extends CipherSpi { /* do final encrypt over totalSz */ tmpIn = new byte[totalSz]; System.arraycopy(buffered, 0, tmpIn, 0, buffered.length); - if (input != null && len > 0) + if (input != null && len > 0) { System.arraycopy(input, inputOffset, tmpIn, buffered.length, len); + } + + /* add padding if encrypting and PKCS5 padding is used. PKCS#5 padding + * is treated the same as PKCS#7 padding here, using each algorithm's + * specific block size */ + if (this.direction == OpMode.WC_ENCRYPT && + this.paddingType == PaddingType.WC_PKCS5) { + if (this.cipherType == CipherType.WC_AES) { + tmpIn = Aes.padPKCS7(tmpIn, Aes.BLOCK_SIZE); + } else if (this.cipherType == CipherType.WC_DES3) { + tmpIn = Des3.padPKCS7(tmpIn, Des3.BLOCK_SIZE); + } + } switch (this.cipherType) { @@ -565,6 +641,12 @@ public class WolfCryptCipher extends CipherSpi { /* truncate */ tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length); + /* strip PKCS#5/PKCS#7 padding if required */ + if (this.direction == OpMode.WC_DECRYPT && + this.paddingType == PaddingType.WC_PKCS5) { + tmpOut = Aes.unPadPKCS7(tmpOut, Aes.BLOCK_SIZE); + } + break; case WC_DES3: @@ -573,6 +655,12 @@ public class WolfCryptCipher extends CipherSpi { /* truncate */ tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length); + /* strip PKCS#5/PKCS#7 padding if required */ + if (this.direction == OpMode.WC_DECRYPT && + this.paddingType == PaddingType.WC_PKCS5) { + tmpOut = Des3.unPadPKCS7(tmpOut, Des3.BLOCK_SIZE); + } + break; case WC_RSA: @@ -793,6 +881,19 @@ public class WolfCryptCipher extends CipherSpi { super(CipherType.WC_AES, CipherMode.WC_CBC, PaddingType.WC_NONE); } } + + /** + * Class for AES-CBC with PKCS#5 padding + */ + public static final class wcAESCBCPKCS5Padding extends WolfCryptCipher { + /** + * Create new wcAESCBCPkcs5Padding object + */ + public wcAESCBCPKCS5Padding() { + super(CipherType.WC_AES, CipherMode.WC_CBC, PaddingType.WC_PKCS5); + } + } + /** * Class for DES-EDE-CBC with no padding */ @@ -804,6 +905,7 @@ public class WolfCryptCipher extends CipherSpi { super(CipherType.WC_DES3, CipherMode.WC_CBC, PaddingType.WC_NONE); } } + /** * Class for RSA-ECB with PKCS1 padding */ diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index 0833992..410e84f 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -121,6 +121,8 @@ public final class WolfCryptProvider extends Provider { /* Cipher */ put("Cipher.AES/CBC/NoPadding", "com.wolfssl.provider.jce.WolfCryptCipher$wcAESCBCNoPadding"); + put("Cipher.AES/CBC/PKCS5Padding", + "com.wolfssl.provider.jce.WolfCryptCipher$wcAESCBCPKCS5Padding"); put("Cipher.DESede/CBC/NoPadding", "com.wolfssl.provider.jce.WolfCryptCipher$wcDESedeCBCNoPadding"); diff --git a/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java b/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java index 831ed69..53286fb 100644 --- a/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java +++ b/src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java @@ -21,6 +21,7 @@ package com.wolfssl.wolfcrypt; +import java.util.Arrays; import java.nio.ByteBuffer; import javax.crypto.ShortBufferException; @@ -192,4 +193,120 @@ public abstract class BlockCipher extends NativeStruct { state = WolfCryptState.UNINITIALIZED; setNativeStruct(NULL); } + + /** + * Return number of PKCS#7 pad bytes required given input size. + * + * @param inputSize size to calculate needed PKCS#7 pad bytes + * @param blockSize Block size of algorithm being used + * + * @return Number of PKCS#7 pad bytes that would be appended to an input + * of size inputSize. + */ + public static int getPKCS7PadSize(int inputSize, int blockSize) { + + int padSz = 0; + + if (inputSize == 0 || blockSize == 0) { + throw new WolfCryptException( + "Input or block size is 0"); + } + + padSz = blockSize - (inputSize % blockSize); + + return padSz; + } + + /** + * Pad input data with PKCS#7 padding. + * + * @param in Input data to be padded + * @param blockSize Block size of algorithm being used + * + * @return Byte array which includes PKCS#7 padding on end + * + * @throws WolfCryptException if input is null, zero length, + * or blockSize is invalid + */ + public static byte[] padPKCS7(byte[] in, int blockSize) + throws WolfCryptException { + + int padSz = 0; + byte[] padded = null; + + if (in == null) { + throw new WolfCryptException( + "Input array is null"); + } + + if (blockSize == 0) { + throw new WolfCryptException("Block size is 0"); + } + + padSz = blockSize - (in.length % blockSize); + padded = new byte[in.length + padSz]; + + System.arraycopy(in, 0, padded, 0, in.length); + + try { + Arrays.fill(padded, in.length, padded.length, (byte)(padSz & 0xff)); + } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) { + throw new WolfCryptException(e); + } + + return padded; + } + + /** + * Unpad PKCS#7-padded data. + * + * @param in Input data which includes PKCS#7 padding on end + * @param blockSize Block size of algorithm being used + * + * @return Byte array with PKCS#7 padding removed + * + * @throws WolfCryptException if input is null, zero length, + * or blockSize is invalid + */ + public static byte[] unPadPKCS7(byte[] in, int blockSize) { + + byte padValue = 0; + byte[] unpadded = null; + boolean valid = true; + + if (in == null || in.length == 0) { + throw new WolfCryptException( + "Input array is null or zero length"); + } + + if (blockSize == 0) { + throw new WolfCryptException("Block size is 0"); + } + + padValue = in[in.length - 1]; + + /* verify pad value is less than or equal to block size */ + if (padValue > (byte)blockSize) { + throw new WolfCryptException( + "Invalid pad value, larger than block size"); + } + + /* verify pad bytes are consistent */ + for (int i = in.length; i > in.length - padValue; i--) { + if (in[i - 1] != padValue) { + valid = false; + } + } + + unpadded = new byte[in.length - padValue]; + System.arraycopy(in, 0, unpadded, 0, in.length - padValue); + + if (!valid) { + throw new WolfCryptException( + "Invalid PKCS#7 padding, pad bytes not consistent"); + } + + return unpadded; + } } + diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java index df30756..a29f2b4 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java @@ -58,10 +58,14 @@ public class WolfCryptCipherTest { /* all supported algos from wolfJCE provider, if enabled */ private static String supportedJCEAlgos[] = { "AES/CBC/NoPadding", + "AES/CBC/PKCS5Padding", "DESede/CBC/NoPadding", "RSA/ECB/PKCS1Padding" }; + /* JCE provider to run below tests against */ + private static final String jceProvider = "wolfJCE"; + /* populated with all enabled algos (some could have been compiled out) */ private static ArrayList enabledJCEAlgos = new ArrayList(); @@ -74,15 +78,16 @@ public class WolfCryptCipherTest { throws NoSuchProviderException, NoSuchPaddingException { /* install wolfJCE provider at runtime */ - Security.addProvider(new WolfCryptProvider()); + Security.insertProviderAt(new WolfCryptProvider(), 1); - Provider p = Security.getProvider("wolfJCE"); + Provider p = Security.getProvider(jceProvider); assertNotNull(p); /* populate enabledJCEAlgos to test */ for (int i = 0; i < supportedJCEAlgos.length; i++) { try { - Cipher c = Cipher.getInstance(supportedJCEAlgos[i], "wolfJCE"); + Cipher c = Cipher.getInstance( + supportedJCEAlgos[i], jceProvider); enabledJCEAlgos.add(supportedJCEAlgos[i]); } catch (NoSuchAlgorithmException e) { @@ -92,6 +97,7 @@ public class WolfCryptCipherTest { /* fill expected block size HashMap */ expectedBlockSizes.put("AES/CBC/NoPadding", 16); + expectedBlockSizes.put("AES/CBC/PKCS5Padding", 16); expectedBlockSizes.put("DESede/CBC/NoPadding", 8); expectedBlockSizes.put("RSA/ECB/PKCS1Padding", 0); } @@ -105,13 +111,13 @@ public class WolfCryptCipherTest { /* try to get all available options we expect to have */ for (int i = 0; i < enabledJCEAlgos.size(); i++) { - cipher = Cipher.getInstance(enabledJCEAlgos.get(i), "wolfJCE"); + cipher = Cipher.getInstance(enabledJCEAlgos.get(i), jceProvider); } /* getting a garbage algorithm should throw * a NoSuchAlgorithmException */ try { - cipher = Cipher.getInstance("NotValid", "wolfJCE"); + cipher = Cipher.getInstance("NotValid", jceProvider); fail("Cipher.getInstance should throw NoSuchAlgorithmException " + "when given bad algorithm value"); @@ -127,7 +133,7 @@ public class WolfCryptCipherTest { Cipher cipher; for (int i = 0; i < enabledJCEAlgos.size(); i++) { - cipher = Cipher.getInstance(enabledJCEAlgos.get(i), "wolfJCE"); + cipher = Cipher.getInstance(enabledJCEAlgos.get(i), jceProvider); if (cipher.getBlockSize() != expectedBlockSizes.get((enabledJCEAlgos.get(i)))) { @@ -181,7 +187,7 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", jceProvider); for (int i = 0; i < vectors.length; i++) { @@ -238,7 +244,7 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", jceProvider); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); @@ -246,11 +252,14 @@ public class WolfCryptCipherTest { cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); tmp = cipher.update(Arrays.copyOfRange(input, 0, 4)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 4, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 8, 12)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 12, 16)); assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -263,11 +272,14 @@ public class WolfCryptCipherTest { cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); tmp = cipher.update(Arrays.copyOfRange(expected, 0, 4)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(expected, 4, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(expected, 8, 12)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(expected, 12, 16)); assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -282,7 +294,8 @@ public class WolfCryptCipherTest { for (int i = 1; i < input.length + 1; i++) { tmp = cipher.update(Arrays.copyOfRange(input, i-1, i)); if ((i % 16) != 0) { - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); } else { assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -298,9 +311,10 @@ public class WolfCryptCipherTest { for (int i = 1; i < expected.length + 1; i++) { tmp = cipher.update(Arrays.copyOfRange(expected, i-1, i)); - if ((i % 16) != 0) - assertArrayEquals(tmp, null); - else { + if ((i % 16) != 0) { + assertNotNull(tmp); + assertEquals(tmp.length, 0); + } else { assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); } @@ -347,16 +361,18 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", jceProvider); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); /* test that doFinal on non-block size input fails */ tmp = cipher.update(Arrays.copyOfRange(input, 0, 8)); /* 8 */ - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 8, 12)); /* 4 */ - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 12, 16)); /* 4 */ assertEquals(tmp.length, 16); @@ -428,7 +444,7 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", jceProvider); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); @@ -437,13 +453,15 @@ public class WolfCryptCipherTest { assertArrayEquals(tmp, Arrays.copyOfRange(expected, 0, 16)); tmp = cipher.update(Arrays.copyOfRange(input, 16, 17)); /* 1 */ - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(0, tmp.length); tmp = cipher.update(Arrays.copyOfRange(input, 17, 33)); /* 16 */ assertArrayEquals(tmp, Arrays.copyOfRange(expected, 16, 32)); tmp = cipher.update(Arrays.copyOfRange(input, 33, 34)); /* 1 */ - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(0, tmp.length); tmp = cipher.doFinal(Arrays.copyOfRange(input, 34, 48)); /* 14 */ assertArrayEquals(tmp, Arrays.copyOfRange(expected, 32, 48)); @@ -492,7 +510,7 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", jceProvider); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); @@ -500,7 +518,8 @@ public class WolfCryptCipherTest { cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); tmp = cipher.update(Arrays.copyOfRange(input, 0, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 8, 16)); assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -511,7 +530,8 @@ public class WolfCryptCipherTest { /* doFinal should have reset our state, try to encrypt again no init */ tmp = cipher.update(Arrays.copyOfRange(input, 0, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 8, 16)); assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -524,7 +544,8 @@ public class WolfCryptCipherTest { cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); tmp = cipher.update(Arrays.copyOfRange(expected, 0, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(expected, 8, 16)); assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -535,7 +556,8 @@ public class WolfCryptCipherTest { /* doFinal should have reset our state, try to decrypt again no init */ tmp = cipher.update(Arrays.copyOfRange(expected, 0, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(expected, 8, 16)); assertEquals(tmp.length, 16); output = Arrays.copyOfRange(tmp, 0, 16); @@ -663,7 +685,652 @@ public class WolfCryptCipherTest { return; } - Cipher ciph = Cipher.getInstance("AES/CBC/NoPadding", "wolfJCE"); + Cipher ciph = Cipher.getInstance("AES/CBC/NoPadding", jceProvider); + SecretKeySpec secretkey = new SecretKeySpec(key, "AES"); + IvParameterSpec spec = new IvParameterSpec(iv); + + /* encrypt big message */ + ciph.init(Cipher.ENCRYPT_MODE, secretkey, spec); + cipher = ciph.doFinal(input); + + /* decrypt cipher */ + ciph.init(Cipher.DECRYPT_MODE, secretkey, spec); + plain = ciph.doFinal(cipher); + + assertArrayEquals(plain, input); + } + + @Test + public void testAesCbcPKCS5Padding() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + CipherVector vectors[] = new CipherVector[] { + /* test vectors {key, iv, input, output } */ + new CipherVector( + new byte[] { + (byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33, + (byte)0x34, (byte)0x35, (byte)0x36, (byte)0x37, + (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }, + new byte[] { + (byte)0x31, (byte)0x32, (byte)0x33, (byte)0x34, + (byte)0x35, (byte)0x36, (byte)0x37, (byte)0x38, + (byte)0x39, (byte)0x30, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }, + new byte[] { + (byte)0x6e, (byte)0x6f, (byte)0x77, (byte)0x20, + (byte)0x69, (byte)0x73, (byte)0x20, (byte)0x74, + (byte)0x68, (byte)0x65, (byte)0x20, (byte)0x74, + (byte)0x69, (byte)0x6d, (byte)0x65, (byte)0x20 + }, + new byte[] { + (byte)0x95, (byte)0x94, (byte)0x92, (byte)0x57, + (byte)0x5f, (byte)0x42, (byte)0x81, (byte)0x53, + (byte)0x2c, (byte)0xcc, (byte)0x9d, (byte)0x46, + (byte)0x77, (byte)0xa2, (byte)0x33, (byte)0xcb, + (byte)0x7d, (byte)0x37, (byte)0x7b, (byte)0x0b, + (byte)0x44, (byte)0xaa, (byte)0xb5, (byte)0xf0, + (byte)0x5f, (byte)0x34, (byte)0xb4, (byte)0xde, + (byte)0xb5, (byte)0xbd, (byte)0x2a, (byte)0xbb + } + ) + }; + + byte output[]; + + if (!enabledJCEAlgos.contains("AES/CBC/PKCS5Padding")) { + /* bail out if AES is not enabled */ + return; + } + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", jceProvider); + + for (int i = 0; i < vectors.length; i++) { + + SecretKeySpec key = new SecretKeySpec(vectors[i].getKey(), "AES"); + IvParameterSpec spec = new IvParameterSpec(vectors[i].getIV()); + + cipher.init(Cipher.ENCRYPT_MODE, key, spec); + output = cipher.doFinal(vectors[i].input); + + assertArrayEquals(output, vectors[i].output); + } + } + + @Test + public void testAesCbcPKCS5PaddingWithUpdate() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + byte[] key = new byte[] { + (byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33, + (byte)0x34, (byte)0x35, (byte)0x36, (byte)0x37, + (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }; + + byte[] iv = new byte[] { + (byte)0x31, (byte)0x32, (byte)0x33, (byte)0x34, + (byte)0x35, (byte)0x36, (byte)0x37, (byte)0x38, + (byte)0x39, (byte)0x30, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }; + + byte[] input = new byte[] { + (byte)0x6e, (byte)0x6f, (byte)0x77, (byte)0x20, + (byte)0x69, (byte)0x73, (byte)0x20, (byte)0x74, + (byte)0x68, (byte)0x65, (byte)0x20, (byte)0x74, + (byte)0x69, (byte)0x6d, (byte)0x65, (byte)0x20 + }; + + byte[] expected = new byte[] { + (byte)0x95, (byte)0x94, (byte)0x92, (byte)0x57, + (byte)0x5f, (byte)0x42, (byte)0x81, (byte)0x53, + (byte)0x2c, (byte)0xcc, (byte)0x9d, (byte)0x46, + (byte)0x77, (byte)0xa2, (byte)0x33, (byte)0xcb, + (byte)0x7d, (byte)0x37, (byte)0x7b, (byte)0x0b, + (byte)0x44, (byte)0xaa, (byte)0xb5, (byte)0xf0, + (byte)0x5f, (byte)0x34, (byte)0xb4, (byte)0xde, + (byte)0xb5, (byte)0xbd, (byte)0x2a, (byte)0xbb + }; + + byte[] tmp = null; + byte[] output = null; + byte[] finalOutput = null; + + if (!enabledJCEAlgos.contains("AES/CBC/PKCS5Padding")) { + /* bail out if AES is not enabled */ + return; + } + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", jceProvider); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + /* test encrypt processing input in 4 byte chunks */ + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + + tmp = cipher.update(Arrays.copyOfRange(input, 0, 4)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(input, 4, 8)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(input, 8, 12)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(input, 12, 16)); + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + + /* final should add extra block of encrypted padding with PKCS5 pad */ + tmp = cipher.doFinal(); + assertEquals(16, tmp.length); + + /* put together full output and compare to expected */ + finalOutput = new byte[output.length + tmp.length]; + System.arraycopy(output, 0, finalOutput, 0, output.length); + System.arraycopy(tmp, 0, finalOutput, output.length, tmp.length); + assertArrayEquals(finalOutput, expected); + + /* test decrypt processing input in 4 byte chunks */ + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + tmp = cipher.update(Arrays.copyOfRange(expected, 0, 4)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 4, 8)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 8, 12)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 12, 16)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 16, 20)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 20, 24)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 24, 28)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 28, 32)); + /* since encrypted had 16 bytes of padding added internally, once + * reaching 32-bytes, 16 should be returned (without padding) */ + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + + tmp = cipher.doFinal(); + assertEquals(0, tmp.length); + assertArrayEquals(output, input); + + /* test encrypt processing in 1 byte chunks */ + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + + for (int i = 1; i < input.length + 1; i++) { + tmp = cipher.update(Arrays.copyOfRange(input, i-1, i)); + if ((i % 16) != 0) { + assertNotNull(tmp); + assertEquals(0, tmp.length); + } else { + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + } + } + + /* final should add extra block of encrypted padding with PKCS5 pad */ + tmp = cipher.doFinal(); + assertEquals(16, tmp.length); + + /* put together full output and compare to expected */ + finalOutput = new byte[output.length + tmp.length]; + System.arraycopy(output, 0, finalOutput, 0, output.length); + System.arraycopy(tmp, 0, finalOutput, output.length, tmp.length); + assertArrayEquals(finalOutput, expected); + + /* test decrypt processing in 1 byte chunks */ + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + finalOutput = new byte[0]; + + for (int i = 0; i < expected.length; i++) { + tmp = cipher.update(Arrays.copyOfRange(expected, i, i+1)); + if (tmp != null && tmp.length > 0) { + /* append data to final output buffer */ + output = new byte[finalOutput.length + tmp.length]; + System.arraycopy(finalOutput, 0, output, 0, finalOutput.length); + System.arraycopy(tmp, 0, output, finalOutput.length, tmp.length); + finalOutput = output; + } + } + + tmp = cipher.doFinal(); + assertEquals(tmp.length, 0); + assertArrayEquals(input, finalOutput); + } + + @Test + public void testAesCbcPKCS5PaddingWithOddUpdateFail() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + byte[] key = new byte[] { + (byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33, + (byte)0x34, (byte)0x35, (byte)0x36, (byte)0x37, + (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }; + + byte[] iv = new byte[] { + (byte)0x31, (byte)0x32, (byte)0x33, (byte)0x34, + (byte)0x35, (byte)0x36, (byte)0x37, (byte)0x38, + (byte)0x39, (byte)0x30, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }; + + byte[] input = new byte[] { + (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, + (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, + (byte)0x09, (byte)0x10, (byte)0x11, (byte)0x12, + (byte)0x13, (byte)0x14, (byte)0x15, (byte)0x16, + (byte)0x17, (byte)0x18, (byte)0x19, (byte)0x20, + }; + + byte[] expected = new byte[] { + (byte)0xb3, (byte)0xc2, (byte)0x53, (byte)0x43, + (byte)0x64, (byte)0xa6, (byte)0x19, (byte)0x8d, + (byte)0x9c, (byte)0x05, (byte)0x7e, (byte)0xf0, + (byte)0xaa, (byte)0x20, (byte)0x13, (byte)0x7f, + (byte)0x15, (byte)0x66, (byte)0x51, (byte)0xf5, + (byte)0x27, (byte)0x3b, (byte)0xea, (byte)0xc4, + (byte)0xcf, (byte)0xb5, (byte)0xcc, (byte)0xfa, + (byte)0x9e, (byte)0xc2, (byte)0xcc, (byte)0x37 + }; + + byte[] tmp = null; + byte[] output = null; + byte[] finalOutput = null; + + if (!enabledJCEAlgos.contains("AES/CBC/PKCS5Padding")) { + /* bail out if AES is not enabled */ + return; + } + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", jceProvider); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + + /* test that doFinal on non-block size input correctly pads */ + tmp = cipher.update(Arrays.copyOfRange(input, 0, 8)); /* 8 */ + assertNotNull(tmp); + assertEquals(tmp.length, 0); + tmp = cipher.update(Arrays.copyOfRange(input, 8, 12)); /* 4 */ + assertNotNull(tmp); + assertEquals(tmp.length, 0); + output = cipher.update(Arrays.copyOfRange(input, 12, 16)); /* 4 */ + assertEquals(output.length, 16); + + /* partial last block, total message size of 20 bytes */ + tmp = cipher.doFinal(Arrays.copyOfRange(input, 16, 20)); /* 4 */ + assertNotNull(tmp); + assertEquals(tmp.length, 16); + + finalOutput = new byte[output.length + tmp.length]; + System.arraycopy(output, 0, finalOutput, 0, output.length); + System.arraycopy(tmp, 0, finalOutput, output.length, tmp.length); + + assertArrayEquals(expected, finalOutput); + } + + @Test + public void testAesCbcPKCS5PaddingWithOddUpdateSuccess() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + byte key[] = new byte[] { + (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, + (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, + (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, + (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, + }; + + byte iv[] = new byte[] { + (byte)0x10, (byte)0x11, (byte)0x12, (byte)0x13, + (byte)0x14, (byte)0x15, (byte)0x16, (byte)0x17, + (byte)0x10, (byte)0x11, (byte)0x12, (byte)0x13, + (byte)0x14, (byte)0x15, (byte)0x16, (byte)0x17, + }; + + byte input[] = new byte[] { + (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23, + (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x27, + (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23, + (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x27, + (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23, + (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x27, + (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23, + (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x27, + (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23, + (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x27, + (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23, + (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x27, + }; + + byte expected[] = new byte[] { + (byte) 0x8d, (byte) 0xda, (byte) 0x93, (byte) 0x7a, + (byte) 0x61, (byte) 0xf5, (byte) 0xc9, (byte) 0x98, + (byte) 0x19, (byte) 0x67, (byte) 0xe2, (byte) 0xd3, + (byte) 0x5a, (byte) 0xa9, (byte) 0x4e, (byte) 0x4f, + (byte) 0x1a, (byte) 0x52, (byte) 0x0c, (byte) 0xab, + (byte) 0x0c, (byte) 0xcc, (byte) 0xb7, (byte) 0x59, + (byte) 0x4c, (byte) 0xe6, (byte) 0x71, (byte) 0x4e, + (byte) 0x2a, (byte) 0x60, (byte) 0x71, (byte) 0x70, + (byte) 0x56, (byte) 0x69, (byte) 0xeb, (byte) 0x20, + (byte) 0xea, (byte) 0xf1, (byte) 0xfe, (byte) 0x75, + (byte) 0x9a, (byte) 0x08, (byte) 0x17, (byte) 0xd3, + (byte) 0xa3, (byte) 0x8e, (byte) 0x04, (byte) 0x8c, + (byte) 0x06, (byte) 0x60, (byte) 0xe6, (byte) 0x90, + (byte) 0x6b, (byte) 0xa4, (byte) 0x0a, (byte) 0xe9, + (byte) 0x36, (byte) 0x62, (byte) 0xef, (byte) 0xf2, + (byte) 0x0f, (byte) 0x77, (byte) 0x1c, (byte) 0xff + }; + + byte tmp[]; + + if (!enabledJCEAlgos.contains("AES/CBC/PKCS5Padding")) { + /* bail out if AES is not enabled */ + return; + } + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", jceProvider); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + + tmp = cipher.update(Arrays.copyOfRange(input, 0, 16)); /* 16 */ + assertArrayEquals(tmp, Arrays.copyOfRange(expected, 0, 16)); + + tmp = cipher.update(Arrays.copyOfRange(input, 16, 17)); /* 1 */ + assertNotNull(tmp); + assertEquals(0, tmp.length); + + tmp = cipher.update(Arrays.copyOfRange(input, 17, 33)); /* 16 */ + assertArrayEquals(tmp, Arrays.copyOfRange(expected, 16, 32)); + + tmp = cipher.update(Arrays.copyOfRange(input, 33, 34)); /* 1 */ + assertNotNull(tmp); + assertEquals(0, tmp.length); + + tmp = cipher.doFinal(Arrays.copyOfRange(input, 34, 48)); /* 14 */ + assertArrayEquals(tmp, Arrays.copyOfRange(expected, 32, 64)); + } + + @Test + public void testAesCbcPKCS5PaddingWithUpdateVerifyFinalResetsState() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + byte[] key = new byte[] { + (byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33, + (byte)0x34, (byte)0x35, (byte)0x36, (byte)0x37, + (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }; + + byte[] iv = new byte[] { + (byte)0x31, (byte)0x32, (byte)0x33, (byte)0x34, + (byte)0x35, (byte)0x36, (byte)0x37, (byte)0x38, + (byte)0x39, (byte)0x30, (byte)0x61, (byte)0x62, + (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x66 + }; + + byte[] input = new byte[] { + (byte)0x6e, (byte)0x6f, (byte)0x77, (byte)0x20, + (byte)0x69, (byte)0x73, (byte)0x20, (byte)0x74, + (byte)0x68, (byte)0x65, (byte)0x20, (byte)0x74, + (byte)0x69, (byte)0x6d, (byte)0x65, (byte)0x20 + }; + + byte[] expected = new byte[] { + (byte)0x95, (byte)0x94, (byte)0x92, (byte)0x57, + (byte)0x5f, (byte)0x42, (byte)0x81, (byte)0x53, + (byte)0x2c, (byte)0xcc, (byte)0x9d, (byte)0x46, + (byte)0x77, (byte)0xa2, (byte)0x33, (byte)0xcb, + (byte)0x7d, (byte)0x37, (byte)0x7b, (byte)0x0b, + (byte)0x44, (byte)0xaa, (byte)0xb5, (byte)0xf0, + (byte)0x5f, (byte)0x34, (byte)0xb4, (byte)0xde, + (byte)0xb5, (byte)0xbd, (byte)0x2a, (byte)0xbb + }; + + byte[] tmp = null; + byte[] output = null; + byte[] finalOutput = null; + + if (!enabledJCEAlgos.contains("AES/CBC/PKCS5Padding")) { + /* bail out if AES is not enabled */ + return; + } + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", jceProvider); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + /* test encrypt */ + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + + tmp = cipher.update(Arrays.copyOfRange(input, 0, 8)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(input, 8, 16)); + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + + tmp = cipher.doFinal(); + assertNotNull(tmp); + assertEquals(16, tmp.length); + + /* put together full output and compare to expected */ + finalOutput = new byte[output.length + tmp.length]; + System.arraycopy(output, 0, finalOutput, 0, output.length); + System.arraycopy(tmp, 0, finalOutput, output.length, tmp.length); + assertArrayEquals(finalOutput, expected); + + /* doFinal should have reset our state, try to encrypt again no init */ + tmp = cipher.update(Arrays.copyOfRange(input, 0, 8)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(input, 8, 16)); + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + + tmp = cipher.doFinal(); + assertNotNull(tmp); + assertEquals(16, tmp.length); + + /* put together full output and compare to expected */ + finalOutput = new byte[output.length + tmp.length]; + System.arraycopy(output, 0, finalOutput, 0, output.length); + System.arraycopy(tmp, 0, finalOutput, output.length, tmp.length); + assertArrayEquals(finalOutput, expected); + + /* test decrypt */ + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + tmp = cipher.update(Arrays.copyOfRange(expected, 0, 8)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 8, 16)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 16, 24)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 24, 32)); + assertNotNull(tmp); + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + + tmp = cipher.doFinal(); + assertNotNull(tmp); + assertEquals(0, tmp.length); + assertArrayEquals(input, output); + + /* doFinal should have reset our state, try to decrypt again no init */ + tmp = cipher.update(Arrays.copyOfRange(expected, 0, 8)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 8, 16)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 16, 24)); + assertNotNull(tmp); + assertEquals(0, tmp.length); + tmp = cipher.update(Arrays.copyOfRange(expected, 24, 32)); + assertNotNull(tmp); + assertEquals(16, tmp.length); + output = Arrays.copyOfRange(tmp, 0, 16); + + tmp = cipher.doFinal(); + assertNotNull(tmp); + assertEquals(0, tmp.length); + assertArrayEquals(input, output); + } + + @Test + public void testAesCbcPKCS5PaddingBigMessage() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + final byte[] input = new byte[] { + /* "All work and no play makes Jack a dull boy. " */ + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20, + (byte)0x61, (byte)0x20, (byte)0x64, (byte)0x75, + (byte)0x6c, (byte)0x6c, (byte)0x20, (byte)0x62, + (byte)0x6f, (byte)0x79, (byte)0x2e, (byte)0x20, + (byte)0x41, (byte)0x6c, (byte)0x6c, (byte)0x20, + (byte)0x77, (byte)0x6f, (byte)0x72, (byte)0x6b, + (byte)0x20, (byte)0x61, (byte)0x6e, (byte)0x64, + (byte)0x20, (byte)0x6e, (byte)0x6f, (byte)0x20, + (byte)0x70, (byte)0x6c, (byte)0x61, (byte)0x79, + (byte)0x20, (byte)0x6d, (byte)0x61, (byte)0x6b, + (byte)0x65, (byte)0x73, (byte)0x20, (byte)0x4a, + (byte)0x61, (byte)0x63, (byte)0x6b, (byte)0x20 + }; + + final byte[] key = "0123456789abcdeffedcba9876543210".getBytes(); + final byte[] iv = "1234567890abcdef".getBytes(); + + byte[] cipher = null; + byte[] plain = null; + + if (!enabledJCEAlgos.contains("AES/CBC/PKCS5Padding")) { + /* bail out if AES is not enabled */ + return; + } + + Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding", jceProvider); SecretKeySpec secretkey = new SecretKeySpec(key, "AES"); IvParameterSpec spec = new IvParameterSpec(iv); @@ -726,7 +1393,7 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding", jceProvider); for (int i = 0; i < vectors.length; i++) { @@ -788,14 +1455,15 @@ public class WolfCryptCipherTest { return; } - Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding", "wolfJCE"); + Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding", jceProvider); SecretKeySpec keyspec = new SecretKeySpec(key, "DESede"); IvParameterSpec spec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, keyspec, spec); tmp = cipher.update(Arrays.copyOfRange(input, 0, 4)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = cipher.update(Arrays.copyOfRange(input, 4, 8)); assertArrayEquals(tmp, Arrays.copyOfRange(output, 0, 8)); @@ -856,7 +1524,7 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", "wolfJCE"); + Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); for (int i = 0; i < vectors.length; i++) { @@ -916,30 +1584,38 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", "wolfJCE"); + Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); /* PRIVATE ENCRYPT, 4 byte chunks + 1 remaining for final */ ciph.init(Cipher.ENCRYPT_MODE, priv); tmp = ciph.update(Arrays.copyOfRange(input, 0, 4)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 4, 8)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 8, 16)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 16, 24)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); ciphertext = ciph.doFinal(Arrays.copyOfRange(input, 24, 25)); /* PUBLIC DECRYPT, 50-byte chunks + 56 remaining for final */ ciph.init(Cipher.DECRYPT_MODE, pub); tmp = ciph.update(Arrays.copyOfRange(ciphertext, 0, 50)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertext, 50, 100)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertext, 100, 150)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertext, 150, 200)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); plaintext = ciph.doFinal(Arrays.copyOfRange(ciphertext, 200, 256)); assertArrayEquals(plaintext, input); @@ -948,16 +1624,19 @@ public class WolfCryptCipherTest { ciph.init(Cipher.ENCRYPT_MODE, pub); for (int i = 1; i < input.length + 1; i++) { tmp = ciph.update(Arrays.copyOfRange(input, i-1, i)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); } ciphertext = ciph.doFinal(); /* PRIVATE DECRYPT, 100-byte chunks + 56 remaining for final */ ciph.init(Cipher.DECRYPT_MODE, priv); tmp = ciph.update(Arrays.copyOfRange(ciphertext, 0, 100)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertext, 100, 200)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); plaintext = ciph.doFinal(Arrays.copyOfRange(ciphertext, 200, 256)); assertArrayEquals(plaintext, input); } @@ -997,41 +1676,49 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", "wolfJCE"); + Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); /* PRIVATE ENCRYPT */ /* storing to ciphertextA */ ciph.init(Cipher.ENCRYPT_MODE, priv); tmp = ciph.update(Arrays.copyOfRange(input, 0, 16)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 16, 24)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); ciphertextA = ciph.doFinal(Arrays.copyOfRange(input, 24, 25)); /* PRIVATE ENCRYPT */ /* doFinal should reset state, encrypt again without init */ tmp = ciph.update(Arrays.copyOfRange(input, 0, 16)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 16, 24)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); ciphertextB = ciph.doFinal(Arrays.copyOfRange(input, 24, 25)); /* PUBLIC DECRYPT */ /* public decrypt, verifying ciphertextA */ ciph.init(Cipher.DECRYPT_MODE, pub); tmp = ciph.update(Arrays.copyOfRange(ciphertextA, 0, 150)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertextA, 150, 200)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); plaintextA = ciph.doFinal(Arrays.copyOfRange(ciphertextA, 200, 256)); assertArrayEquals(plaintextA, input); /* PUBLIC DECRYPT */ /* doFinal should reset state, decrypt ciphertext B without init */ tmp = ciph.update(Arrays.copyOfRange(ciphertextB, 0, 150)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertextB, 150, 200)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); plaintextB = ciph.doFinal(Arrays.copyOfRange(ciphertextB, 200, 256)); assertArrayEquals(plaintextB, input); @@ -1039,35 +1726,43 @@ public class WolfCryptCipherTest { /* storing to ciphertextA */ ciph.init(Cipher.ENCRYPT_MODE, pub); tmp = ciph.update(Arrays.copyOfRange(input, 0, 16)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 16, 24)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); ciphertextA = ciph.doFinal(Arrays.copyOfRange(input, 24, 25)); /* PUBLIC ENCRYPT */ /* doFinal should reset state, encrypt again without init */ tmp = ciph.update(Arrays.copyOfRange(input, 0, 16)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(input, 16, 24)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); ciphertextB = ciph.doFinal(Arrays.copyOfRange(input, 24, 25)); /* PRIVATE DECRYPT */ /* public decrypt, verifying ciphertextA */ ciph.init(Cipher.DECRYPT_MODE, priv); tmp = ciph.update(Arrays.copyOfRange(ciphertextA, 0, 150)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertextA, 150, 200)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); plaintextA = ciph.doFinal(Arrays.copyOfRange(ciphertextA, 200, 256)); assertArrayEquals(plaintextA, input); /* PRIVATE DECRYPT */ /* doFinal should reset state, decrypt ciphertext B without init */ tmp = ciph.update(Arrays.copyOfRange(ciphertextB, 0, 150)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); tmp = ciph.update(Arrays.copyOfRange(ciphertextB, 150, 200)); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); plaintextB = ciph.doFinal(Arrays.copyOfRange(ciphertextB, 200, 256)); assertArrayEquals(plaintextB, input); } @@ -1099,69 +1794,81 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", "wolfJCE"); + Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); /* PRIVATE ENCRYPT */ ciph.init(Cipher.ENCRYPT_MODE, priv); + tmp = ciph.update(inputA); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); + tmp = ciph.update(inputB); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); try { ciphertext = ciph.doFinal(); fail("Cipher.doFinal should throw exception when data is larger " + "than RSA key size"); - } catch (WolfCryptException e) { - assertEquals("RSA buffer error, output too small or input too big", - e.getMessage()); + } catch (WolfCryptException | IllegalBlockSizeException e) { + /* expected */ } /* PUBLIC DECRYPT */ ciph.init(Cipher.DECRYPT_MODE, pub); + tmp = ciph.update(inputA); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); + tmp = ciph.update(inputB); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); try { plaintext = ciph.doFinal(); fail("Cipher.doFinal should throw exception when data is larger " + "than RSA key size"); - } catch (WolfCryptException e) { - /* will throw exception with either "Rsa Padding error" or - * "Bad function argument" depending on wolfSSL version. But, in - * any case, we expect to get an exception here. */ + } catch (WolfCryptException | IllegalBlockSizeException e) { + /* expected */ } /* PUBLIC ENCRYPT */ ciph.init(Cipher.ENCRYPT_MODE, pub); + tmp = ciph.update(inputA); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); + tmp = ciph.update(inputB); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); try { ciphertext = ciph.doFinal(); fail("Cipher.doFinal should throw exception when data is larger " + "than RSA key size"); - } catch (WolfCryptException e) { - assertEquals("RSA buffer error, output too small or input too big", - e.getMessage()); + } catch (WolfCryptException | IllegalBlockSizeException e) { + /* expected */ } /* PRIVATE DECRYPT */ ciph.init(Cipher.DECRYPT_MODE, priv); + tmp = ciph.update(inputA); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); + tmp = ciph.update(inputB); - assertArrayEquals(tmp, null); + assertNotNull(tmp); + assertEquals(tmp.length, 0); try { plaintext = ciph.doFinal(); fail("Cipher.doFinal should throw exception when data is larger " + "than RSA key size"); - } catch (WolfCryptException e) { + } catch (WolfCryptException | IllegalBlockSizeException e) { /* expected */ } } @@ -1206,7 +1913,7 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciphA = Cipher.getInstance("RSA/ECB/PKCS1Padding", "wolfJCE"); + Cipher ciphA = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); Cipher ciphB = Cipher.getInstance("RSA/ECB/PKCS1Padding"); Provider prov = ciphB.getProvider(); diff --git a/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java b/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java index 57dbabf..551e439 100644 --- a/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java +++ b/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java @@ -23,8 +23,8 @@ package com.wolfssl.wolfcrypt.test; import static org.junit.Assert.*; +import java.util.Arrays; import java.nio.ByteBuffer; - import javax.crypto.ShortBufferException; import org.junit.Assume; @@ -417,5 +417,104 @@ public class AesTest { enc.releaseNativeStruct(); dec.releaseNativeStruct(); } + + @Test + public void testPadPKCS7() { + + int padSz = 0; + int expectedPadSz = 0; + byte[] input = null; + byte[] padded = null; + byte[] unpadded = null; + + /* Test calculated pad size matches expected for input sizes + * from 1 to Aes.BLOCK_SIZE */ + for (int i = 0; i < Aes.BLOCK_SIZE; i++) { + padSz = Aes.getPKCS7PadSize(i + 1, Aes.BLOCK_SIZE); + expectedPadSz = Aes.BLOCK_SIZE - ((i + 1) % Aes.BLOCK_SIZE); + assertEquals(padSz, expectedPadSz); + } + + /* Test pad/unpad of arrays sized 1 to 2 * Aes.BLOCK SIZE bytes, + * Fill test arrays with 0xAA */ + for (int i = 0; i < 2 * Aes.BLOCK_SIZE; i++) { + input = new byte[i+1]; + Arrays.fill(input, 0, input.length, (byte)0xAA); + + padded = Aes.padPKCS7(input, Aes.BLOCK_SIZE); + unpadded = Aes.unPadPKCS7(padded, Aes.BLOCK_SIZE); + + assertEquals(input.length, unpadded.length); + assertArrayEquals(input, unpadded); + } + + /* Test padPKCS7() with null input */ + try { + padded = Aes.padPKCS7(null, Aes.BLOCK_SIZE); + fail("null input should throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + + /* Test padPKCS7() with zero length input */ + input = new byte[0]; + padded = Aes.padPKCS7(input, Aes.BLOCK_SIZE); + assertNotNull(padded); + assertEquals(16, padded.length); + + /* Test padPKCS7() with zero length block size */ + try { + input = new byte[10]; + padded = Aes.padPKCS7(input, 0); + fail("zero length block size should throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + + /* Test unPadPKCS7() with null input */ + try { + unpadded = Aes.unPadPKCS7(null, Aes.BLOCK_SIZE); + fail("null input should throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + + /* Test unPadPKCS7() with zero length input */ + try { + input = new byte[0]; + unpadded = Aes.unPadPKCS7(input, Aes.BLOCK_SIZE); + fail("zero length input should throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + + /* Test unPadPKCS7() with zero length block size */ + try { + input = new byte[10]; + unpadded = Aes.unPadPKCS7(input, 0); + fail("zero length block size should throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + + /* Test unPadPKCS7() with inconsistent pad value */ + try { + padded = Util.h2b("00112233445566778899AABBCC020303"); + unpadded = Aes.unPadPKCS7(padded, Aes.BLOCK_SIZE); + fail("inconsistent padding should throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + + /* Test unPadPKCS7() with pad value larger than block size */ + try { + padded = Util.h2b("00112233445566778899AABBCC111111"); + unpadded = Aes.unPadPKCS7(padded, Aes.BLOCK_SIZE); + fail("pad value larger than block size should " + + "throw WolfCryptException"); + } catch (WolfCryptException e) { + /* expected */ + } + } } From 8788d42474360b136cc626aa6c28b97da40246d7 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 15 Jun 2023 10:57:57 -0600 Subject: [PATCH 2/3] JCE: add Cipher support for RSA, same as RSA/ECB/PKCS1Padding --- README_JCE.md | 1 + .../provider/jce/WolfCryptProvider.java | 2 + .../jce/test/WolfCryptCipherTest.java | 174 ++++++++++-------- 3 files changed, 105 insertions(+), 72 deletions(-) diff --git a/README_JCE.md b/README_JCE.md index fe1a89b..18782aa 100644 --- a/README_JCE.md +++ b/README_JCE.md @@ -40,6 +40,7 @@ The JCE provider currently supports the following algorithms: AES/CBC/NoPadding AES/CBC/PKCS5Padding DESede/CBC/NoPadding + RSA RSA/ECB/PKCS1Padding Mac Class diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index 410e84f..601b95e 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -127,6 +127,8 @@ public final class WolfCryptProvider extends Provider { put("Cipher.DESede/CBC/NoPadding", "com.wolfssl.provider.jce.WolfCryptCipher$wcDESedeCBCNoPadding"); + put("Cipher.RSA", + "com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding"); put("Cipher.RSA/ECB/PKCS1Padding", "com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding"); diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java index a29f2b4..fd5b557 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java @@ -60,12 +60,16 @@ public class WolfCryptCipherTest { "AES/CBC/NoPadding", "AES/CBC/PKCS5Padding", "DESede/CBC/NoPadding", + "RSA", "RSA/ECB/PKCS1Padding" }; /* JCE provider to run below tests against */ private static final String jceProvider = "wolfJCE"; + /* Interop provider, may be changed in testProviderInstallationAtRuntime */ + private static String interopProvider = null; + /* populated with all enabled algos (some could have been compiled out) */ private static ArrayList enabledJCEAlgos = new ArrayList(); @@ -99,7 +103,15 @@ public class WolfCryptCipherTest { expectedBlockSizes.put("AES/CBC/NoPadding", 16); expectedBlockSizes.put("AES/CBC/PKCS5Padding", 16); expectedBlockSizes.put("DESede/CBC/NoPadding", 8); + expectedBlockSizes.put("RSA", 0); expectedBlockSizes.put("RSA/ECB/PKCS1Padding", 0); + + /* try to set up interop provider, if available */ + /* NOTE: add other platform providers here if needed */ + p = Security.getProvider("SunJCE"); + if (p != null) { + interopProvider = "SunJCE"; + } } @Test @@ -1484,14 +1496,13 @@ public class WolfCryptCipherTest { } } - @Test - public void testRSAECBPKCS1Padding() + private void testRSAPublicPrivateEncryptDecrypt(String algo) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException { - CipherVector vectors[] = new CipherVector[] { + CipherVector[] vectors = new CipherVector[] { /* test vectors {key, iv, input, output } */ new CipherVector( null, @@ -1509,13 +1520,8 @@ public class WolfCryptCipherTest { ) }; - byte ciphertext[]; - byte plaintext[]; - - if (!enabledJCEAlgos.contains("RSA/ECB/PKCS1Padding")) { - /* bail out if RSA is not enabled */ - return; - } + byte[] ciphertext = null; + byte[] plaintext = null; KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048, new SecureRandom()); @@ -1524,7 +1530,12 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); + if (!enabledJCEAlgos.contains(algo)) { + /* mode not supported, return without testing */ + return; + } + + Cipher ciph = Cipher.getInstance(algo, jceProvider); for (int i = 0; i < vectors.length; i++) { @@ -1550,15 +1561,13 @@ public class WolfCryptCipherTest { } } - /* testing RSA encrypt/decrypt using various update/final sizes */ - @Test - public void testRSAECBPKCS1PaddingWithUpdate() + private void testRSAWithUpdateSizes(String algo) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException { - byte input[] = new byte[] { + byte[] input = new byte[] { (byte)0x45, (byte)0x76, (byte)0x65, (byte)0x72, (byte)0x79, (byte)0x6f, (byte)0x6e, (byte)0x65, (byte)0x20, (byte)0x67, (byte)0x65, (byte)0x74, @@ -1568,14 +1577,9 @@ public class WolfCryptCipherTest { (byte)0x2e }; - byte tmp[]; - byte ciphertext[]; - byte plaintext[]; - - if (!enabledJCEAlgos.contains("RSA/ECB/PKCS1Padding")) { - /* bail out if RSA is not enabled */ - return; - } + byte[] tmp = null; + byte[] ciphertext = null; + byte[] plaintext = null; KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048, new SecureRandom()); @@ -1584,7 +1588,12 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); + if (!enabledJCEAlgos.contains(algo)) { + /* mode not supported, return without testing */ + return; + } + + Cipher ciph = Cipher.getInstance(algo, jceProvider); /* PRIVATE ENCRYPT, 4 byte chunks + 1 remaining for final */ ciph.init(Cipher.ENCRYPT_MODE, priv); @@ -1641,14 +1650,13 @@ public class WolfCryptCipherTest { assertArrayEquals(plaintext, input); } - @Test - public void testRSAECBPKCS1PaddingWithUpdateVerifyFinalResetsState() + private void testRSAWithUpdateVerifyFinalResetsState(String algo) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException { - byte input[] = new byte[] { + byte[] input = new byte[] { (byte)0x45, (byte)0x76, (byte)0x65, (byte)0x72, (byte)0x79, (byte)0x6f, (byte)0x6e, (byte)0x65, (byte)0x20, (byte)0x67, (byte)0x65, (byte)0x74, @@ -1658,16 +1666,11 @@ public class WolfCryptCipherTest { (byte)0x2e }; - byte tmp[]; - byte ciphertextA[] = null; - byte ciphertextB[] = null; - byte plaintextA[] = null; - byte plaintextB[] = null; - - if (!enabledJCEAlgos.contains("RSA/ECB/PKCS1Padding")) { - /* bail out if RSA is not enabled */ - return; - } + byte[] tmp = null; + byte[] ciphertextA = null; + byte[] ciphertextB = null; + byte[] plaintextA = null; + byte[] plaintextB = null; KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048, new SecureRandom()); @@ -1676,7 +1679,12 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); + if (!enabledJCEAlgos.contains(algo)) { + /* mode not supported, return without testing */ + return; + } + + Cipher ciph = Cipher.getInstance(algo, jceProvider); /* PRIVATE ENCRYPT */ /* storing to ciphertextA */ @@ -1767,25 +1775,18 @@ public class WolfCryptCipherTest { assertArrayEquals(plaintextB, input); } - /* test RSA with update, and too large of data */ - @Test - public void testRSAECBPKCS1PaddingWithTooBigData() + private void testRSAWithTooBigData(String algo) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException { - byte tmp[]; - byte ciphertext[]; - byte plaintext[]; + byte[] tmp = null; + byte[] ciphertext = null; + byte[] plaintext = null; - if (!enabledJCEAlgos.contains("RSA/ECB/PKCS1Padding")) { - /* bail out if RSA is not enabled */ - return; - } - - byte inputA[] = new byte[2048]; - byte inputB[] = new byte[100]; + byte[] inputA = new byte[2048]; + byte[] inputB = new byte[100]; KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048, new SecureRandom()); @@ -1794,7 +1795,12 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciph = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); + if (!enabledJCEAlgos.contains(algo)) { + /* mode not supported, return without testing */ + return; + } + + Cipher ciph = Cipher.getInstance(algo, jceProvider); /* PRIVATE ENCRYPT */ ciph.init(Cipher.ENCRYPT_MODE, priv); @@ -1809,8 +1815,8 @@ public class WolfCryptCipherTest { try { ciphertext = ciph.doFinal(); - fail("Cipher.doFinal should throw exception when data is larger " + - "than RSA key size"); + fail("Cipher.doFinal should throw exception when data " + + "is larger than RSA key size"); } catch (WolfCryptException | IllegalBlockSizeException e) { /* expected */ } @@ -1828,8 +1834,8 @@ public class WolfCryptCipherTest { try { plaintext = ciph.doFinal(); - fail("Cipher.doFinal should throw exception when data is larger " + - "than RSA key size"); + fail("Cipher.doFinal should throw exception when data " + + "is larger than RSA key size"); } catch (WolfCryptException | IllegalBlockSizeException e) { /* expected */ } @@ -1847,8 +1853,8 @@ public class WolfCryptCipherTest { try { ciphertext = ciph.doFinal(); - fail("Cipher.doFinal should throw exception when data is larger " + - "than RSA key size"); + fail("Cipher.doFinal should throw exception when data " + + "is larger than RSA key size"); } catch (WolfCryptException | IllegalBlockSizeException e) { /* expected */ } @@ -1866,21 +1872,20 @@ public class WolfCryptCipherTest { try { plaintext = ciph.doFinal(); - fail("Cipher.doFinal should throw exception when data is larger " + - "than RSA key size"); + fail("Cipher.doFinal should throw exception when data " + + "is larger than RSA key size"); } catch (WolfCryptException | IllegalBlockSizeException e) { /* expected */ } } - @Test - public void testRSAECBPKCS1PaddingInterop() + private void testRSAInterop(String algo) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException { - CipherVector vectors[] = new CipherVector[] { + CipherVector[] vectors = new CipherVector[] { /* test vectors {key, iv, input, output } */ new CipherVector( null, @@ -1898,13 +1903,8 @@ public class WolfCryptCipherTest { ) }; - byte ciphertext[]; - byte plaintext[]; - - if (!enabledJCEAlgos.contains("RSA/ECB/PKCS1Padding")) { - /* bail out if RSA is not enabled */ - return; - } + byte[] ciphertext = null; + byte[] plaintext = null; KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048, new SecureRandom()); @@ -1913,8 +1913,13 @@ public class WolfCryptCipherTest { PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - Cipher ciphA = Cipher.getInstance("RSA/ECB/PKCS1Padding", jceProvider); - Cipher ciphB = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + if (!enabledJCEAlgos.contains(algo)) { + /* mode not supported, return without testing */ + return; + } + + Cipher ciphA = Cipher.getInstance(algo, jceProvider); + Cipher ciphB = Cipher.getInstance(algo, interopProvider); Provider prov = ciphB.getProvider(); if (prov.equals("wolfJCE")) { @@ -1974,6 +1979,31 @@ public class WolfCryptCipherTest { } } + @Test + public void testRSA() + throws NoSuchProviderException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, InvalidAlgorithmParameterException, + BadPaddingException { + + testRSAPublicPrivateEncryptDecrypt("RSA"); + testRSAPublicPrivateEncryptDecrypt("RSA/ECB/PKCS1Padding"); + + /* test RSA encrypt/decrypt using various update/final sizes */ + testRSAWithUpdateSizes("RSA"); + testRSAWithUpdateSizes("RSA/ECB/PKCS1Padding"); + + testRSAWithUpdateVerifyFinalResetsState("RSA"); + testRSAWithUpdateVerifyFinalResetsState("RSA/ECB/PKCS1Padding"); + + /* test RSA with update, and too large of data */ + testRSAWithTooBigData("RSA"); + testRSAWithTooBigData("RSA/ECB/PKCS1Padding"); + + testRSAInterop("RSA"); + testRSAInterop("RSA/ECB/PKCS1Padding"); + } + private class CipherVector { private byte key[]; From a2e7643464307e8443af8d5914dd1df468733837 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Fri, 7 Jul 2023 16:06:05 -0600 Subject: [PATCH 3/3] JNI: compile in 3DES FIPS APIs for FIPSv2 --- jni/jni_fips.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/jni/jni_fips.c b/jni/jni_fips.c index d05d64e..414ec94 100644 --- a/jni/jni_fips.c +++ b/jni/jni_fips.c @@ -868,7 +868,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1SetKey_1fips__Lcom_ jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -908,7 +908,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1SetKey_1fips__Lcom_ jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -948,7 +948,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1SetIV_1fips__Lcom_w jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -981,7 +981,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1SetIV_1fips__Lcom_w jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -1016,7 +1016,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1CbcEncrypt_1fips__L jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -1055,7 +1055,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1CbcEncrypt_1fips__L jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -1096,7 +1096,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1CbcDecrypt_1fips__L jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL; @@ -1135,7 +1135,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_Fips_Des3_1CbcDecrypt_1fips__L jint ret = NOT_COMPILED_IN; #if defined(HAVE_FIPS) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2)) && \ !defined(NO_DES3) Des3* des = NULL;