| package org.bouncycastle.crypto.engines; |
| |
| import org.bouncycastle.crypto.util.Pack; |
| |
| /** |
| * Implementation of Daniel J. Bernstein's ChaCha stream cipher. |
| */ |
| public class ChaChaEngine extends Salsa20Engine |
| { |
| |
| /** |
| * Creates a 20 rounds ChaCha engine. |
| */ |
| public ChaChaEngine() |
| { |
| super(); |
| } |
| |
| /** |
| * Creates a ChaCha engine with a specific number of rounds. |
| * @param rounds the number of rounds (must be an even number). |
| */ |
| public ChaChaEngine(int rounds) |
| { |
| super(rounds); |
| } |
| |
| public String getAlgorithmName() |
| { |
| return "ChaCha" + rounds; |
| } |
| |
| protected void advanceCounter() |
| { |
| if (++engineState[12] == 0) |
| { |
| ++engineState[13]; |
| } |
| } |
| |
| protected void resetCounter() |
| { |
| engineState[12] = engineState[13] = 0; |
| } |
| |
| protected void setKey(byte[] keyBytes, byte[] ivBytes) |
| { |
| if ((keyBytes.length != 16) && (keyBytes.length != 32)) |
| { |
| throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key"); |
| } |
| |
| int offset = 0; |
| byte[] constants; |
| |
| // Key |
| engineState[4] = Pack.littleEndianToInt(keyBytes, 0); |
| engineState[5] = Pack.littleEndianToInt(keyBytes, 4); |
| engineState[6] = Pack.littleEndianToInt(keyBytes, 8); |
| engineState[7] = Pack.littleEndianToInt(keyBytes, 12); |
| |
| if (keyBytes.length == 32) |
| { |
| constants = sigma; |
| offset = 16; |
| } else |
| { |
| constants = tau; |
| } |
| |
| engineState[8] = Pack.littleEndianToInt(keyBytes, offset); |
| engineState[9] = Pack.littleEndianToInt(keyBytes, offset + 4); |
| engineState[10] = Pack.littleEndianToInt(keyBytes, offset + 8); |
| engineState[11] = Pack.littleEndianToInt(keyBytes, offset + 12); |
| |
| engineState[0] = Pack.littleEndianToInt(constants, 0); |
| engineState[1] = Pack.littleEndianToInt(constants, 4); |
| engineState[2] = Pack.littleEndianToInt(constants, 8); |
| engineState[3] = Pack.littleEndianToInt(constants, 12); |
| |
| // Counter |
| engineState[12] = engineState[13] = 0; |
| |
| // IV |
| engineState[14] = Pack.littleEndianToInt(ivBytes, 0); |
| engineState[15] = Pack.littleEndianToInt(ivBytes, 4); |
| } |
| |
| protected void generateKeyStream(byte[] output) |
| { |
| chachaCore(rounds, engineState, x); |
| Pack.intToLittleEndian(x, output, 0); |
| } |
| |
| /** |
| * ChacCha function |
| * |
| * @param input input data |
| * |
| * @return keystream |
| */ |
| public static void chachaCore(int rounds, int[] input, int[] x) |
| { |
| if (input.length != 16) { |
| throw new IllegalArgumentException(); |
| } |
| if (x.length != 16) { |
| throw new IllegalArgumentException(); |
| } |
| if (rounds % 2 != 0) { |
| throw new IllegalArgumentException("Number of rounds must be even"); |
| } |
| |
| int x00 = input[ 0]; |
| int x01 = input[ 1]; |
| int x02 = input[ 2]; |
| int x03 = input[ 3]; |
| int x04 = input[ 4]; |
| int x05 = input[ 5]; |
| int x06 = input[ 6]; |
| int x07 = input[ 7]; |
| int x08 = input[ 8]; |
| int x09 = input[ 9]; |
| int x10 = input[10]; |
| int x11 = input[11]; |
| int x12 = input[12]; |
| int x13 = input[13]; |
| int x14 = input[14]; |
| int x15 = input[15]; |
| |
| for (int i = rounds; i > 0; i -= 2) |
| { |
| x00 += x04; x12 = rotl(x12 ^ x00, 16); |
| x08 += x12; x04 = rotl(x04 ^ x08, 12); |
| x00 += x04; x12 = rotl(x12 ^ x00, 8); |
| x08 += x12; x04 = rotl(x04 ^ x08, 7); |
| x01 += x05; x13 = rotl(x13 ^ x01, 16); |
| x09 += x13; x05 = rotl(x05 ^ x09, 12); |
| x01 += x05; x13 = rotl(x13 ^ x01, 8); |
| x09 += x13; x05 = rotl(x05 ^ x09, 7); |
| x02 += x06; x14 = rotl(x14 ^ x02, 16); |
| x10 += x14; x06 = rotl(x06 ^ x10, 12); |
| x02 += x06; x14 = rotl(x14 ^ x02, 8); |
| x10 += x14; x06 = rotl(x06 ^ x10, 7); |
| x03 += x07; x15 = rotl(x15 ^ x03, 16); |
| x11 += x15; x07 = rotl(x07 ^ x11, 12); |
| x03 += x07; x15 = rotl(x15 ^ x03, 8); |
| x11 += x15; x07 = rotl(x07 ^ x11, 7); |
| x00 += x05; x15 = rotl(x15 ^ x00, 16); |
| x10 += x15; x05 = rotl(x05 ^ x10, 12); |
| x00 += x05; x15 = rotl(x15 ^ x00, 8); |
| x10 += x15; x05 = rotl(x05 ^ x10, 7); |
| x01 += x06; x12 = rotl(x12 ^ x01, 16); |
| x11 += x12; x06 = rotl(x06 ^ x11, 12); |
| x01 += x06; x12 = rotl(x12 ^ x01, 8); |
| x11 += x12; x06 = rotl(x06 ^ x11, 7); |
| x02 += x07; x13 = rotl(x13 ^ x02, 16); |
| x08 += x13; x07 = rotl(x07 ^ x08, 12); |
| x02 += x07; x13 = rotl(x13 ^ x02, 8); |
| x08 += x13; x07 = rotl(x07 ^ x08, 7); |
| x03 += x04; x14 = rotl(x14 ^ x03, 16); |
| x09 += x14; x04 = rotl(x04 ^ x09, 12); |
| x03 += x04; x14 = rotl(x14 ^ x03, 8); |
| x09 += x14; x04 = rotl(x04 ^ x09, 7); |
| |
| } |
| |
| x[ 0] = x00 + input[ 0]; |
| x[ 1] = x01 + input[ 1]; |
| x[ 2] = x02 + input[ 2]; |
| x[ 3] = x03 + input[ 3]; |
| x[ 4] = x04 + input[ 4]; |
| x[ 5] = x05 + input[ 5]; |
| x[ 6] = x06 + input[ 6]; |
| x[ 7] = x07 + input[ 7]; |
| x[ 8] = x08 + input[ 8]; |
| x[ 9] = x09 + input[ 9]; |
| x[10] = x10 + input[10]; |
| x[11] = x11 + input[11]; |
| x[12] = x12 + input[12]; |
| x[13] = x13 + input[13]; |
| x[14] = x14 + input[14]; |
| x[15] = x15 + input[15]; |
| } |
| |
| } |