Merge pull request #51 from cconlon/aesPkcs5Padding

JCE: add support for Cipher AES/CBC/PKCS5Padding, RSA
pull/52/head
JacobBarthelmeh 2023-07-10 09:38:20 -06:00 committed by GitHub
commit fbfa297cc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1261 additions and 196 deletions

View File

@ -38,7 +38,9 @@ The JCE provider currently supports the following algorithms:
Cipher Class
AES/CBC/NoPadding
AES/CBC/PKCS5Padding
DESede/CBC/NoPadding
RSA
RSA/ECB/PKCS1Padding
Mac Class

View File

@ -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

View File

@ -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;

View File

@ -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
*/

View File

@ -121,10 +121,14 @@ 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");
put("Cipher.RSA",
"com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding");
put("Cipher.RSA/ECB/PKCS1Padding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding");

View File

@ -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;
}
}

View File

@ -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 */
}
}
}