wolfcrypt-jni/src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java

1195 lines
36 KiB
Java

/* WolfCryptCipher.java
*
* Copyright (C) 2006-2024 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
package com.wolfssl.provider.jce;
import java.util.Arrays;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.SecretKey;
import javax.crypto.BadPaddingException;
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;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.interfaces.RSAPrivateKey;
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;
import com.wolfssl.provider.jce.WolfCryptDebug;
/**
* wolfCrypt JCE Cipher (AES, 3DES) wrapper
*/
public class WolfCryptCipher extends CipherSpi {
enum CipherType {
WC_AES,
WC_DES3,
WC_RSA
}
enum CipherMode {
WC_ECB,
WC_CBC,
WC_GCM
}
enum PaddingType {
WC_NONE,
WC_PKCS1,
WC_PKCS5
}
enum OpMode {
WC_ENCRYPT,
WC_DECRYPT
}
enum RsaKeyType {
WC_RSA_PRIVATE,
WC_RSA_PUBLIC
}
private CipherType cipherType = null;
private CipherMode cipherMode = null;
private PaddingType paddingType = null;
private OpMode direction = null;
private RsaKeyType rsaKeyType = null;
private int blockSize = 0;
private Aes aes = null;
private AesGcm aesGcm = null;
private Des3 des3 = null;
private Rsa rsa = null;
private Rng rng = null;
/* for debug logging */
private String algString;
private String algMode;
/* stash key and IV here for easy lookup */
private Key storedKey = null;
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];
private WolfCryptCipher(CipherType type, CipherMode mode,
PaddingType pad) {
this.cipherType = type;
this.cipherMode = mode;
this.paddingType = pad;
this.rng = new Rng();
this.rng.init();
switch (cipherType) {
case WC_AES:
blockSize = Aes.BLOCK_SIZE;
break;
case WC_DES3:
blockSize = Des3.BLOCK_SIZE;
break;
}
if (WolfCryptDebug.DEBUG) {
algString = typeToString(cipherType);
algMode = modeToString(cipherMode);
}
}
/**
* 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 {
int supported = 0;
if (mode.equals("ECB")) {
/* RSA is ECB mode */
if (cipherType == CipherType.WC_RSA) {
cipherMode = CipherMode.WC_ECB;
supported = 1;
log("set mode to ECB");
}
} else if (mode.equals("CBC")) {
/* AES and 3DES support CBC */
if (cipherType == CipherType.WC_AES ||
cipherType == CipherType.WC_DES3 ) {
cipherMode = CipherMode.WC_CBC;
supported = 1;
log("set mode to CBC");
}
} else if (mode.equals("GCM")) {
/* AES supports GCM */
if (cipherType == CipherType.WC_AES) {
cipherMode = CipherMode.WC_GCM;
supported = 1;
log("set mode to GCM");
}
}
if (supported == 0) {
throw new NoSuchAlgorithmException(
"Unsupported cipher mode for active algorithm choice: " +
mode);
}
}
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
int supported = 0;
if (padding.equals("NoPadding")) {
if (cipherType == CipherType.WC_AES ||
cipherType == CipherType.WC_DES3) {
paddingType = PaddingType.WC_NONE;
supported = 1;
log("set padding to NoPadding");
}
} else if (padding.equals("PKCS1Padding")) {
if (cipherType == CipherType.WC_RSA) {
paddingType = PaddingType.WC_PKCS1;
supported = 1;
log("set padding to PKCS1Padding");
}
} else if (padding.equals("PKCS5Padding")) {
if ((cipherType == CipherType.WC_AES) &&
(cipherMode == CipherMode.WC_CBC)) {
paddingType = PaddingType.WC_PKCS5;
supported = 1;
log("set padding to PKCS5Padding");
}
}
if (supported == 0) {
throw new NoSuchPaddingException(
"Unsupported padding type for active algorithm choice: " +
padding);
}
}
@Override
protected int engineGetBlockSize() {
return this.blockSize;
}
@Override
protected int engineGetOutputSize(int inputLen)
throws IllegalStateException {
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) {
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;
size += Aes.getPKCS7PadSize(size, Aes.BLOCK_SIZE);
}
else {
throw new IllegalStateException(
"Unsupported padding mode for Cipher Aes");
}
break;
case WC_DES3:
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:
size = this.rsa.getEncryptSize();
break;
}
return size;
}
@Override
protected byte[] engineGetIV() {
return this.iv;
}
@Override
protected AlgorithmParameters engineGetParameters() {
/* not currently supported by wolfCrypt JCE provider */
return null;
}
private void wolfCryptSetDirection(int opmode)
throws InvalidKeyException {
/* we don't currently support AES key wrap in JCE yet,
* so don't allow WRAP_MODE or UNWRAP_MODE */
switch (opmode) {
case Cipher.ENCRYPT_MODE:
this.direction = OpMode.WC_ENCRYPT;
break;
case Cipher.DECRYPT_MODE:
this.direction = OpMode.WC_DECRYPT;
break;
default:
throw new InvalidKeyException(
"Cipher opmode must be ENCRYPT_MODE or DECRPYT_MODE");
}
}
private void wolfCryptSetIV(AlgorithmParameterSpec spec,
SecureRandom random) throws InvalidAlgorithmParameterException {
/* store AlgorithmParameterSpec for class reset */
this.storedSpec = spec;
/* RSA doesn't need an IV */
if (this.cipherType == CipherType.WC_RSA)
return;
/* store IV, or generate random IV if not available */
if (spec == null) {
this.iv = new byte[this.blockSize];
if (random != null) {
random.nextBytes(this.iv);
} else {
SecureRandom rand = new SecureRandom();
rand.nextBytes(this.iv);
}
} else {
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;
/* 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();
}
}
}
private void wolfCryptSetKey(Key key)
throws InvalidKeyException {
int ret = 0;
long[] idx = {0};
byte[] encodedKey;
/* validate key class type */
if (this.cipherType == CipherType.WC_RSA) {
if (key instanceof RSAPrivateKey) {
this.rsaKeyType = RsaKeyType.WC_RSA_PRIVATE;
} else if (key instanceof RSAPublicKey) {
this.rsaKeyType = RsaKeyType.WC_RSA_PUBLIC;
} else {
throw new InvalidKeyException(
"Cipher key must be of type RSAPrivateKey or " +
"RSAPublicKey when used for RSA encrypt or decrypt");
}
} else if (!(key instanceof SecretKey)) {
throw new InvalidKeyException(
"Cipher key must be of type SecretKey");
}
/* save key for class state resets */
this.storedKey = key;
/* import key */
encodedKey = key.getEncoded();
if (encodedKey == null) {
throw new InvalidKeyException("Key does not support encoding");
}
switch (cipherType) {
case WC_AES:
if (this.direction == OpMode.WC_ENCRYPT) {
if (cipherMode == CipherMode.WC_GCM) {
this.aesGcm.setKey(encodedKey);
}
else {
this.aes.setKey(encodedKey, iv, Aes.ENCRYPT_MODE);
}
} else {
if (cipherMode == CipherMode.WC_GCM) {
this.aesGcm.setKey(encodedKey);
}
else {
this.aes.setKey(encodedKey, iv, Aes.DECRYPT_MODE);
}
}
break;
case WC_DES3:
if (this.direction == OpMode.WC_ENCRYPT) {
this.des3.setKey(encodedKey, iv, Des3.ENCRYPT_MODE);
} else {
this.des3.setKey(encodedKey, iv, Des3.DECRYPT_MODE);
}
break;
case WC_RSA:
/* reset key struct if needed */
if (this.rsa != null)
this.rsa.releaseNativeStruct();
this.rsa = new Rsa();
this.rsa.setRng(this.rng);
if (this.rsaKeyType == RsaKeyType.WC_RSA_PRIVATE) {
this.rsa.decodePrivateKeyPKCS8(encodedKey);
} else {
this.rsa.decodePublicKey(encodedKey);
}
break;
}
}
/* called by engineInit() functions */
private void wolfCryptCipherInit(int opmode, Key key,
AlgorithmParameterSpec spec, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
InitializeNativeStructs();
wolfCryptSetDirection(opmode);
wolfCryptSetIV(spec, random);
wolfCryptSetKey(key);
this.operationStarted = false;
this.cipherInitialized = true;
}
@Override
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
try {
wolfCryptCipherInit(opmode, key, null, random);
log("initialized with key");
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("Invalid algorithm parameters");
}
}
@Override
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
wolfCryptCipherInit(opmode, key, params, random);
log("initialized with key and AlgorithmParameterSpec");
}
@Override
protected void engineInit(int opmode, Key key,
AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
AlgorithmParameterSpec spec;
try {
if (this.cipherMode == CipherMode.WC_GCM) {
spec = params.getParameterSpec(GCMParameterSpec.class);
}
else {
spec = params.getParameterSpec(IvParameterSpec.class);
}
log("initialized with key and AlgorithmParameters");
} catch (InvalidParameterSpecException ipe) {
throw new InvalidAlgorithmParameterException(ipe);
}
wolfCryptCipherInit(opmode, key, spec, random);
}
/* return 1 if this is a block cipher, otherwise 0 */
private boolean isBlockCipher() {
boolean isBlockCipher = false;
switch (this.cipherType) {
case WC_AES:
case WC_DES3:
isBlockCipher = true;
break;
default:
isBlockCipher = false;
break;
};
return isBlockCipher;
}
/* return 1 if cipher is a block cipher and lenth is a block
* length multiple, otherwise 0 */
private int isValidBlockLength(int length) {
/* skip if not a block cipher */
if (isBlockCipher() == false) {
return 1;
}
if ((length % this.blockSize) != 0) {
return 0;
}
return 1;
}
private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len)
throws IllegalArgumentException {
int blocks = 0;
int remaining = 0;
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");
this.operationStarted = true;
if ((buffered.length + len) == 0) {
/* no data to process */
return null;
}
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;
}
/* 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];
}
/* 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 */
tmpBuf = new byte[buffered.length - bytesToProcess];
System.arraycopy(buffered, bytesToProcess, tmpBuf, 0, tmpBuf.length);
buffered = tmpBuf;
/* 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);
/* truncate */
output = Arrays.copyOfRange(output, 0, tmpIn.length);
break;
case WC_DES3:
output = this.des3.update(tmpIn, 0, tmpIn.length);
/* truncate */
output = Arrays.copyOfRange(output, 0, tmpIn.length);
break;
default:
throw new RuntimeException("Unsupported algorithm type");
};
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)
throws IllegalBlockSizeException, BadPaddingException {
int totalSz = 0;
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)) &&
(totalSz % blockSize != 0)) {
throw new IllegalBlockSizeException(
"Input length not multiple of " + blockSize + " bytes");
}
/* do final encrypt over totalSz */
tmpIn = new byte[totalSz];
System.arraycopy(buffered, 0, tmpIn, 0, buffered.length);
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) {
case WC_AES:
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);
/* 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 &&
this.paddingType == PaddingType.WC_PKCS5) {
tmpOut = Aes.unPadPKCS7(tmpOut, Aes.BLOCK_SIZE);
}
break;
case WC_DES3:
tmpOut = this.des3.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 &&
this.paddingType == PaddingType.WC_PKCS5) {
tmpOut = Des3.unPadPKCS7(tmpOut, Des3.BLOCK_SIZE);
}
break;
case WC_RSA:
if (this.direction == OpMode.WC_ENCRYPT) {
if (this.rsaKeyType == RsaKeyType.WC_RSA_PRIVATE) {
tmpOut = this.rsa.sign(tmpIn, this.rng);
} else {
tmpOut = this.rsa.encrypt(tmpIn, this.rng);
}
} else {
if (this.rsaKeyType == RsaKeyType.WC_RSA_PRIVATE) {
tmpOut = this.rsa.decrypt(tmpIn);
} else {
tmpOut = this.rsa.verify(tmpIn);
}
}
break;
default:
throw new RuntimeException("Unsupported algorithm type");
};
/* 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) {
throw new RuntimeException(e.getMessage());
}
return tmpOut;
}
@Override
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");
}
log("update (offset: " + inputOffset + ", len: " + inputLen + ")");
output = wolfCryptUpdate(input, inputOffset, inputLen);
return output;
}
@Override
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalStateException, ShortBufferException {
byte tmpOut[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
log("update (in offset: " + inputOffset + ", len: " +
inputLen + ", out offset: " + outputOffset + ")");
tmpOut = wolfCryptUpdate(input, inputOffset, inputLen);
if (tmpOut == null) {
return 0;
}
System.arraycopy(tmpOut, 0, output, outputOffset, tmpOut.length);
return tmpOut.length;
}
private void zeroArray(byte[] in) {
if (in == null)
return;
for (int i = 0; i < in.length; i++) {
in[i] = 0;
}
}
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalStateException, IllegalBlockSizeException,
BadPaddingException {
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
log("final (offset: " + inputOffset + ", len: " + inputLen + ")");
return wolfCryptFinal(input, inputOffset, inputLen);
}
@Override
protected int engineDoFinal(byte[] input, int inputOffset,
int inputLen, byte[] output, int outputOffset)
throws IllegalStateException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
byte tmpOut[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
log("final (in offset: " + inputOffset + ", len: " +
inputLen + ", out offset: " + outputOffset + ")");
tmpOut = wolfCryptFinal(input, inputOffset, inputLen);
System.arraycopy(tmpOut, 0, output, outputOffset, tmpOut.length);
return tmpOut.length;
}
@Override
protected int engineGetKeySize(Key key)
throws InvalidKeyException {
byte encodedKey[] = null;
/* validate key class type */
if (this.cipherType == CipherType.WC_RSA) {
if (key instanceof RSAPrivateKey) {
this.rsaKeyType = RsaKeyType.WC_RSA_PRIVATE;
} else if (key instanceof RSAPublicKey) {
this.rsaKeyType = RsaKeyType.WC_RSA_PUBLIC;
} else {
throw new InvalidKeyException(
"Cipher key must be of type RSAPrivateKey or " +
"RSAPublicKey when used for RSA encrypt or decrypt");
}
} else if (!(key instanceof SecretKey)) {
throw new InvalidKeyException(
"Cipher key must be of type SecretKey");
}
encodedKey = key.getEncoded();
if (encodedKey == null)
throw new InvalidKeyException("Key does not support encoding");
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:
return "AES";
case WC_DES3:
return "3DES";
case WC_RSA:
return "RSA";
default:
return "None";
}
}
private String modeToString(CipherMode type) {
switch (type) {
case WC_ECB:
return "ECB";
case WC_CBC:
return "CBC";
case WC_GCM:
return "GCM";
default:
return "None";
}
}
private void log(String msg) {
WolfCryptDebug.print("[Cipher, " + algString + "-" +
algMode + "] " + msg);
}
@SuppressWarnings("deprecation")
@Override
protected void finalize() throws Throwable {
try {
if (this.aes != null) {
this.aes.releaseNativeStruct();
this.aes = 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) {
this.rsa.releaseNativeStruct();
this.rsa = null;
}
if (this.rng != null) {
this.rng.releaseNativeStruct();
this.rng = null;
}
zeroArray(this.iv);
this.storedKey = null;
this.storedSpec = null;
} finally {
super.finalize();
}
}
/**
* Class for AES-CBC with no padding
*/
public static final class wcAESCBCNoPadding extends WolfCryptCipher {
/**
* Create new wcAESCBCNoPadding object
*/
public wcAESCBCNoPadding() {
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 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
*/
public static final class wcDESedeCBCNoPadding extends WolfCryptCipher {
/**
* Create new wcDESedeCBCNoPadding object
*/
public wcDESedeCBCNoPadding() {
super(CipherType.WC_DES3, CipherMode.WC_CBC, PaddingType.WC_NONE);
}
}
/**
* Class for RSA-ECB with PKCS1 padding
*/
public static final class wcRSAECBPKCS1Padding extends WolfCryptCipher {
/**
* Create new wcRSAECBPKCS1Padding object
*/
public wcRSAECBPKCS1Padding() {
super(CipherType.WC_RSA, CipherMode.WC_ECB, PaddingType.WC_PKCS1);
}
}
}