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 class that provides a basic International Data Encryption Algorithm (IDEA) engine.

This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM" implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (barring 1 typo at the end of the mulinv function!).

It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/

Note: This algorithm was patented in the USA, Japan and Europe. These patents expired in 2011/2012.

/** * A class that provides a basic International Data Encryption Algorithm (IDEA) engine. * <p> * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM" * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (barring 1 typo at the * end of the mulinv function!). * <p> * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/ * <p> * Note: This algorithm was patented in the USA, Japan and Europe. These patents expired in 2011/2012. */
public class IDEAEngine implements BlockCipher { protected static final int BLOCK_SIZE = 8; private int[] workingKey = null;
standard constructor.
/** * standard constructor. */
public IDEAEngine() { }
initialise an IDEA cipher.
Params:
  • forEncryption – whether or not we are for encryption.
  • params – the parameters required to set up the cipher.
Throws:
/** * initialise an IDEA cipher. * * @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) { workingKey = generateWorkingKey(forEncryption, ((KeyParameter)params).getKey()); return; } throw new IllegalArgumentException("invalid parameter passed to IDEA init - " + params.getClass().getName()); } public String getAlgorithmName() { return "IDEA"; } public int getBlockSize() { return BLOCK_SIZE; } public int processBlock( byte[] in, int inOff, byte[] out, int outOff) { if (workingKey == null) { throw new IllegalStateException("IDEA engine not initialised"); } if ((inOff + BLOCK_SIZE) > in.length) { throw new DataLengthException("input buffer too short"); } if ((outOff + BLOCK_SIZE) > out.length) { throw new OutputLengthException("output buffer too short"); } ideaFunc(workingKey, in, inOff, out, outOff); return BLOCK_SIZE; } public void reset() { } private static final int MASK = 0xffff; private static final int BASE = 0x10001; private int bytesToWord( byte[] in, int inOff) { return ((in[inOff] << 8) & 0xff00) + (in[inOff + 1] & 0xff); } private void wordToBytes( int word, byte[] out, int outOff) { out[outOff] = (byte)(word >>> 8); out[outOff + 1] = (byte)word; }
return x = x * y where the multiplication is done modulo 65537 (0x10001) (as defined in the IDEA specification) and a zero input is taken to be 65536 (0x10000).
Params:
  • x – the x value
  • y – the y value
Returns:x = x * y
/** * return x = x * y where the multiplication is done modulo * 65537 (0x10001) (as defined in the IDEA specification) and * a zero input is taken to be 65536 (0x10000). * * @param x the x value * @param y the y value * @return x = x * y */
private int mul( int x, int y) { if (x == 0) { x = (BASE - y); } else if (y == 0) { x = (BASE - x); } else { int p = x * y; y = p & MASK; x = p >>> 16; x = y - x + ((y < x) ? 1 : 0); } return x & MASK; } private void ideaFunc( int[] workingKey, byte[] in, int inOff, byte[] out, int outOff) { int x0, x1, x2, x3, t0, t1; int keyOff = 0; x0 = bytesToWord(in, inOff); x1 = bytesToWord(in, inOff + 2); x2 = bytesToWord(in, inOff + 4); x3 = bytesToWord(in, inOff + 6); for (int round = 0; round < 8; round++) { x0 = mul(x0, workingKey[keyOff++]); x1 += workingKey[keyOff++]; x1 &= MASK; x2 += workingKey[keyOff++]; x2 &= MASK; x3 = mul(x3, workingKey[keyOff++]); t0 = x1; t1 = x2; x2 ^= x0; x1 ^= x3; x2 = mul(x2, workingKey[keyOff++]); x1 += x2; x1 &= MASK; x1 = mul(x1, workingKey[keyOff++]); x2 += x1; x2 &= MASK; x0 ^= x1; x3 ^= x2; x1 ^= t1; x2 ^= t0; } wordToBytes(mul(x0, workingKey[keyOff++]), out, outOff); wordToBytes(x2 + workingKey[keyOff++], out, outOff + 2); /* NB: Order */ wordToBytes(x1 + workingKey[keyOff++], out, outOff + 4); wordToBytes(mul(x3, workingKey[keyOff]), out, outOff + 6); }
The following function is used to expand the user key to the encryption subkey. The first 16 bytes are the user key, and the rest of the subkey is calculated by rotating the previous 16 bytes by 25 bits to the left, and so on until the subkey is completed.
/** * The following function is used to expand the user key to the encryption * subkey. The first 16 bytes are the user key, and the rest of the subkey * is calculated by rotating the previous 16 bytes by 25 bits to the left, * and so on until the subkey is completed. */
private int[] expandKey( byte[] uKey) { int[] key = new int[52]; if (uKey.length < 16) { byte[] tmp = new byte[16]; System.arraycopy(uKey, 0, tmp, tmp.length - uKey.length, uKey.length); uKey = tmp; } for (int i = 0; i < 8; i++) { key[i] = bytesToWord(uKey, i * 2); } for (int i = 8; i < 52; i++) { if ((i & 7) < 6) { key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK; } else if ((i & 7) == 6) { key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK; } else { key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK; } } return key; }
This function computes multiplicative inverse using Euclid's Greatest Common Divisor algorithm. Zero and one are self inverse.

i.e. x * mulInv(x) == 1 (modulo BASE)

/** * This function computes multiplicative inverse using Euclid's Greatest * Common Divisor algorithm. Zero and one are self inverse. * <p> * i.e. x * mulInv(x) == 1 (modulo BASE) */
private int mulInv( int x) { int t0, t1, q, y; if (x < 2) { return x; } t0 = 1; t1 = BASE / x; y = BASE % x; while (y != 1) { q = x / y; x = x % y; t0 = (t0 + (t1 * q)) & MASK; if (x == 1) { return t0; } q = y / x; y = y % x; t1 = (t1 + (t0 * q)) & MASK; } return (1 - t1) & MASK; }
Return the additive inverse of x.

i.e. x + addInv(x) == 0

/** * Return the additive inverse of x. * <p> * i.e. x + addInv(x) == 0 */
int addInv( int x) { return (0 - x) & MASK; }
The function to invert the encryption subkey to the decryption subkey. It also involves the multiplicative inverse and the additive inverse functions.
/** * The function to invert the encryption subkey to the decryption subkey. * It also involves the multiplicative inverse and the additive inverse functions. */
private int[] invertKey( int[] inKey) { int t1, t2, t3, t4; int p = 52; /* We work backwards */ int[] key = new int[52]; int inOff = 0; t1 = mulInv(inKey[inOff++]); t2 = addInv(inKey[inOff++]); t3 = addInv(inKey[inOff++]); t4 = mulInv(inKey[inOff++]); key[--p] = t4; key[--p] = t3; key[--p] = t2; key[--p] = t1; for (int round = 1; round < 8; round++) { t1 = inKey[inOff++]; t2 = inKey[inOff++]; key[--p] = t2; key[--p] = t1; t1 = mulInv(inKey[inOff++]); t2 = addInv(inKey[inOff++]); t3 = addInv(inKey[inOff++]); t4 = mulInv(inKey[inOff++]); key[--p] = t4; key[--p] = t2; /* NB: Order */ key[--p] = t3; key[--p] = t1; } t1 = inKey[inOff++]; t2 = inKey[inOff++]; key[--p] = t2; key[--p] = t1; t1 = mulInv(inKey[inOff++]); t2 = addInv(inKey[inOff++]); t3 = addInv(inKey[inOff++]); t4 = mulInv(inKey[inOff]); key[--p] = t4; key[--p] = t3; key[--p] = t2; key[--p] = t1; return key; } private int[] generateWorkingKey( boolean forEncryption, byte[] userKey) { if (forEncryption) { return expandKey(userKey); } else { return invertKey(expandKey(userKey)); } } }