/** Copied from Bouncycastle v147/SICBlockCipher.java. Unfortunately we can't use their
* JCE without sorting out the policy files issues. Bouncycastle is MIT X licensed i.e.
* GPL compatible. */
package freenet.crypt;
/**
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
* block cipher. This mode is also known as CTR mode.
*/
public class CTRBlockCipher
{
/** Block cipher */
private final BlockCipher cipher;
/** Block size in bytes.
* Equal to IV.length = counter.length = counterOut.length. */
private final int blockSize;
/** Initialization vector, equal to the initial value of the plaintext
* counter. */
private final byte[] IV;
/** The plaintext block counter. This is incremented (from [31]
* backwards) after each block encryption. */
private final byte[] counter;
/** The ciphertext block counter. This is the result of encrypting the
* plaintext block counter. It is XOR'ed with the plaintext to get the
* ciphertext. */
private final byte[] counterOut;
/** Offset within the current block. */
private int blockOffset;
/**
* Basic constructor.
*
* @param c the block cipher to be used.
*/
public CTRBlockCipher(BlockCipher c)
{
this.cipher = c;
this.blockSize = cipher.getBlockSize()/8;
this.IV = new byte[blockSize];
this.counter = new byte[blockSize];
this.counterOut = new byte[blockSize];
this.blockOffset = IV.length;
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public BlockCipher getUnderlyingCipher()
{
return cipher;
}
/** Initialize the cipher with an IV. Must only be called once for any
* given IV!
* @param iv The initialization vector. This is the initial value of
* the plaintext counter. The plaintext is XORed with a sequence of
* bytes consisting of the encryption of successive values of the counter.
* @throws IllegalArgumentException If the IV length is wrong.
*/
public void init(byte[] iv, int offset, int length)
throws IllegalArgumentException
{
if(length != IV.length)
throw new IllegalArgumentException();
System.arraycopy(iv, offset, IV, 0, IV.length);
System.arraycopy(IV, 0, counter, 0, counter.length);
processBlock();
}
public void init(byte[] iv)
throws IllegalArgumentException
{
init(iv, 0, iv.length);
}
public int getBlockSize()
{
return cipher.getBlockSize();
}
public byte processByte(byte in) {
if(blockOffset == counterOut.length) {
processBlock();
}
return (byte) (in ^ counterOut[blockOffset++]);
}
/**
* Encrypt some data.
* @param input The input data array.
* @param offsetIn The offset within the input data array to the first byte.
* @param length The number of bytes to encrypt.
* @param output The output data array.
* @param offsetOut The offset within the output data array to the first byte.
*/
public void processBytes(byte[] input, int offsetIn, int length, byte[] output, int offsetOut) {
// XOR the plaintext with counterOut until we run out of blockOffset,
// then processBlock() to get a new counterOut.
if (blockOffset != 0) {
/* handle first partially consumed block */
int len = Math.min(blockSize - blockOffset, length);
length -= len;
while(len-- > 0)
output[offsetOut++] = (byte) (input[offsetIn++] ^ counterOut[blockOffset++]);
if(length == 0) return;
processBlock();
}
assert(blockOffset == 0);
while(length > blockSize) {
/* consume full blocks */
// note: we skip *last* full block to avoid extra processBlock()
length -= blockSize;
while (blockOffset < blockSize)
output[offsetOut++] = (byte) (input[offsetIn++] ^ counterOut[blockOffset++]);
processBlock();
}
assert(blockOffset == 0 && length <= blockSize);
if (length == 0) return;
while (length-- > 0) {
/* handle final block */
output[offsetOut++] = (byte) (input[offsetIn++] ^ counterOut[blockOffset++]);
}
}
/** Encrypt counter to counterOut, and then increment counter. */
private void processBlock()
throws IllegalStateException
{
// Our ciphers clobber the input array, so it is essential to copy
// the counter to counterOut and then encrypt in-place.
System.arraycopy(counter, 0, counterOut, 0, counter.length);
cipher.encipher(counterOut, counterOut);
// Now increment counter.
for (int i = counter.length; i-- > 0 && (++counter[i]) == (byte)0;) {
/* nothing here */
}
blockOffset = 0;
}
}