JCE: add Cipher support for AES/GCM/NoPadding

pull/62/head
Chris Conlon 2024-02-09 14:57:05 -07:00
parent a5434c8c38
commit 9bd93de973
7 changed files with 1375 additions and 81 deletions

View File

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

View File

@ -151,6 +151,54 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha384En
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha512Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: RsaEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: RsaKeyGenEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaKeyGenEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: DhEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_DhEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: EccEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: EccKeyGenEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccKeyGenEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: EccDheEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccDheEnabled
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif

View File

@ -244,3 +244,75 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha512En
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifndef NO_RSA
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaKeyGenEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_DhEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifndef NO_DH
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifdef HAVE_ECC
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccKeyGenEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if defined(HAVE_ECC) && defined(WOLFSSL_KEY_GEN)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccDheEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}

View File

@ -22,6 +22,7 @@
package com.wolfssl.provider.jce;
import java.util.Arrays;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
@ -31,6 +32,7 @@ import javax.crypto.ShortBufferException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import java.security.SecureRandom;
import java.security.AlgorithmParameters;
@ -46,6 +48,7 @@ import java.security.interfaces.RSAPublicKey;
import com.wolfssl.wolfcrypt.WolfCrypt;
import com.wolfssl.wolfcrypt.Asn;
import com.wolfssl.wolfcrypt.Aes;
import com.wolfssl.wolfcrypt.AesGcm;
import com.wolfssl.wolfcrypt.Des3;
import com.wolfssl.wolfcrypt.Rsa;
import com.wolfssl.wolfcrypt.Rng;
@ -65,7 +68,8 @@ public class WolfCryptCipher extends CipherSpi {
enum CipherMode {
WC_ECB,
WC_CBC
WC_CBC,
WC_GCM
}
enum PaddingType {
@ -92,10 +96,11 @@ public class WolfCryptCipher extends CipherSpi {
private int blockSize = 0;
private Aes aes = null;
private Des3 des3 = null;
private Rsa rsa = null;
private Rng rng = null;
private Aes aes = null;
private AesGcm aesGcm = null;
private Des3 des3 = null;
private Rsa rsa = null;
private Rng rng = null;
/* for debug logging */
private WolfCryptDebug debug;
@ -107,6 +112,18 @@ public class WolfCryptCipher extends CipherSpi {
private AlgorithmParameterSpec storedSpec = null;
private byte[] iv = null;
/* AES-GCM tag length (bytes) */
private int gcmTagLen = 0;
/* AAD data for AES-GCM, populated via engineUpdateAAD() */
private byte[] aadData = null;
/* Has update/final been called yet, gates setting of AAD for GCM */
private boolean operationStarted = false;
/* Has this Cipher been inintialized? */
private boolean cipherInitialized = false;
/* buffered data from update calls */
private byte[] buffered = new byte[0];
@ -122,19 +139,12 @@ public class WolfCryptCipher extends CipherSpi {
switch (cipherType) {
case WC_AES:
aes = new Aes();
blockSize = Aes.BLOCK_SIZE;
break;
case WC_DES3:
des3 = new Des3();
blockSize = Des3.BLOCK_SIZE;
break;
case WC_RSA:
rsa = new Rsa();
rsa.setRng(this.rng);
break;
}
if (debug.DEBUG) {
@ -143,6 +153,48 @@ public class WolfCryptCipher extends CipherSpi {
}
}
/**
* Reset / re-create internal native struct for algorithm.
* Should be called during wolfCryptInit() and wolfCryptFinal()
*/
private void InitializeNativeStructs() {
switch (this.cipherType) {
case WC_AES:
if (cipherMode == CipherMode.WC_CBC) {
if (aes != null) {
aes.releaseNativeStruct();
aes = null;
}
aes = new Aes();
}
else if (cipherMode == CipherMode.WC_GCM) {
if (aesGcm != null) {
aesGcm.releaseNativeStruct();
aesGcm = null;
}
aesGcm = new AesGcm();
}
break;
case WC_DES3:
if (des3 != null) {
des3.releaseNativeStruct();
des3 = null;
}
des3 = new Des3();
break;
case WC_RSA:
if (rsa != null) {
rsa.releaseNativeStruct();
rsa = null;
}
rsa = new Rsa();
rsa.setRng(this.rng);
break;
}
}
@Override
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
@ -156,8 +208,9 @@ public class WolfCryptCipher extends CipherSpi {
cipherMode = CipherMode.WC_ECB;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set mode to ECB");
}
}
} else if (mode.equals("CBC")) {
@ -168,14 +221,28 @@ public class WolfCryptCipher extends CipherSpi {
cipherMode = CipherMode.WC_CBC;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set mode to CBC");
}
}
} else if (mode.equals("GCM")) {
/* AES supports GCM */
if (cipherType == CipherType.WC_AES) {
cipherMode = CipherMode.WC_GCM;
supported = 1;
if (debug.DEBUG) {
log("set mode to GCM");
}
}
}
if (supported == 0) {
throw new NoSuchAlgorithmException(
"Unsupported cipher mode for active algorithm choice");
"Unsupported cipher mode for active algorithm choice: " +
mode);
}
}
@ -192,8 +259,9 @@ public class WolfCryptCipher extends CipherSpi {
paddingType = PaddingType.WC_NONE;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set padding to NoPadding");
}
}
} else if (padding.equals("PKCS1Padding")) {
@ -202,24 +270,29 @@ public class WolfCryptCipher extends CipherSpi {
paddingType = PaddingType.WC_PKCS1;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set padding to PKCS1Padding");
}
}
} else if (padding.equals("PKCS5Padding")) {
if (cipherType == CipherType.WC_AES) {
if ((cipherType == CipherType.WC_AES) &&
(cipherMode == CipherMode.WC_CBC)) {
paddingType = PaddingType.WC_PKCS5;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set padding to PKCS5Padding");
}
}
}
if (supported == 0) {
throw new NoSuchPaddingException(
"Unsupported padding type for active algorithm choice");
"Unsupported padding type for active algorithm choice: " +
padding);
}
}
@ -234,12 +307,24 @@ public class WolfCryptCipher extends CipherSpi {
int size = 0;
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
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;
if (cipherMode == CipherMode.WC_GCM) {
/* In AES-GCM mode we append the authentication tag
* to the end of ciphertext */
size = inputLen + this.gcmTagLen;
}
else {
/* 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;
@ -330,21 +415,48 @@ public class WolfCryptCipher extends CipherSpi {
}
} else {
if (!(spec instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"AlgorithmParameterSpec must be of type IvParameterSpec");
if (cipherMode == CipherMode.WC_GCM) {
if (!(spec instanceof GCMParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"AlgorithmParameterSpec must be of type " +
"GCMParameterSpec");
}
GCMParameterSpec gcmSpec = (GCMParameterSpec)spec;
if (gcmSpec.getIV() == null ||
gcmSpec.getIV().length == 0) {
throw new InvalidAlgorithmParameterException(
"AES-GCM IV is null or 0 length");
}
this.iv = gcmSpec.getIV().clone();
/* store tag length as bytes */
if (gcmSpec.getTLen() == 0) {
throw new InvalidAlgorithmParameterException(
"Tag length cannot be zero");
}
this.gcmTagLen = (gcmSpec.getTLen() / 8);
}
else {
if (!(spec instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"AlgorithmParameterSpec must be of type " +
"IvParameterSpec");
}
IvParameterSpec ivSpec = (IvParameterSpec)spec;
IvParameterSpec ivSpec = (IvParameterSpec)spec;
/* IV should be of block size length */
if (ivSpec.getIV().length != this.blockSize) {
throw new InvalidAlgorithmParameterException(
"Bad IV length (" + ivSpec.getIV().length +
"), must be " + blockSize + " bytes long");
/* IV should be of block size length */
if (ivSpec.getIV().length != this.blockSize) {
throw new InvalidAlgorithmParameterException(
"Bad IV length (" + ivSpec.getIV().length +
"), must be " + blockSize + " bytes long");
}
this.iv = ivSpec.getIV().clone();
}
this.iv = ivSpec.getIV();
}
}
@ -380,15 +492,26 @@ public class WolfCryptCipher extends CipherSpi {
/* import key */
encodedKey = key.getEncoded();
if (encodedKey == null)
if (encodedKey == null) {
throw new InvalidKeyException("Key does not support encoding");
}
switch (cipherType) {
case WC_AES:
if (this.direction == OpMode.WC_ENCRYPT) {
this.aes.setKey(encodedKey, iv, Aes.ENCRYPT_MODE);
if (cipherMode == CipherMode.WC_GCM) {
this.aesGcm.setKey(encodedKey);
}
else {
this.aes.setKey(encodedKey, iv, Aes.ENCRYPT_MODE);
}
} else {
this.aes.setKey(encodedKey, iv, Aes.DECRYPT_MODE);
if (cipherMode == CipherMode.WC_GCM) {
this.aesGcm.setKey(encodedKey);
}
else {
this.aes.setKey(encodedKey, iv, Aes.DECRYPT_MODE);
}
}
break;
@ -425,9 +548,12 @@ public class WolfCryptCipher extends CipherSpi {
AlgorithmParameterSpec spec, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
InitializeNativeStructs();
wolfCryptSetDirection(opmode);
wolfCryptSetIV(spec, random);
wolfCryptSetKey(key);
this.operationStarted = false;
this.cipherInitialized = true;
}
@Override
@ -438,8 +564,9 @@ public class WolfCryptCipher extends CipherSpi {
wolfCryptCipherInit(opmode, key, null, random);
if (debug.DEBUG)
if (debug.DEBUG) {
log("initialized with key");
}
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("Invalid algorithm parameters");
@ -453,8 +580,9 @@ public class WolfCryptCipher extends CipherSpi {
wolfCryptCipherInit(opmode, key, params, random);
if (debug.DEBUG)
if (debug.DEBUG) {
log("initialized with key and AlgorithmParameterSpec");
}
}
@Override
@ -466,10 +594,16 @@ public class WolfCryptCipher extends CipherSpi {
try {
spec = params.getParameterSpec(IvParameterSpec.class);
if (this.cipherMode == CipherMode.WC_GCM) {
spec = params.getParameterSpec(GCMParameterSpec.class);
}
else {
spec = params.getParameterSpec(IvParameterSpec.class);
}
if (debug.DEBUG)
if (debug.DEBUG) {
log("initialized with key and AlgorithmParameters");
}
} catch (InvalidParameterSpecException ipe) {
throw new InvalidAlgorithmParameterException(ipe);
@ -490,6 +624,7 @@ public class WolfCryptCipher extends CipherSpi {
break;
default:
isBlockCipher = false;
break;
};
return isBlockCipher;
@ -500,16 +635,19 @@ public class WolfCryptCipher extends CipherSpi {
private int isValidBlockLength(int length) {
/* skip if not a block cipher */
if (isBlockCipher() == false)
if (isBlockCipher() == false) {
return 1;
}
if ((length % this.blockSize) != 0)
if ((length % this.blockSize) != 0) {
return 0;
}
return 1;
}
private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len) {
private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len)
throws IllegalArgumentException {
int blocks = 0;
int remaining = 0;
@ -521,6 +659,8 @@ public class WolfCryptCipher extends CipherSpi {
if (input == null || len < 0)
throw new IllegalArgumentException("Null input buffer or len < 0");
this.operationStarted = true;
if ((buffered.length + len) == 0) {
/* no data to process */
return null;
@ -534,9 +674,12 @@ public class WolfCryptCipher extends CipherSpi {
buffered = tmpIn;
}
/* keep buffered data if RSA or data is less than block size */
if (cipherType == CipherType.WC_RSA ||
buffered.length < blockSize) {
/* keep buffered data if RSA or data is less than block size, or doing
* AES-GCM without stream mode compiled natively */
if ((cipherType == CipherType.WC_RSA) ||
((cipherType == CipherType.WC_AES) &&
(cipherMode == CipherMode.WC_GCM)) ||
(buffered.length < blockSize)) {
return new byte[0];
}
@ -568,6 +711,8 @@ public class WolfCryptCipher extends CipherSpi {
/* process tmpIn[] */
switch (this.cipherType) {
/* Only CBC mode reaches this point currently, GCM caches all
* data internally above until final call */
case WC_AES:
output = this.aes.update(tmpIn, 0, tmpIn.length);
@ -603,9 +748,12 @@ public class WolfCryptCipher extends CipherSpi {
byte tmpIn[] = null;
byte tmpOut[] = null;
this.operationStarted = true;
totalSz = buffered.length + len;
/* AES-GCM does not require block size inputs */
if (isBlockCipher() &&
(cipherMode != CipherMode.WC_GCM) &&
(this.direction == OpMode.WC_DECRYPT ||
(this.direction == OpMode.WC_ENCRYPT &&
this.paddingType != PaddingType.WC_PKCS5)) &&
@ -636,10 +784,39 @@ public class WolfCryptCipher extends CipherSpi {
switch (this.cipherType) {
case WC_AES:
tmpOut = this.aes.update(tmpIn, 0, tmpIn.length);
if (cipherMode == CipherMode.WC_GCM) {
if (this.direction == OpMode.WC_ENCRYPT) {
byte[] tag = new byte[this.gcmTagLen];
tmpOut = this.aesGcm.encrypt(tmpIn, this.iv, tag,
this.aadData);
/* truncate */
tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length);
/* Concatenate auth tag to end of ciphertext */
byte[] totalOut = new byte[tmpOut.length + tag.length];
System.arraycopy(tmpOut, 0, totalOut, 0, tmpOut.length);
System.arraycopy(tag, 0, totalOut, tmpOut.length,
tag.length);
tmpOut = totalOut;
}
else {
/* Get auth tag from end of ciphertext */
byte[] tag = Arrays.copyOfRange(tmpIn,
tmpIn.length - this.gcmTagLen,
tmpIn.length);
/* Shrink ciphertext array down to not include tag */
tmpIn = Arrays.copyOfRange(tmpIn, 0,
tmpIn.length - this.gcmTagLen);
tmpOut = this.aesGcm.decrypt(tmpIn, this.iv, tag,
this.aadData);
}
}
else {
tmpOut = this.aes.update(tmpIn, 0, tmpIn.length);
/* truncate */
tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length);
}
/* strip PKCS#5/PKCS#7 padding if required */
if (this.direction == OpMode.WC_DECRYPT &&
@ -690,15 +867,21 @@ public class WolfCryptCipher extends CipherSpi {
/* reset state, user doesn't need to call init again before use */
try {
buffered = new byte[0];
if (this.direction == OpMode.WC_ENCRYPT) {
wolfCryptSetDirection(Cipher.ENCRYPT_MODE);
} else {
wolfCryptSetDirection(Cipher.DECRYPT_MODE);
}
InitializeNativeStructs();
wolfCryptSetIV(storedSpec, null);
wolfCryptSetKey(storedKey);
this.aadData = null;
this.operationStarted = false;
this.cipherInitialized = true;
} catch (InvalidKeyException e) {
throw new RuntimeException(e.getMessage());
} catch (InvalidAlgorithmParameterException e) {
@ -709,10 +892,16 @@ public class WolfCryptCipher extends CipherSpi {
}
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)
throws IllegalStateException {
byte output[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("update (offset: " + inputOffset + ", len: " +
inputLen + ")");
@ -725,10 +914,15 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
throws IllegalStateException, ShortBufferException {
byte tmpOut[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("update (in offset: " + inputOffset + ", len: " +
inputLen + ", out offset: " + outputOffset + ")");
@ -755,7 +949,13 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
throws IllegalStateException, IllegalBlockSizeException,
BadPaddingException {
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("final (offset: " + inputOffset + ", len: " +
@ -767,11 +967,16 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected int engineDoFinal(byte[] input, int inputOffset,
int inputLen, byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
throws IllegalStateException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
byte tmpOut[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("final (in offset: " + inputOffset + ", len: " +
inputLen + ", out offset: " + outputOffset + ")");
@ -816,6 +1021,71 @@ public class WolfCryptCipher extends CipherSpi {
return encodedKey.length;
}
@Override
protected void engineUpdateAAD(byte[] src, int offset, int len)
throws IllegalArgumentException, IllegalStateException {
if (this.cipherType != CipherType.WC_AES ||
this.cipherMode != CipherMode.WC_GCM) {
throw new IllegalStateException(
"AAD only supported for AES-GCM");
}
if (this.operationStarted) {
throw new IllegalStateException(
"Must set AAD before calling Cipher.update/final");
}
if (!this.cipherInitialized || this.aesGcm == null) {
throw new IllegalStateException(
"Cipher not initialized yet");
}
if (src == null || offset < 0 || len < 0 ||
(src.length < (offset + len))) {
throw new IllegalArgumentException(
"Source buffer is null or bad offset/len");
}
if (this.aadData == null) {
/* Store as new array inside object */
this.aadData = new byte[len];
System.arraycopy(src, offset, this.aadData, 0, len);
}
else {
/* Append to existing AAD array held inside object */
byte[] tmp = new byte[this.aadData.length + len];
System.arraycopy(this.aadData, 0, tmp, 0, this.aadData.length);
System.arraycopy(src, offset, tmp, this.aadData.length, len);
this.aadData = tmp;
}
}
@Override
protected void engineUpdateAAD(ByteBuffer src)
throws IllegalArgumentException, IllegalStateException {
int originalPos = 0;
byte[] remaining = null;
if (src == null) {
throw new IllegalArgumentException("Source buffer is null");
}
originalPos = src.position();
remaining = new byte[src.remaining()];
src.get(remaining);
try {
engineUpdateAAD(remaining, 0, remaining.length);
} catch (IllegalStateException | IllegalArgumentException e) {
/* restore state of ByteBuffer on state error before returning */
src.position(originalPos);
throw e;
}
}
private String typeToString(CipherType type) {
switch (type) {
case WC_AES:
@ -835,6 +1105,8 @@ public class WolfCryptCipher extends CipherSpi {
return "ECB";
case WC_CBC:
return "CBC";
case WC_GCM:
return "GCM";
default:
return "None";
}
@ -848,17 +1120,30 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected void finalize() throws Throwable {
try {
if (this.aes != null)
if (this.aes != null) {
this.aes.releaseNativeStruct();
this.aes = null;
}
if (this.des3 != null)
if (this.aesGcm != null) {
this.aesGcm.releaseNativeStruct();
this.aesGcm = null;
}
if (this.des3 != null) {
this.des3.releaseNativeStruct();
this.des3 = null;
}
if (this.rsa != null)
if (this.rsa != null) {
this.rsa.releaseNativeStruct();
this.rsa = null;
}
if (this.rng != null)
if (this.rng != null) {
this.rng.releaseNativeStruct();
this.rng = null;
}
zeroArray(this.iv);
@ -894,6 +1179,18 @@ public class WolfCryptCipher extends CipherSpi {
}
}
/**
* Class for AES-GCM with no padding
*/
public static final class wcAESGCMNoPadding extends WolfCryptCipher {
/**
* Create new wcAESGCMNoPadding object
*/
public wcAESGCMNoPadding() {
super(CipherType.WC_AES, CipherMode.WC_GCM, PaddingType.WC_NONE);
}
}
/**
* Class for DES-EDE-CBC with no padding
*/

View File

@ -119,34 +119,58 @@ public final class WolfCryptProvider extends Provider {
}
/* Cipher */
put("Cipher.AES/CBC/NoPadding",
if (FeatureDetect.AesCbcEnabled()) {
put("Cipher.AES/CBC/NoPadding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcAESCBCNoPadding");
put("Cipher.AES/CBC/PKCS5Padding",
put("Cipher.AES/CBC/PKCS5Padding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcAESCBCPKCS5Padding");
}
if (FeatureDetect.AesGcmEnabled()) {
put("Cipher.AES/GCM/NoPadding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcAESGCMNoPadding");
}
put("Cipher.DESede/CBC/NoPadding",
if (FeatureDetect.Des3Enabled()) {
put("Cipher.DESede/CBC/NoPadding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcDESedeCBCNoPadding");
}
put("Cipher.RSA",
if (FeatureDetect.RsaEnabled()) {
put("Cipher.RSA",
"com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding");
put("Cipher.RSA/ECB/PKCS1Padding",
put("Cipher.RSA/ECB/PKCS1Padding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding");
}
/* KeyAgreement */
put("KeyAgreement.DiffieHellman",
if (FeatureDetect.DhEnabled()) {
put("KeyAgreement.DiffieHellman",
"com.wolfssl.provider.jce.WolfCryptKeyAgreement$wcDH");
put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");
put("KeyAgreement.ECDH",
put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");
}
if (FeatureDetect.EccDheEnabled()) {
put("KeyAgreement.ECDH",
"com.wolfssl.provider.jce.WolfCryptKeyAgreement$wcECDH");
}
/* KeyPairGenerator */
put("KeyPairGenerator.RSA",
if (FeatureDetect.RsaKeyGenEnabled()) {
put("KeyPairGenerator.RSA",
"com.wolfssl.provider.jce.WolfCryptKeyPairGenerator$wcKeyPairGenRSA");
put("KeyPairGenerator.EC",
}
if (FeatureDetect.EccKeyGenEnabled()) {
put("KeyPairGenerator.EC",
"com.wolfssl.provider.jce.WolfCryptKeyPairGenerator$wcKeyPairGenECC");
put("KeyPairGenerator.DH",
}
if (FeatureDetect.DhEnabled()) {
put("KeyPairGenerator.DH",
"com.wolfssl.provider.jce.WolfCryptKeyPairGenerator$wcKeyPairGenDH");
put("Alg.Alias.KeyPairGenerator.DiffieHellman", "DH");
put("Alg.Alias.KeyPairGenerator.DiffieHellman", "DH");
}
/* KeyStore */
put("KeyStore.WKS",
"com.wolfssl.provider.jce.WolfSSLKeyStore$WolfSSLKeyStoreWKS");
/* If using a FIPS version of wolfCrypt, allow private key to be
* exported for use. Only applicable to FIPS 140-3 */

View File

@ -160,6 +160,49 @@ public class FeatureDetect {
*/
public static native boolean HmacSha512Enabled();
/**
* Tests if RSA is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean RsaEnabled();
/**
* Tests if RSA key generation is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean RsaKeyGenEnabled();
/**
* Tests if DH is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean DhEnabled();
/**
* Tests if ECC is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean EccEnabled();
/**
* Tests if ECC key generation is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean EccKeyGenEnabled();
/**
* Tests if ECDHE / wc_ecc_shared_secret() is compiled into the native
* wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean EccDheEnabled();
/**
* Loads JNI library.
*

View File

@ -34,10 +34,12 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.BadPaddingException;
@ -55,6 +57,7 @@ import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import com.wolfssl.wolfcrypt.WolfCrypt;
import com.wolfssl.wolfcrypt.FeatureDetect;
import com.wolfssl.wolfcrypt.Fips;
import com.wolfssl.provider.jce.WolfCryptProvider;
import com.wolfssl.wolfcrypt.WolfCryptException;
@ -65,6 +68,7 @@ public class WolfCryptCipherTest {
private static String supportedJCEAlgos[] = {
"AES/CBC/NoPadding",
"AES/CBC/PKCS5Padding",
"AES/GCM/NoPadding",
"DESede/CBC/NoPadding",
"RSA",
"RSA/ECB/PKCS1Padding"
@ -111,6 +115,7 @@ public class WolfCryptCipherTest {
/* fill expected block size HashMap */
expectedBlockSizes.put("AES/CBC/NoPadding", 16);
expectedBlockSizes.put("AES/CBC/PKCS5Padding", 16);
expectedBlockSizes.put("AES/GCM/NoPadding", 16);
expectedBlockSizes.put("DESede/CBC/NoPadding", 8);
expectedBlockSizes.put("RSA", 0);
expectedBlockSizes.put("RSA/ECB/PKCS1Padding", 0);
@ -172,7 +177,7 @@ public class WolfCryptCipherTest {
BadPaddingException {
CipherVector vectors[] = new CipherVector[] {
/* test vectors {key, iv, input, output } */
/* test vectors {key, iv, input, output, tag, aad } */
new CipherVector(
new byte[] {
(byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33,
@ -197,7 +202,8 @@ public class WolfCryptCipherTest {
(byte)0x5f, (byte)0x42, (byte)0x81, (byte)0x53,
(byte)0x2c, (byte)0xcc, (byte)0x9d, (byte)0x46,
(byte)0x77, (byte)0xa2, (byte)0x33, (byte)0xcb
}
},
null, null
)
};
@ -215,7 +221,19 @@ public class WolfCryptCipherTest {
SecretKeySpec key = new SecretKeySpec(vectors[i].getKey(), "AES");
IvParameterSpec spec = new IvParameterSpec(vectors[i].getIV());
/* getOutputSize() before init() should throw exception */
try {
cipher.getOutputSize(vectors[i].getInput().length);
fail("getOutputSize() before init() should fail");
} catch (IllegalStateException e) {
/* expected, continue */
}
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
assertEquals(vectors[i].getOutput().length,
cipher.getOutputSize(vectors[i].getInput().length));
output = cipher.doFinal(vectors[i].input);
assertArrayEquals(output, vectors[i].output);
@ -758,7 +776,8 @@ public class WolfCryptCipherTest {
(byte)0x44, (byte)0xaa, (byte)0xb5, (byte)0xf0,
(byte)0x5f, (byte)0x34, (byte)0xb4, (byte)0xde,
(byte)0xb5, (byte)0xbd, (byte)0x2a, (byte)0xbb
}
},
null, null
)
};
@ -1474,6 +1493,772 @@ public class WolfCryptCipherTest {
}
}
/* test vectors {key, iv, input, output, tag, aad } */
CipherVector aesGcmVectors[] = new CipherVector[] {
/* AES-GCM-128 */
/* The following is an interesting test case from the example
* FIPS test vectors for AES-GCM. IVlen = 1 byte */
new CipherVector(
new byte[] { /* key (k3 from test.c) */
(byte)0xbb, (byte)0x01, (byte)0xd7, (byte)0x03,
(byte)0x81, (byte)0x1c, (byte)0x10, (byte)0x1a,
(byte)0x35, (byte)0xe0, (byte)0xff, (byte)0xd2,
(byte)0x91, (byte)0xba, (byte)0xf2, (byte)0x4b
},
new byte[] { /* iv (iv3 from test.c) */
(byte)0xca
},
new byte[] { /* input (p3 from test.c) */
(byte)0x57, (byte)0xce, (byte)0x45, (byte)0x1f,
(byte)0xa5, (byte)0xe2, (byte)0x35, (byte)0xa5,
(byte)0x8e, (byte)0x1a, (byte)0xa2, (byte)0x3b,
(byte)0x77, (byte)0xcb, (byte)0xaf, (byte)0xe2
},
new byte[] { /* output (c3 from test.c) */
(byte)0x6b, (byte)0x5f, (byte)0xb3, (byte)0x9d,
(byte)0xc1, (byte)0xc5, (byte)0x7a, (byte)0x4f,
(byte)0xf3, (byte)0x51, (byte)0x4d, (byte)0xc2,
(byte)0xd5, (byte)0xf0, (byte)0xd0, (byte)0x07
},
new byte[] { /* tag (t3 from test.c) */
(byte)0x06, (byte)0x90, (byte)0xed, (byte)0x01,
(byte)0x34, (byte)0xdd, (byte)0xc6, (byte)0x95,
(byte)0x31, (byte)0x2e, (byte)0x2a, (byte)0xf9,
(byte)0x57, (byte)0x7a, (byte)0x1e, (byte)0xa6
},
new byte[] { /* aad (a3 from test.c) */
(byte)0x40, (byte)0xfc, (byte)0xdc, (byte)0xd7,
(byte)0x4a, (byte)0xd7, (byte)0x8b, (byte)0xf1,
(byte)0x3e, (byte)0x7c, (byte)0x60, (byte)0x55,
(byte)0x50, (byte)0x51, (byte)0xdd, (byte)0x54
}
),
/* AES-GCM-192 */
/* FIPS, QAT and PIC32MZ HW Crypto only support 12-byte IV */
/* Test Case 12, uses same plaintext and AAD data. */
new CipherVector(
new byte[] { /* key (k2 from test.c) */
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08,
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c
},
new byte[] { /* iv (iv2 from test.c) */
(byte)0x93, (byte)0x13, (byte)0x22, (byte)0x5d,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0x55, (byte)0x90, (byte)0x9c, (byte)0x5a,
(byte)0xff, (byte)0x52, (byte)0x69, (byte)0xaa,
(byte)0x6a, (byte)0x7a, (byte)0x95, (byte)0x38,
(byte)0x53, (byte)0x4f, (byte)0x7d, (byte)0xa1,
(byte)0xe4, (byte)0xc3, (byte)0x03, (byte)0xd2,
(byte)0xa3, (byte)0x18, (byte)0xa7, (byte)0x28,
(byte)0xc3, (byte)0xc0, (byte)0xc9, (byte)0x51,
(byte)0x56, (byte)0x80, (byte)0x95, (byte)0x39,
(byte)0xfc, (byte)0xf0, (byte)0xe2, (byte)0x42,
(byte)0x9a, (byte)0x6b, (byte)0x52, (byte)0x54,
(byte)0x16, (byte)0xae, (byte)0xdb, (byte)0xf5,
(byte)0xa0, (byte)0xde, (byte)0x6a, (byte)0x57,
(byte)0xa6, (byte)0x37, (byte)0xb3, (byte)0x9b
},
new byte[] { /* input (p from test.c) */
(byte)0xd9, (byte)0x31, (byte)0x32, (byte)0x25,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0xa5, (byte)0x59, (byte)0x09, (byte)0xc5,
(byte)0xaf, (byte)0xf5, (byte)0x26, (byte)0x9a,
(byte)0x86, (byte)0xa7, (byte)0xa9, (byte)0x53,
(byte)0x15, (byte)0x34, (byte)0xf7, (byte)0xda,
(byte)0x2e, (byte)0x4c, (byte)0x30, (byte)0x3d,
(byte)0x8a, (byte)0x31, (byte)0x8a, (byte)0x72,
(byte)0x1c, (byte)0x3c, (byte)0x0c, (byte)0x95,
(byte)0x95, (byte)0x68, (byte)0x09, (byte)0x53,
(byte)0x2f, (byte)0xcf, (byte)0x0e, (byte)0x24,
(byte)0x49, (byte)0xa6, (byte)0xb5, (byte)0x25,
(byte)0xb1, (byte)0x6a, (byte)0xed, (byte)0xf5,
(byte)0xaa, (byte)0x0d, (byte)0xe6, (byte)0x57,
(byte)0xba, (byte)0x63, (byte)0x7b, (byte)0x39
},
new byte[] { /* output (c2 from test.c) */
(byte)0xd2, (byte)0x7e, (byte)0x88, (byte)0x68,
(byte)0x1c, (byte)0xe3, (byte)0x24, (byte)0x3c,
(byte)0x48, (byte)0x30, (byte)0x16, (byte)0x5a,
(byte)0x8f, (byte)0xdc, (byte)0xf9, (byte)0xff,
(byte)0x1d, (byte)0xe9, (byte)0xa1, (byte)0xd8,
(byte)0xe6, (byte)0xb4, (byte)0x47, (byte)0xef,
(byte)0x6e, (byte)0xf7, (byte)0xb7, (byte)0x98,
(byte)0x28, (byte)0x66, (byte)0x6e, (byte)0x45,
(byte)0x81, (byte)0xe7, (byte)0x90, (byte)0x12,
(byte)0xaf, (byte)0x34, (byte)0xdd, (byte)0xd9,
(byte)0xe2, (byte)0xf0, (byte)0x37, (byte)0x58,
(byte)0x9b, (byte)0x29, (byte)0x2d, (byte)0xb3,
(byte)0xe6, (byte)0x7c, (byte)0x03, (byte)0x67,
(byte)0x45, (byte)0xfa, (byte)0x22, (byte)0xe7,
(byte)0xe9, (byte)0xb7, (byte)0x37, (byte)0x3b
},
new byte[] { /* tag (t2 from test.c) */
(byte)0xdc, (byte)0xf5, (byte)0x66, (byte)0xff,
(byte)0x29, (byte)0x1c, (byte)0x25, (byte)0xbb,
(byte)0xb8, (byte)0x56, (byte)0x8f, (byte)0xc3,
(byte)0xd3, (byte)0x76, (byte)0xa6, (byte)0xd9
},
new byte[] { /* aad (a from test.c) */
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xab, (byte)0xad, (byte)0xda, (byte)0xd2
}
),
/* AES-GCM-256 */
/* This is Test Case 16 from the document Galois/Counter Mode of
* Operation (GCM) by McGrew and Viega. */
new CipherVector(
new byte[] { /* key (k1 from test.c) */
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08,
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08
},
new byte[] { /* iv (iv1 from test.c) */
(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe,
(byte)0xfa, (byte)0xce, (byte)0xdb, (byte)0xad,
(byte)0xde, (byte)0xca, (byte)0xf8, (byte)0x88
},
new byte[] { /* input (p from test.c) */
(byte)0xd9, (byte)0x31, (byte)0x32, (byte)0x25,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0xa5, (byte)0x59, (byte)0x09, (byte)0xc5,
(byte)0xaf, (byte)0xf5, (byte)0x26, (byte)0x9a,
(byte)0x86, (byte)0xa7, (byte)0xa9, (byte)0x53,
(byte)0x15, (byte)0x34, (byte)0xf7, (byte)0xda,
(byte)0x2e, (byte)0x4c, (byte)0x30, (byte)0x3d,
(byte)0x8a, (byte)0x31, (byte)0x8a, (byte)0x72,
(byte)0x1c, (byte)0x3c, (byte)0x0c, (byte)0x95,
(byte)0x95, (byte)0x68, (byte)0x09, (byte)0x53,
(byte)0x2f, (byte)0xcf, (byte)0x0e, (byte)0x24,
(byte)0x49, (byte)0xa6, (byte)0xb5, (byte)0x25,
(byte)0xb1, (byte)0x6a, (byte)0xed, (byte)0xf5,
(byte)0xaa, (byte)0x0d, (byte)0xe6, (byte)0x57,
(byte)0xba, (byte)0x63, (byte)0x7b, (byte)0x39
},
new byte[] { /* output (c1 from test.c) */
(byte)0x52, (byte)0x2d, (byte)0xc1, (byte)0xf0,
(byte)0x99, (byte)0x56, (byte)0x7d, (byte)0x07,
(byte)0xf4, (byte)0x7f, (byte)0x37, (byte)0xa3,
(byte)0x2a, (byte)0x84, (byte)0x42, (byte)0x7d,
(byte)0x64, (byte)0x3a, (byte)0x8c, (byte)0xdc,
(byte)0xbf, (byte)0xe5, (byte)0xc0, (byte)0xc9,
(byte)0x75, (byte)0x98, (byte)0xa2, (byte)0xbd,
(byte)0x25, (byte)0x55, (byte)0xd1, (byte)0xaa,
(byte)0x8c, (byte)0xb0, (byte)0x8e, (byte)0x48,
(byte)0x59, (byte)0x0d, (byte)0xbb, (byte)0x3d,
(byte)0xa7, (byte)0xb0, (byte)0x8b, (byte)0x10,
(byte)0x56, (byte)0x82, (byte)0x88, (byte)0x38,
(byte)0xc5, (byte)0xf6, (byte)0x1e, (byte)0x63,
(byte)0x93, (byte)0xba, (byte)0x7a, (byte)0x0a,
(byte)0xbc, (byte)0xc9, (byte)0xf6, (byte)0x62
},
new byte[] { /* tag (t1 from test.c) */
(byte)0x76, (byte)0xfc, (byte)0x6e, (byte)0xce,
(byte)0x0f, (byte)0x4e, (byte)0x17, (byte)0x68,
(byte)0xcd, (byte)0xdf, (byte)0x88, (byte)0x53,
(byte)0xbb, (byte)0x2d, (byte)0x55, (byte)0x1b
},
new byte[] { /* aad (a from test.c) */
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xab, (byte)0xad, (byte)0xda, (byte)0xd2
}
)
};
/*
* Test Cipher("AES/GCM/NoPadding") processing with single call to
* doFinal().
*/
@Test
public void testAesGcmNoPadding() throws NoSuchAlgorithmException,
InvalidKeyException, IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
byte output[] = null;
byte plain[] = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
for (int i = 0; i < aesGcmVectors.length; i++) {
/* skip AES-128 vector if not compiled in native library */
if ((i == 0) && (!FeatureDetect.Aes128Enabled())) {
continue;
}
/* skip AES-192 vector if not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if ((i == 1) && (!FeatureDetect.Aes192Enabled() || Fips.enabled)) {
continue;
}
/* skip AES-256 vector if not compiled in native library */
if ((i == 2) && (!FeatureDetect.Aes256Enabled())) {
continue;
}
byte[] vOut = aesGcmVectors[i].getOutput();
byte[] vTag = aesGcmVectors[i].getTag();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(
aesGcmVectors[i].getKey(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), aesGcmVectors[i].getIV());
if (i == 0) {
/* getOutputSize() before init() should throw exception */
try {
enc.getOutputSize(aesGcmVectors[i].getInput().length);
fail("getOutputSize() before init() should fail");
} catch (IllegalStateException e) {
/* expected, continue */
}
}
/* Encrypt */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.updateAAD(aesGcmVectors[i].getAAD());
assertEquals(tmpOut.length,
enc.getOutputSize(aesGcmVectors[i].getInput().length));
output = enc.doFinal(aesGcmVectors[i].getInput());
/* Concatenate tag to ciphertext, JCE Cipher does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
assertArrayEquals(tmpOut, output);
/* Decrypt */
dec.init(Cipher.DECRYPT_MODE, key, spec);
dec.updateAAD(aesGcmVectors[i].getAAD());
plain = dec.doFinal(output);
/* plain is just ciphertext, no tag */
assertArrayEquals(aesGcmVectors[i].getInput(), plain);
/* ----- confirm wrong result if no AAD given but needed ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
output = enc.doFinal(aesGcmVectors[i].getInput());
/* Concatenate tag to ciphertext, JCE Cipher does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
if (Arrays.equals(tmpOut, output)) {
fail("Encrypt without needed AAD should not match expected");
}
}
}
/*
* Test Cipher("AES/GCM/NoPadding") with multiple calls to update()
* on both encrypt and decrypt.
*/
@Test
public void testAesGcmNoPaddingWithUpdate()
throws NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
byte tmp[] = null;
byte output[] = null;
byte plain[] = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
for (int i = 0; i < aesGcmVectors.length; i++) {
int inIdx = 0;
int outIdx = 0;
int fourByteBlocks = 0;
int remainingBytes = 0;
byte[] vIn = aesGcmVectors[i].getInput();
byte[] vOut = aesGcmVectors[i].getOutput();
byte[] vTag = aesGcmVectors[i].getTag();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(
aesGcmVectors[i].getKey(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), aesGcmVectors[i].getIV());
/* skip AES-128 vector if not compiled in native library */
if ((i == 0) && (!FeatureDetect.Aes128Enabled())) {
continue;
}
/* skip AES-192 vector if not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if ((i == 1) && (!FeatureDetect.Aes192Enabled() || Fips.enabled)) {
continue;
}
/* skip AES-256 vector if not compiled in native library */
if ((i == 2) && (!FeatureDetect.Aes256Enabled())) {
continue;
}
/* ----- ENCRYPT (vIn -> output) ----- */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.updateAAD(aesGcmVectors[i].getAAD());
/* Expected output is size vOut + vTag.length */
output = new byte[vOut.length + vTag.length];
/* Process via update() by 4-byte blocks */
fourByteBlocks = vIn.length / 4;
remainingBytes = vIn.length % 4;
for (int j = 0; j < fourByteBlocks; j++) {
tmp = enc.update(Arrays.copyOfRange(vIn, inIdx, inIdx + 4));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(4, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, output, outIdx, tmp.length);
inIdx += 4;
outIdx += tmp.length;
}
/* Process any remaining data (CipherVector.input length was not
* a multiple of 4 bytes */
if (remainingBytes > 0) {
tmp = enc.update(Arrays.copyOfRange(vIn, inIdx,
inIdx + remainingBytes));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(remainingBytes, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, output, outIdx, tmp.length);
inIdx += remainingBytes;
outIdx += tmp.length;
}
/* doFinal() should get tag (or whole ciphertext if AES-GCM
* streaming is not enabled at native wolfSSL level) */
tmp = enc.doFinal();
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(vTag.length, tmp.length);
} else {
assertEquals(tmpOut.length, tmp.length);
}
System.arraycopy(tmp, 0, output, outIdx, tmp.length);
outIdx += tmp.length;
/* Sanity check on total length written */
assertEquals(output.length, outIdx);
/* Concatenate tag to end of ciphertext from our test vector, JCE
* Cipher class already does this internally and will be the format
* returned from update/final */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
/* Encrypted matches vector output? */
assertArrayEquals(tmpOut, output);
/* ----- DECRYPT (output -> plain) ----- */
/* Sanity check, makes sure we built up output correctly */
assertArrayEquals(tmpOut, output);
/* Decrypt */
dec.init(Cipher.DECRYPT_MODE, key, spec);
dec.updateAAD(aesGcmVectors[i].getAAD());
/* plain is just plaintext, no tag */
plain = new byte[vIn.length];
/* Process via update() by 4-byte blocks */
fourByteBlocks = output.length / 4;
remainingBytes = output.length % 4;
inIdx = 0;
outIdx = 0;
for (int j = 0; j < fourByteBlocks; j++) {
tmp = dec.update(Arrays.copyOfRange(output, inIdx, inIdx + 4));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(4, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, plain, outIdx, tmp.length);
inIdx += 4;
outIdx += tmp.length;
}
/* Process any remaining data (output length was not
* a multiple of 4 bytes */
if (remainingBytes > 0) {
tmp = dec.update(Arrays.copyOfRange(output, inIdx,
inIdx + remainingBytes));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(remainingBytes, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, plain, outIdx, tmp.length);
inIdx += remainingBytes;
outIdx += tmp.length;
}
/* doFinal() will return whole plaintext if AES-GCM
* streaming is not enabled at native wolfSSL level */
tmp = dec.doFinal();
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(0, tmp.length);
} else {
assertEquals(vIn.length, tmp.length);
}
System.arraycopy(tmp, 0, plain, outIdx, tmp.length);
outIdx += tmp.length;
/* Sanity check on total length written */
assertEquals(plain.length, outIdx);
/* Decrypted matches vector input? */
assertArrayEquals(vIn, plain);
}
}
/*
* Test Cipher("AES/GCM/NoPadding") to make sure updateAAD() correctly
* throws an exception when called after a call to update or doFinal.
*/
@Test
public void testAesGcmNoPaddingUpdateAADByteArray()
throws NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
int i = 0;
byte output[] = null;
byte plain[] = null;
CipherVector vect = null;
byte[] vOut = null;
byte[] vIn = null;
byte[] vKey = null;
byte[] vIV = null;
byte[] vTag = null;
byte[] vAAD = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
/* Try to pick a vector based on what is compiled in natively,
* doesn't matter too much which vector we get */
if (FeatureDetect.Aes128Enabled()) {
vect = aesGcmVectors[0];
}
else if (FeatureDetect.Aes192Enabled() && !Fips.enabled) {
vect = aesGcmVectors[1];
}
else if (FeatureDetect.Aes256Enabled()) {
vect = aesGcmVectors[2];
}
else {
/* No test vector found, skipping test */
return;
}
vOut = vect.getOutput();
vIn = vect.getInput();
vKey = vect.getKey();
vIV = vect.getIV();
vTag = vect.getTag();
vAAD = vect.getAAD();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(vKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), vIV);
/* Encrypt, test calling updateAAD() multiple times */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
enc.updateAAD(new byte[] {vAAD[i]});
}
output = enc.doFinal(vIn);
/* Concatenate tag to end of ciphertext, JCE Cipher class already
* does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
assertArrayEquals(tmpOut, output);
/* Decrypt, test calling updateAAD() multiple times */
dec.init(Cipher.DECRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
dec.updateAAD(new byte[] {vAAD[i]});
}
plain = dec.doFinal(output);
/* plain is just ciphertext, no tag */
assertArrayEquals(vIn, plain);
/* ----- updateAAD() after update() should fail ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.update(vIn);
try {
enc.updateAAD(vAAD);
fail("updateAAD() after update() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
/* ----- updateAAD() throws exception if called before init ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
try {
enc.updateAAD(vAAD);
fail("updateAAD() before init() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
}
/*
* Test Cipher("AES/GCM/NoPadding") to make sure updateAAD() correctly
* throws an exception when called after a call to update or doFinal,
* using ByteBuffer method.
*/
@Test
public void testAesGcmNoPaddingUpdateAADByteBuffer()
throws NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
int i = 0;
byte output[] = null;
byte plain[] = null;
CipherVector vect = null;
byte[] vOut = null;
byte[] vIn = null;
byte[] vKey = null;
byte[] vIV = null;
byte[] vTag = null;
byte[] vAAD = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
/* Try to pick a vector based on what is compiled in natively,
* doesn't matter too much which vector we get */
if (FeatureDetect.Aes128Enabled()) {
vect = aesGcmVectors[0];
}
else if (FeatureDetect.Aes192Enabled() && !Fips.enabled) {
vect = aesGcmVectors[1];
}
else if (FeatureDetect.Aes256Enabled()) {
vect = aesGcmVectors[2];
}
else {
/* No test vector found, skipping test */
return;
}
vOut = vect.getOutput();
vIn = vect.getInput();
vKey = vect.getKey();
vIV = vect.getIV();
vTag = vect.getTag();
vAAD = vect.getAAD();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(vKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), vIV);
/* Encrypt, test calling updateAAD() multiple times */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
enc.updateAAD(ByteBuffer.wrap(new byte[] {vAAD[i]}));
}
output = enc.doFinal(vIn);
/* Concatenate tag to end of ciphertext, JCE Cipher class already
* does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
assertArrayEquals(tmpOut, output);
/* Decrypt, test calling updateAAD() multiple times */
dec.init(Cipher.DECRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
dec.updateAAD(ByteBuffer.wrap(new byte[] {vAAD[i]}));
}
plain = dec.doFinal(output);
/* plain is just ciphertext, no tag */
assertArrayEquals(vIn, plain);
/* ----- updateAAD() after update() should fail ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.update(vIn);
try {
enc.updateAAD(ByteBuffer.wrap(vAAD));
fail("updateAAD() after update() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
/* ----- updateAAD() throws exception if called before init ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
try {
enc.updateAAD(ByteBuffer.wrap(vAAD));
fail("updateAAD() before init() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
}
/*
* Test Cipher("AES/GCM/NoPadding") interop if other provider is available.
*/
@Test
public void testAesGcmNoPaddingInterop() throws NoSuchAlgorithmException,
InvalidKeyException, IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
byte cipher[] = null;
byte plain[] = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
if (interopProvider == null) {
/* no interop provider available, skip */
return;
}
Cipher ciphA = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher ciphB = Cipher.getInstance("AES/GCM/NoPadding", interopProvider);
for (int i = 0; i < aesGcmVectors.length; i++) {
/* skip AES-128 vector if not compiled in native library */
if ((i == 0) && (!FeatureDetect.Aes128Enabled())) {
continue;
}
/* skip AES-192 vector if not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if ((i == 1) && (!FeatureDetect.Aes192Enabled() || Fips.enabled)) {
continue;
}
/* skip AES-256 vector if not compiled in native library */
if ((i == 2) && (!FeatureDetect.Aes256Enabled())) {
continue;
}
byte[] vOut = aesGcmVectors[i].getOutput();
byte[] vIn = aesGcmVectors[i].getInput();
byte[] vKey = aesGcmVectors[i].getKey();
byte[] vIV = aesGcmVectors[i].getIV();
byte[] vTag = aesGcmVectors[i].getTag();
byte[] vAAD = aesGcmVectors[i].getAAD();
/* Concatenate tag to ciphertext, JCE Cipher does this internally */
byte[] tmpOut = new byte[vOut.length + vTag.length];
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
SecretKeySpec key = new SecretKeySpec(vKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), vIV);
/* Encrypt with wolfJCE */
ciphA.init(Cipher.ENCRYPT_MODE, key, spec);
ciphA.updateAAD(vAAD);
cipher = ciphA.doFinal(vIn);
assertArrayEquals(tmpOut, cipher);
/* Decrypt with INTEROP provider */
ciphB.init(Cipher.DECRYPT_MODE, key, spec);
ciphB.updateAAD(vAAD);
plain = ciphB.doFinal(cipher);
assertArrayEquals(vIn, plain);
/* Reset Cipher (same IV can't be used twice) */
ciphA = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
ciphB = Cipher.getInstance("AES/GCM/NoPadding", interopProvider);
/* Encrypt with INTEROP provider */
ciphB.init(Cipher.ENCRYPT_MODE, key, spec);
ciphB.updateAAD(vAAD);
cipher = ciphB.doFinal(vIn);
assertArrayEquals(tmpOut, cipher);
/* Decrypt with wolfJCE */
ciphA.init(Cipher.DECRYPT_MODE, key, spec);
ciphA.updateAAD(vAAD);
plain = ciphA.doFinal(cipher);
assertArrayEquals(vIn, plain);
}
}
@Test
public void testDESedeCbcNoPadding()
throws NoSuchProviderException, NoSuchAlgorithmException,
@ -1511,13 +2296,15 @@ public class WolfCryptCipherTest {
(byte)0x12, (byte)0xd5, (byte)0x08, (byte)0x98,
(byte)0x18, (byte)0x94, (byte)0x15, (byte)0x74,
(byte)0x87, (byte)0x12, (byte)0x7d, (byte)0xb0
}
},
null, null
)
};
byte output[];
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding")) {
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding") ||
!FeatureDetect.Des3Enabled()) {
/* bail out if 3DES is not enabled */
return;
}
@ -1579,7 +2366,8 @@ public class WolfCryptCipherTest {
byte tmp[];
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding")) {
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding") ||
!FeatureDetect.Des3Enabled()) {
/* bail out if 3DES is not enabled */
return;
}
@ -1636,7 +2424,8 @@ public class WolfCryptCipherTest {
(byte)0x90, (byte)0xab, (byte)0xcd, (byte)0xef,
};
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding")) {
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding") ||
!FeatureDetect.Des3Enabled()) {
/* skip if DESede/CBC/NoPadding is not enabled */
return;
}
@ -1742,7 +2531,7 @@ public class WolfCryptCipherTest {
(byte)0x20, (byte)0x6f, (byte)0x66, (byte)0x66,
(byte)0x2e
},
null
null, null, null
)
};
@ -2125,7 +2914,7 @@ public class WolfCryptCipherTest {
(byte)0x20, (byte)0x6f, (byte)0x66, (byte)0x66,
(byte)0x2e
},
null
null, null, null
)
};
@ -2236,13 +3025,17 @@ public class WolfCryptCipherTest {
private byte iv[];
private byte input[];
private byte output[];
private byte tag[]; /* AES-GCM auth tag */
private byte aad[]; /* AES-GCM additional auth data */
public CipherVector(byte[] key, byte[] iv, byte[] input,
byte[] output) {
byte[] output, byte[] tag, byte[] aad) {
this.key = key;
this.iv = iv;
this.input = input;
this.output = output;
this.tag = tag;
this.aad = aad;
}
public void setKey(byte[] key) {
@ -2261,6 +3054,14 @@ public class WolfCryptCipherTest {
this.output = output;
}
public void setTag(byte[] tag) {
this.tag = tag;
}
public void setAad(byte[] aad) {
this.aad = aad;
}
public byte[] getKey() {
return this.key;
}
@ -2276,6 +3077,14 @@ public class WolfCryptCipherTest {
public byte[] getOutput() {
return this.output;
}
public byte[] getTag() {
return this.tag;
}
public byte[] getAAD() {
return this.aad;
}
}
}