package aalg.crypto;

import aalg.algebra.Utils;

public class CtrStreamCipher implements ISymmetricStreamCipher {
	private final IBlockCipher dataBlockCipher;
	private final IBlockCipher ivBlockCipher;
	private final IRandomGenerator rng;
	private final int[] increment;
	
	public static final long INCREMENT_CONSTANT = 0x170FB82DE4369AC5L;
	
	public CtrStreamCipher(final IBlockCipher dataBlockCipher, final IBlockCipher ivBlockCipher, final IRandomGenerator rng) {
		this.dataBlockCipher = dataBlockCipher;
		this.ivBlockCipher = ivBlockCipher;
		this.rng = rng;
		
		this.increment = generateIncrement (dataBlockCipher.getBlockLength() / Utils.INT_BYTE_COUNT);
	}
	
	public static final int[] generateIncrement(final int intCount) {
		final int[] increment = new int[intCount];
		long constant = INCREMENT_CONSTANT;
		
		for (int i = 0; i < intCount; i++) {
			if ((i & 0x01) == 0)
				increment[i] = (int) constant;
			else {
				increment[i] = (int) (constant >>> Integer.SIZE);
				constant = Long.rotateRight(constant, 1);
			}
		}
		
		return increment;
	}

	@Override
	public CtrStreamEncryptor getEncryptor() {
		return new CtrStreamEncryptor(dataBlockCipher, ivBlockCipher, rng, increment);
	}

	@Override
	public CtrStreamDecryptor getDecryptor() {
		return new CtrStreamDecryptor(dataBlockCipher, ivBlockCipher, increment);
	}

	@Override
	public int getKeySize() {
		return ivBlockCipher.getKeySize() + dataBlockCipher.getBlockLength();
	}

}
