package freenet.crypt;
import java.util.Arrays;
import junit.framework.TestCase;
import freenet.crypt.ciphers.Rijndael;
import freenet.support.HexUtil;
import freenet.support.math.MersenneTwister;
// 256,256 PCFB is the same as 256,256 CFB, however JCA does not support 256-bit block size, so we can't
// test against JCA. We will move to the standard block size, and stop using PCFB, eventually, but we'll
// need PCFB for a while if only for old keys, so we need to test it.
public class PCFBModeTest extends TestCase {
private MersenneTwister mt = new MersenneTwister(1634);
// FIXME I don't think there are any standard test vectors?
byte[] PCFB_256_ENCRYPT_KEY = HexUtil
.hexToBytes("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
// FIXME This IV was tailored for CTR mode and 128-bit block, maybe needs adjustement
byte[] PCFB_256_ENCRYPT_IV = HexUtil
.hexToBytes("f0f1f2f3f4f5f6f7f8f9fafbfcfdfefff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
// FIXME This plaintext was tailored for 128-bit block, maybe needs adjustement
byte[] PCFB_256_ENCRYPT_PLAINTEXT = HexUtil
.hexToBytes("6bc1bee22e409f96e93d7e117393172a"
+ "ae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52ef"
+ "f69f2445df4f9b17ad2b417be66c3710");
byte[] PCFB_256_ENCRYPT_CIPHERTEXT = HexUtil
.hexToBytes("c964b00326e216214f1a68f5b0872608"
+ "1b403c92fe02898664a81f5bbbbf8341"
+ "fc1d04b2c1addfb826cca1eab6813127"
+ "2751b9d6cd536f78059b10b4867dbbd9");
byte[] PCFB_256_DECRYPT_KEY = PCFB_256_ENCRYPT_KEY;
byte[] PCFB_256_DECRYPT_IV = PCFB_256_ENCRYPT_IV;
byte[] PCFB_256_DECRYPT_PLAINTEXT = PCFB_256_ENCRYPT_PLAINTEXT;
byte[] PCFB_256_DECRYPT_CIPHERTEXT = PCFB_256_ENCRYPT_CIPHERTEXT;
public void testKnownValues() throws UnsupportedCipherException {
// Rijndael(256,256)
checkKnownValues(256, PCFB_256_ENCRYPT_KEY, PCFB_256_ENCRYPT_IV,
PCFB_256_ENCRYPT_PLAINTEXT, PCFB_256_ENCRYPT_CIPHERTEXT);
checkKnownValues(256, PCFB_256_DECRYPT_KEY, PCFB_256_DECRYPT_IV,
PCFB_256_DECRYPT_PLAINTEXT, PCFB_256_DECRYPT_CIPHERTEXT);
}
public void testKnownValuesRandomLength() throws UnsupportedCipherException {
// Rijndael(256,256)
checkKnownValuesRandomLength(256, PCFB_256_ENCRYPT_KEY, PCFB_256_ENCRYPT_IV,
PCFB_256_ENCRYPT_PLAINTEXT, PCFB_256_ENCRYPT_CIPHERTEXT);
checkKnownValuesRandomLength(256, PCFB_256_DECRYPT_KEY, PCFB_256_DECRYPT_IV,
PCFB_256_DECRYPT_PLAINTEXT, PCFB_256_DECRYPT_CIPHERTEXT);
}
private void checkKnownValues(int bits, byte[] key, byte[] iv, byte[] plaintext,
byte[] ciphertext) throws UnsupportedCipherException {
Rijndael cipher = new Rijndael(bits, bits);
cipher.initialize(key);
PCFBMode ctr = PCFBMode.create(cipher);
ctr.reset(iv);
byte[] output = new byte[plaintext.length];
System.arraycopy(plaintext, 0, output, 0, plaintext.length);
//ctr.blockEncipher(plaintext, 0, plaintext.length, output, 0);
ctr.blockEncipher(output, 0, output.length);
//System.out.println(HexUtil.bytesToHex(output));
assertTrue(Arrays.equals(output, ciphertext));
ctr.reset(iv);
//ctr.blockDecipher(output, 0, output.length, output, 0);
ctr.blockDecipher(output, 0, output.length);
assertTrue(Arrays.equals(output, plaintext));
}
private void checkKnownValuesRandomLength(int bits, byte[] key, byte[] iv,
byte[] plaintext, byte[] ciphertext)
throws UnsupportedCipherException {
for (int i = 0; i < 1024; i++) {
long seed = mt.nextLong();
Rijndael cipher = new Rijndael(bits, bits);
cipher.initialize(key);
PCFBMode ctr = PCFBMode.create(cipher);
ctr.reset(iv);
byte[] output = new byte[plaintext.length];
MersenneTwister random = new MersenneTwister(seed);
int ptr = 0;
System.arraycopy(plaintext, 0, output, 0, plaintext.length);
while (ptr < plaintext.length) {
int max = plaintext.length - ptr;
int count = (max == 1) ? 1 : (random.nextInt(max - 1) + 1);
/*ctr.blockEncipher(plaintext, ptr, count, output, ptr);*/
ctr.blockEncipher(output, ptr, count);
ptr += count;
}
assertTrue(Arrays.equals(output, ciphertext));
ctr.reset(iv);
ptr = 0;
while (ptr < plaintext.length) {
int max = plaintext.length - ptr;
int count = (max == 1) ? 1 : (random.nextInt(max - 1) + 1);
/*ctr.blockDecipher(output, ptr, count, output, ptr);*/
ctr.blockDecipher(output, ptr, count);
ptr += count;
}
assertTrue(Arrays.equals(output, plaintext));
}
}
public void testRandom() throws UnsupportedCipherException {
for(int i=0;i<1024;i++) {
byte[] plaintext = new byte[mt.nextInt(4096)+1];
byte[] key = new byte[32];
byte[] iv = new byte[32];
mt.nextBytes(plaintext);
mt.nextBytes(key);
mt.nextBytes(iv);
// First encrypt as a block.
Rijndael cipher = new Rijndael(256, 256);
cipher.initialize(key);
PCFBMode ctr = PCFBMode.create(cipher);
ctr.reset(iv);
byte[] ciphertext = new byte[plaintext.length];
System.arraycopy(plaintext, 0, ciphertext, 0, ciphertext.length);
//ctr.blockEncipher(plaintext, 0, plaintext.length, ciphertext, 0);
ctr.blockEncipher(ciphertext, 0, ciphertext.length);
// Now decrypt.
ctr = PCFBMode.create(cipher);
ctr.reset(iv);
byte[] finalPlaintext = new byte[plaintext.length];
System.arraycopy(ciphertext, 0, finalPlaintext, 0, ciphertext.length);
//ctr.blockDecipher(ciphertext, 0, ciphertext.length, finalPlaintext, 0);
ctr.blockDecipher(finalPlaintext, 0, finalPlaintext.length);
assertTrue(Arrays.equals(finalPlaintext, plaintext));
// Now encrypt again, in random pieces.
cipher.initialize(key);
ctr = PCFBMode.create(cipher);
ctr.reset(iv);
byte[] output = new byte[plaintext.length];
MersenneTwister random = new MersenneTwister(mt.nextLong());
int ptr = 0;
System.arraycopy(plaintext, 0, output, 0, plaintext.length);
while (ptr < plaintext.length) {
int max = plaintext.length - ptr;
int count = (max == 1) ? 1 : (random.nextInt(max - 1) + 1);
//ctr.blockEncipher(plaintext, ptr, count, output, ptr);
ctr.blockEncipher(output, ptr, count);
ptr += count;
}
assertTrue(Arrays.equals(output, ciphertext));
// ... and decrypt again, in random pieces.
ptr = 0;
ctr.reset(iv);
while (ptr < plaintext.length) {
int max = plaintext.length - ptr;
int count = (max == 1) ? 1 : (random.nextInt(max - 1) + 1);
//ctr.blockDecipher(output, ptr, count, output, ptr);
ctr.blockDecipher(output, ptr, count);
ptr += count;
}
assertTrue(Arrays.equals(output, plaintext));
}
}
}