/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import javax.crypto.SecretKey;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.BlowfishEngine;
import org.bouncycastle.crypto.engines.TwofishEngine;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.modes.CCMBlockCipher;
import org.bouncycastle.crypto.modes.CFBBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.modes.OCBBlockCipher;
import org.bouncycastle.crypto.modes.OFBBlockCipher;
import org.cryptacular.FailListener;
import org.cryptacular.generator.Nonce;
import org.cryptacular.generator.SecretKeyGenerator;
import org.cryptacular.generator.sp80038a.LongCounterNonce;
import org.cryptacular.generator.sp80038a.RBGNonce;
import org.cryptacular.generator.sp80038d.CounterNonce;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* Unit test for {@link CipherUtil} class.
*
* @author Middleware Services
*/
@Listeners(FailListener.class)
public class CipherUtilTest
{
@DataProvider(name = "block-cipher")
public Object[][] getBlockCipherData()
{
return
new Object[][] {
new Object[] {
// Plaintext is NOT multiple of block size
"Able was I ere I saw elba.",
new CBCBlockCipher(new AESEngine()),
new RBGNonce(16),
},
// Plaintext is multiple of block size
new Object[] {
"Four score and seven years ago, our forefathers ",
new CBCBlockCipher(new BlowfishEngine()),
new RBGNonce(8),
},
// OFB
new Object[] {
"Have you passed through this night?",
new OFBBlockCipher(new BlowfishEngine(), 64),
new LongCounterNonce(),
},
// CFB
new Object[] {
"I went to the woods because I wished to live deliberately, to front only the essential facts of life",
new CFBBlockCipher(new AESEngine(), 128),
new RBGNonce(16),
},
};
}
@DataProvider(name = "aead-block-cipher")
public Object[][] getAeadBlockCipherData()
{
return
new Object[][] {
new Object[] {
// Plaintext is NOT multiple of block size
"I never picked cotton like my mother did",
new GCMBlockCipher(new AESEngine()),
},
new Object[] {
// Plaintext is multiple of block size
"Cogito ergo sum.",
new GCMBlockCipher(new AESEngine()),
},
// CCM
new Object[] {
"Thousands of candles can be lit from a single candle and the life of the candle will not be shortened.",
new CCMBlockCipher(new TwofishEngine()),
},
// OCB
new Object[] {
"I slept and dreamt life was joy. I awoke and saw that life was service. " +
"I acted and behold: service was joy.",
new OCBBlockCipher(new AESEngine(), new AESEngine()),
},
};
}
@DataProvider(name = "plaintext-files")
public Object[][] getPlaintextFiles()
{
return
new Object[][] {
new Object[] {"src/test/resources/plaintexts/lorem-1200.txt"},
new Object[] {"src/test/resources/plaintexts/lorem-5000.txt"},
};
}
@Test(dataProvider = "block-cipher")
public void testBlockCipherEncryptDecrypt(final String plaintext, final BlockCipher cipher, final Nonce nonce)
{
final SecretKey key = SecretKeyGenerator.generate(cipher);
final byte[] ciphertext = CipherUtil.encrypt(cipher, key, nonce, plaintext.getBytes());
final byte[] result = CipherUtil.decrypt(cipher, key, ciphertext);
assertEquals(new String(result), plaintext);
}
@Test(dataProvider = "aead-block-cipher")
public void testAeadBlockCipherEncryptDecrypt(final String plaintext, final AEADBlockCipher cipher)
{
final BlockCipher under = cipher.getUnderlyingCipher();
final SecretKey key = SecretKeyGenerator.generate(under);
final byte[] ciphertext = CipherUtil.encrypt(cipher, key, new RBGNonce(12), plaintext.getBytes());
final byte[] result = CipherUtil.decrypt(cipher, key, ciphertext);
assertEquals(new String(result), plaintext);
}
@Test(dataProvider = "plaintext-files")
public void testBlockCipherEncryptDecryptStream(final String path)
throws Exception
{
final BlockCipher cipher = new CBCBlockCipher(new AESEngine());
final SecretKey key = SecretKeyGenerator.generate(cipher);
final Nonce nonce = new CounterNonce("vt-crypt", 1);
final File file = new File(path);
final String expected = new String(StreamUtil.readAll(file));
final ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
CipherUtil.encrypt(cipher, key, nonce, StreamUtil.makeStream(file), tempOut);
final ByteArrayInputStream tempIn = new ByteArrayInputStream(tempOut.toByteArray());
final ByteArrayOutputStream actual = new ByteArrayOutputStream();
CipherUtil.decrypt(cipher, key, tempIn, actual);
assertEquals(new String(actual.toByteArray()), expected);
}
@Test(dataProvider = "plaintext-files")
public void testAeadBlockCipherEncryptDecryptStream(final String path)
throws Exception
{
final AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
final SecretKey key = SecretKeyGenerator.generate(cipher.getUnderlyingCipher());
final File file = new File(path);
final String expected = new String(StreamUtil.readAll(file));
final ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
CipherUtil.encrypt(cipher, key, new RBGNonce(), StreamUtil.makeStream(file), tempOut);
final ByteArrayInputStream tempIn = new ByteArrayInputStream(tempOut.toByteArray());
final ByteArrayOutputStream actual = new ByteArrayOutputStream();
CipherUtil.decrypt(cipher, key, tempIn, actual);
assertEquals(new String(actual.toByteArray()), expected);
}
}