| package org.bouncycastle.crypto.engines; |
| |
| import org.bouncycastle.crypto.BlockCipher; |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.DataLengthException; |
| import org.bouncycastle.crypto.OutputLengthException; |
| import org.bouncycastle.crypto.params.KeyParameter; |
| |
| /** |
| * A Noekeon engine, using direct-key mode. |
| */ |
| |
| public class NoekeonEngine |
| implements BlockCipher |
| { |
| private static final int genericSize = 16; // Block and key size, as well as the amount of rounds. |
| |
| private static final int[] nullVector = |
| { |
| 0x00, 0x00, 0x00, 0x00 // Used in decryption |
| }, |
| |
| roundConstants = |
| { |
| 0x80, 0x1b, 0x36, 0x6c, |
| 0xd8, 0xab, 0x4d, 0x9a, |
| 0x2f, 0x5e, 0xbc, 0x63, |
| 0xc6, 0x97, 0x35, 0x6a, |
| 0xd4 |
| }; |
| |
| private int[] state = new int[4], // a |
| subKeys = new int[4], // k |
| decryptKeys = new int[4]; |
| |
| private boolean _initialised, |
| _forEncryption; |
| |
| /** |
| * Create an instance of the Noekeon encryption algorithm |
| * and set some defaults |
| */ |
| public NoekeonEngine() |
| { |
| _initialised = false; |
| } |
| |
| public String getAlgorithmName() |
| { |
| return "Noekeon"; |
| } |
| |
| public int getBlockSize() |
| { |
| return genericSize; |
| } |
| |
| /** |
| * initialise |
| * |
| * @param forEncryption whether or not we are for encryption. |
| * @param params the parameters required to set up the cipher. |
| * @exception IllegalArgumentException if the params argument is |
| * inappropriate. |
| */ |
| public void init( |
| boolean forEncryption, |
| CipherParameters params) |
| { |
| if (!(params instanceof KeyParameter)) |
| { |
| throw new IllegalArgumentException("invalid parameter passed to Noekeon init - " + params.getClass().getName()); |
| } |
| |
| _forEncryption = forEncryption; |
| _initialised = true; |
| |
| KeyParameter p = (KeyParameter)params; |
| |
| setKey(p.getKey()); |
| } |
| |
| public int processBlock( |
| byte[] in, |
| int inOff, |
| byte[] out, |
| int outOff) |
| { |
| if (!_initialised) |
| { |
| throw new IllegalStateException(getAlgorithmName()+" not initialised"); |
| } |
| |
| if ((inOff + genericSize) > in.length) |
| { |
| throw new DataLengthException("input buffer too short"); |
| } |
| |
| if ((outOff + genericSize) > out.length) |
| { |
| throw new OutputLengthException("output buffer too short"); |
| } |
| |
| return (_forEncryption) ? encryptBlock(in, inOff, out, outOff) |
| : decryptBlock(in, inOff, out, outOff); |
| } |
| |
| public void reset() |
| { |
| } |
| |
| /** |
| * Re-key the cipher. |
| * <p> |
| * @param key the key to be used |
| */ |
| private void setKey( |
| byte[] key) |
| { |
| subKeys[0] = bytesToIntBig(key, 0); |
| subKeys[1] = bytesToIntBig(key, 4); |
| subKeys[2] = bytesToIntBig(key, 8); |
| subKeys[3] = bytesToIntBig(key, 12); |
| } |
| |
| private int encryptBlock( |
| byte[] in, |
| int inOff, |
| byte[] out, |
| int outOff) |
| { |
| state[0] = bytesToIntBig(in, inOff); |
| state[1] = bytesToIntBig(in, inOff+4); |
| state[2] = bytesToIntBig(in, inOff+8); |
| state[3] = bytesToIntBig(in, inOff+12); |
| |
| int i; |
| for (i = 0; i < genericSize; i++) |
| { |
| state[0] ^= roundConstants[i]; |
| theta(state, subKeys); |
| pi1(state); |
| gamma(state); |
| pi2(state); |
| } |
| |
| state[0] ^= roundConstants[i]; |
| theta(state, subKeys); |
| |
| intToBytesBig(state[0], out, outOff); |
| intToBytesBig(state[1], out, outOff+4); |
| intToBytesBig(state[2], out, outOff+8); |
| intToBytesBig(state[3], out, outOff+12); |
| |
| return genericSize; |
| } |
| |
| private int decryptBlock( |
| byte[] in, |
| int inOff, |
| byte[] out, |
| int outOff) |
| { |
| state[0] = bytesToIntBig(in, inOff); |
| state[1] = bytesToIntBig(in, inOff+4); |
| state[2] = bytesToIntBig(in, inOff+8); |
| state[3] = bytesToIntBig(in, inOff+12); |
| |
| System.arraycopy(subKeys, 0, decryptKeys, 0, subKeys.length); |
| theta(decryptKeys, nullVector); |
| |
| int i; |
| for (i = genericSize; i > 0; i--) |
| { |
| theta(state, decryptKeys); |
| state[0] ^= roundConstants[i]; |
| pi1(state); |
| gamma(state); |
| pi2(state); |
| } |
| |
| theta(state, decryptKeys); |
| state[0] ^= roundConstants[i]; |
| |
| intToBytesBig(state[0], out, outOff); |
| intToBytesBig(state[1], out, outOff+4); |
| intToBytesBig(state[2], out, outOff+8); |
| intToBytesBig(state[3], out, outOff+12); |
| |
| return genericSize; |
| } |
| |
| private void gamma(int[] a) |
| { |
| a[1] ^= ~a[3] & ~a[2]; |
| a[0] ^= a[2] & a[1]; |
| |
| int tmp = a[3]; |
| a[3] = a[0]; |
| a[0] = tmp; |
| a[2] ^= a[0]^a[1]^a[3]; |
| |
| a[1] ^= ~a[3] & ~a[2]; |
| a[0] ^= a[2] & a[1]; |
| } |
| |
| private void theta(int[] a, int[] k) |
| { |
| int tmp; |
| |
| tmp = a[0]^a[2]; |
| tmp ^= rotl(tmp,8)^rotl(tmp,24); |
| a[1] ^= tmp; |
| a[3] ^= tmp; |
| |
| for (int i = 0; i < 4; i++) |
| { |
| a[i] ^= k[i]; |
| } |
| |
| tmp = a[1]^a[3]; |
| tmp ^= rotl(tmp,8)^rotl(tmp,24); |
| a[0] ^= tmp; |
| a[2] ^= tmp; |
| } |
| |
| private void pi1(int[] a) |
| { |
| a[1] = rotl(a[1], 1); |
| a[2] = rotl(a[2], 5); |
| a[3] = rotl(a[3], 2); |
| } |
| |
| private void pi2(int[] a) |
| { |
| a[1] = rotl(a[1], 31); |
| a[2] = rotl(a[2], 27); |
| a[3] = rotl(a[3], 30); |
| } |
| |
| // Helpers |
| |
| private int bytesToIntBig(byte[] in, int off) |
| { |
| return ((in[off++]) << 24) | |
| ((in[off++] & 0xff) << 16) | |
| ((in[off++] & 0xff) << 8) | |
| (in[off ] & 0xff); |
| } |
| |
| private void intToBytesBig(int x, byte[] out, int off) |
| { |
| out[off++] = (byte)(x >>> 24); |
| out[off++] = (byte)(x >>> 16); |
| out[off++] = (byte)(x >>> 8); |
| out[off ] = (byte)x; |
| } |
| |
| private int rotl(int x, int y) |
| { |
| return (x << y) | (x >>> (32-y)); |
| } |
| } |