diff options
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java')
-rw-r--r-- | bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java new file mode 100644 index 0000000..f5ecb75 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java @@ -0,0 +1,304 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.StreamCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * Implementation of Martin Hell's, Thomas Johansson's and Willi Meier's stream + * cipher, Grain-128. + */ +public class Grain128Engine + implements StreamCipher +{ + + /** + * Constants + */ + private static final int STATE_SIZE = 4; + + /** + * Variables to hold the state of the engine during encryption and + * decryption + */ + private byte[] workingKey; + private byte[] workingIV; + private byte[] out; + private int[] lfsr; + private int[] nfsr; + private int output; + private int index = 4; + + private boolean initialised = false; + + public String getAlgorithmName() + { + return "Grain-128"; + } + + /** + * Initialize a Grain-128 cipher. + * + * @param forEncryption Whether or not we are for encryption. + * @param params The parameters required to set up the cipher. + * @throws IllegalArgumentException If the params argument is inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + /** + * Grain encryption and decryption is completely symmetrical, so the + * 'forEncryption' is irrelevant. + */ + if (!(params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException( + "Grain-128 Init parameters must include an IV"); + } + + ParametersWithIV ivParams = (ParametersWithIV)params; + + byte[] iv = ivParams.getIV(); + + if (iv == null || iv.length != 12) + { + throw new IllegalArgumentException( + "Grain-128 requires exactly 12 bytes of IV"); + } + + if (!(ivParams.getParameters() instanceof KeyParameter)) + { + throw new IllegalArgumentException( + "Grain-128 Init parameters must include a key"); + } + + KeyParameter key = (KeyParameter)ivParams.getParameters(); + + /** + * Initialize variables. + */ + workingIV = new byte[key.getKey().length]; + workingKey = new byte[key.getKey().length]; + lfsr = new int[STATE_SIZE]; + nfsr = new int[STATE_SIZE]; + out = new byte[4]; + + System.arraycopy(iv, 0, workingIV, 0, iv.length); + System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length); + + reset(); + } + + /** + * 256 clocks initialization phase. + */ + private void initGrain() + { + for (int i = 0; i < 8; i++) + { + output = getOutput(); + nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0] ^ output); + lfsr = shift(lfsr, getOutputLFSR() ^ output); + } + initialised = true; + } + + /** + * Get output from non-linear function g(x). + * + * @return Output from NFSR. + */ + private int getOutputNFSR() + { + int b0 = nfsr[0]; + int b3 = nfsr[0] >>> 3 | nfsr[1] << 29; + int b11 = nfsr[0] >>> 11 | nfsr[1] << 21; + int b13 = nfsr[0] >>> 13 | nfsr[1] << 19; + int b17 = nfsr[0] >>> 17 | nfsr[1] << 15; + int b18 = nfsr[0] >>> 18 | nfsr[1] << 14; + int b26 = nfsr[0] >>> 26 | nfsr[1] << 6; + int b27 = nfsr[0] >>> 27 | nfsr[1] << 5; + int b40 = nfsr[1] >>> 8 | nfsr[2] << 24; + int b48 = nfsr[1] >>> 16 | nfsr[2] << 16; + int b56 = nfsr[1] >>> 24 | nfsr[2] << 8; + int b59 = nfsr[1] >>> 27 | nfsr[2] << 5; + int b61 = nfsr[1] >>> 29 | nfsr[2] << 3; + int b65 = nfsr[2] >>> 1 | nfsr[3] << 31; + int b67 = nfsr[2] >>> 3 | nfsr[3] << 29; + int b68 = nfsr[2] >>> 4 | nfsr[3] << 28; + int b84 = nfsr[2] >>> 20 | nfsr[3] << 12; + int b91 = nfsr[2] >>> 27 | nfsr[3] << 5; + int b96 = nfsr[3]; + + return b0 ^ b26 ^ b56 ^ b91 ^ b96 ^ b3 & b67 ^ b11 & b13 ^ b17 & b18 + ^ b27 & b59 ^ b40 & b48 ^ b61 & b65 ^ b68 & b84; + } + + /** + * Get output from linear function f(x). + * + * @return Output from LFSR. + */ + private int getOutputLFSR() + { + int s0 = lfsr[0]; + int s7 = lfsr[0] >>> 7 | lfsr[1] << 25; + int s38 = lfsr[1] >>> 6 | lfsr[2] << 26; + int s70 = lfsr[2] >>> 6 | lfsr[3] << 26; + int s81 = lfsr[2] >>> 17 | lfsr[3] << 15; + int s96 = lfsr[3]; + + return s0 ^ s7 ^ s38 ^ s70 ^ s81 ^ s96; + } + + /** + * Get output from output function h(x). + * + * @return Output from h(x). + */ + private int getOutput() + { + int b2 = nfsr[0] >>> 2 | nfsr[1] << 30; + int b12 = nfsr[0] >>> 12 | nfsr[1] << 20; + int b15 = nfsr[0] >>> 15 | nfsr[1] << 17; + int b36 = nfsr[1] >>> 4 | nfsr[2] << 28; + int b45 = nfsr[1] >>> 13 | nfsr[2] << 19; + int b64 = nfsr[2]; + int b73 = nfsr[2] >>> 9 | nfsr[3] << 23; + int b89 = nfsr[2] >>> 25 | nfsr[3] << 7; + int b95 = nfsr[2] >>> 31 | nfsr[3] << 1; + int s8 = lfsr[0] >>> 8 | lfsr[1] << 24; + int s13 = lfsr[0] >>> 13 | lfsr[1] << 19; + int s20 = lfsr[0] >>> 20 | lfsr[1] << 12; + int s42 = lfsr[1] >>> 10 | lfsr[2] << 22; + int s60 = lfsr[1] >>> 28 | lfsr[2] << 4; + int s79 = lfsr[2] >>> 15 | lfsr[3] << 17; + int s93 = lfsr[2] >>> 29 | lfsr[3] << 3; + int s95 = lfsr[2] >>> 31 | lfsr[3] << 1; + + return b12 & s8 ^ s13 & s20 ^ b95 & s42 ^ s60 & s79 ^ b12 & b95 & s95 ^ s93 + ^ b2 ^ b15 ^ b36 ^ b45 ^ b64 ^ b73 ^ b89; + } + + /** + * Shift array 32 bits and add val to index.length - 1. + * + * @param array The array to shift. + * @param val The value to shift in. + * @return The shifted array with val added to index.length - 1. + */ + private int[] shift(int[] array, int val) + { + array[0] = array[1]; + array[1] = array[2]; + array[2] = array[3]; + array[3] = val; + + return array; + } + + /** + * Set keys, reset cipher. + * + * @param keyBytes The key. + * @param ivBytes The IV. + */ + private void setKey(byte[] keyBytes, byte[] ivBytes) + { + ivBytes[12] = (byte)0xFF; + ivBytes[13] = (byte)0xFF; + ivBytes[14] = (byte)0xFF; + ivBytes[15] = (byte)0xFF; + workingKey = keyBytes; + workingIV = ivBytes; + + /** + * Load NFSR and LFSR + */ + int j = 0; + for (int i = 0; i < nfsr.length; i++) + { + nfsr[i] = ((workingKey[j + 3]) << 24) | ((workingKey[j + 2]) << 16) + & 0x00FF0000 | ((workingKey[j + 1]) << 8) & 0x0000FF00 + | ((workingKey[j]) & 0x000000FF); + + lfsr[i] = ((workingIV[j + 3]) << 24) | ((workingIV[j + 2]) << 16) + & 0x00FF0000 | ((workingIV[j + 1]) << 8) & 0x0000FF00 + | ((workingIV[j]) & 0x000000FF); + j += 4; + } + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, + int outOff) + throws DataLengthException + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName() + + " not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + for (int i = 0; i < len; i++) + { + out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream()); + } + + return len; + } + + public void reset() + { + index = 4; + setKey(workingKey, workingIV); + initGrain(); + } + + /** + * Run Grain one round(i.e. 32 bits). + */ + private void oneRound() + { + output = getOutput(); + out[0] = (byte)output; + out[1] = (byte)(output >> 8); + out[2] = (byte)(output >> 16); + out[3] = (byte)(output >> 24); + + nfsr = shift(nfsr, getOutputNFSR() ^ lfsr[0]); + lfsr = shift(lfsr, getOutputLFSR()); + } + + public byte returnByte(byte in) + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName() + + " not initialised"); + } + return (byte)(in ^ getKeyStream()); + } + + private byte getKeyStream() + { + if (index > 3) + { + oneRound(); + index = 0; + } + return out[index++]; + } +} |