package org.bouncycastle.crypto.engines; import org.bouncycastle.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(long diff) { int hi = (int)(diff >>> 32); int lo = (int)diff; if (hi > 0) { engineState[13] += hi; } int oldState = engineState[12]; engineState[12] += lo; if (oldState != 0 && engineState[12] < oldState) { engineState[13]++; } } protected void advanceCounter() { if (++engineState[12] == 0) { ++engineState[13]; } } protected void retreatCounter(long diff) { int hi = (int)(diff >>> 32); int lo = (int)diff; if (hi != 0) { if ((engineState[13] & 0xffffffffL) >= (hi & 0xffffffffL)) { engineState[13] -= hi; } else { throw new IllegalStateException("attempt to reduce counter past zero."); } } if ((engineState[12] & 0xffffffffL) >= (lo & 0xffffffffL)) { engineState[12] -= lo; } else { if (engineState[13] != 0) { --engineState[13]; engineState[12] -= lo; } else { throw new IllegalStateException("attempt to reduce counter past zero."); } } } protected void retreatCounter() { if (engineState[12] == 0 && engineState[13] == 0) { throw new IllegalStateException("attempt to reduce counter past zero."); } if (--engineState[12] == -1) { --engineState[13]; } } protected long getCounter() { return ((long)engineState[13] << 32) | (engineState[12] & 0xffffffffL); } protected void resetCounter() { engineState[12] = engineState[13] = 0; } protected void setKey(byte[] keyBytes, byte[] ivBytes) { if (keyBytes != null) { if ((keyBytes.length != 16) && (keyBytes.length != 32)) { throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key"); } packTauOrSigma(keyBytes.length, engineState, 0); // Key Pack.littleEndianToInt(keyBytes, 0, engineState, 4, 4); Pack.littleEndianToInt(keyBytes, keyBytes.length - 16, engineState, 8, 4); } // IV Pack.littleEndianToInt(ivBytes, 0, engineState, 14, 2); } protected void generateKeyStream(byte[] output) { chachaCore(rounds, engineState, x); Pack.intToLittleEndian(x, output, 0); } /** * ChaCha function * * @param input input data */ 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]; } }