/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.bean;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import javax.crypto.SecretKey;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.cryptacular.CiphertextHeader;
import org.cryptacular.adapter.AEADBlockCipherAdapter;
import org.cryptacular.generator.Nonce;
import org.cryptacular.spec.Spec;
/**
* Cipher bean that performs encryption with a block cipher in AEAD mode (e.g. GCM, CCM).
*
* @author Middleware Services
*/
public class AEADBlockCipherBean extends AbstractBlockCipherBean
{
/** Mac size in bits. */
public static final int MAC_SIZE_BITS = 128;
/** AEAD block cipher specification (algorithm, mode, padding). */
private Spec<AEADBlockCipher> blockCipherSpec;
/** Creates a new instance. */
public AEADBlockCipherBean() {}
/**
* Creates a new instance by specifying all properties.
*
* @param blockCipherSpec Block cipher specification.
* @param keyStore Key store containing encryption key.
* @param keyAlias Name of encryption key entry in key store.
* @param keyPassword Password used to decrypt key entry in keystore.
* @param nonce Nonce/IV generator.
*/
public AEADBlockCipherBean(
final Spec<AEADBlockCipher> blockCipherSpec,
final KeyStore keyStore,
final String keyAlias,
final String keyPassword,
final Nonce nonce)
{
super(keyStore, keyAlias, keyPassword, nonce);
setBlockCipherSpec(blockCipherSpec);
}
/** @return Block cipher specification. */
public Spec<AEADBlockCipher> getBlockCipherSpec()
{
return blockCipherSpec;
}
/**
* Sets the AEAD block cipher specification.
*
* @param blockCipherSpec Describes a block cipher in terms of algorithm, mode, and padding.
*/
public void setBlockCipherSpec(final Spec<AEADBlockCipher> blockCipherSpec)
{
this.blockCipherSpec = blockCipherSpec;
}
@Override
public void encrypt(final InputStream input, final OutputStream output)
{
if (blockCipherSpec.toString().endsWith("CCM")) {
throw new UnsupportedOperationException("CCM mode ciphers do not support chunked encryption.");
}
super.encrypt(input, output);
}
@Override
public void decrypt(final InputStream input, final OutputStream output)
{
if (blockCipherSpec.toString().endsWith("CCM")) {
throw new UnsupportedOperationException("CCM mode ciphers do not support chunked decryption.");
}
super.decrypt(input, output);
}
@Override
protected AEADBlockCipherAdapter newCipher(final CiphertextHeader header, final boolean mode)
{
final AEADBlockCipher cipher = blockCipherSpec.newInstance();
final SecretKey key = lookupKey(header.getKeyName());
final AEADParameters params = new AEADParameters(
new KeyParameter(key.getEncoded()),
MAC_SIZE_BITS,
header.getNonce(),
header.encode());
cipher.init(mode, params);
return new AEADBlockCipherAdapter(cipher);
}
}