001: package org.bouncycastle.crypto.engines;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.DataLengthException;
006: import org.bouncycastle.crypto.params.KeyParameter;
007:
008: /**
009: * A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
010: * <p>
011: * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
012: * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the
013: * end of the mulinv function!).
014: * <p>
015: * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
016: * <p>
017: * Note: This algorithm is patented in the USA, Japan, and Europe including
018: * at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland
019: * and the United Kingdom. Non-commercial use is free, however any commercial
020: * products are liable for royalties. Please see
021: * <a href="http://www.mediacrypt.com">www.mediacrypt.com</a> for
022: * further details. This announcement has been included at the request of
023: * the patent holders.
024: */
025: public class IDEAEngine implements BlockCipher {
026: protected static final int BLOCK_SIZE = 8;
027:
028: private int[] workingKey = null;
029:
030: /**
031: * standard constructor.
032: */
033: public IDEAEngine() {
034: }
035:
036: /**
037: * initialise an IDEA cipher.
038: *
039: * @param forEncryption whether or not we are for encryption.
040: * @param params the parameters required to set up the cipher.
041: * @exception IllegalArgumentException if the params argument is
042: * inappropriate.
043: */
044: public void init(boolean forEncryption, CipherParameters params) {
045: if (params instanceof KeyParameter) {
046: workingKey = generateWorkingKey(forEncryption,
047: ((KeyParameter) params).getKey());
048: return;
049: }
050:
051: throw new IllegalArgumentException(
052: "invalid parameter passed to IDEA init - "
053: + params.getClass().getName());
054: }
055:
056: public String getAlgorithmName() {
057: return "IDEA";
058: }
059:
060: public int getBlockSize() {
061: return BLOCK_SIZE;
062: }
063:
064: public int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
065: if (workingKey == null) {
066: throw new IllegalStateException(
067: "IDEA engine not initialised");
068: }
069:
070: if ((inOff + BLOCK_SIZE) > in.length) {
071: throw new DataLengthException("input buffer too short");
072: }
073:
074: if ((outOff + BLOCK_SIZE) > out.length) {
075: throw new DataLengthException("output buffer too short");
076: }
077:
078: ideaFunc(workingKey, in, inOff, out, outOff);
079:
080: return BLOCK_SIZE;
081: }
082:
083: public void reset() {
084: }
085:
086: private static final int MASK = 0xffff;
087: private static final int BASE = 0x10001;
088:
089: private int bytesToWord(byte[] in, int inOff) {
090: return ((in[inOff] << 8) & 0xff00) + (in[inOff + 1] & 0xff);
091: }
092:
093: private void wordToBytes(int word, byte[] out, int outOff) {
094: out[outOff] = (byte) (word >>> 8);
095: out[outOff + 1] = (byte) word;
096: }
097:
098: /**
099: * return x = x * y where the multiplication is done modulo
100: * 65537 (0x10001) (as defined in the IDEA specification) and
101: * a zero input is taken to be 65536 (0x10000).
102: *
103: * @param x the x value
104: * @param y the y value
105: * @return x = x * y
106: */
107: private int mul(int x, int y) {
108: if (x == 0) {
109: x = (BASE - y);
110: } else if (y == 0) {
111: x = (BASE - x);
112: } else {
113: int p = x * y;
114:
115: y = p & MASK;
116: x = p >>> 16;
117: x = y - x + ((y < x) ? 1 : 0);
118: }
119:
120: return x & MASK;
121: }
122:
123: private void ideaFunc(int[] workingKey, byte[] in, int inOff,
124: byte[] out, int outOff) {
125: int x0, x1, x2, x3, t0, t1;
126: int keyOff = 0;
127:
128: x0 = bytesToWord(in, inOff);
129: x1 = bytesToWord(in, inOff + 2);
130: x2 = bytesToWord(in, inOff + 4);
131: x3 = bytesToWord(in, inOff + 6);
132:
133: for (int round = 0; round < 8; round++) {
134: x0 = mul(x0, workingKey[keyOff++]);
135: x1 += workingKey[keyOff++];
136: x1 &= MASK;
137: x2 += workingKey[keyOff++];
138: x2 &= MASK;
139: x3 = mul(x3, workingKey[keyOff++]);
140:
141: t0 = x1;
142: t1 = x2;
143: x2 ^= x0;
144: x1 ^= x3;
145:
146: x2 = mul(x2, workingKey[keyOff++]);
147: x1 += x2;
148: x1 &= MASK;
149:
150: x1 = mul(x1, workingKey[keyOff++]);
151: x2 += x1;
152: x2 &= MASK;
153:
154: x0 ^= x1;
155: x3 ^= x2;
156: x1 ^= t1;
157: x2 ^= t0;
158: }
159:
160: wordToBytes(mul(x0, workingKey[keyOff++]), out, outOff);
161: wordToBytes(x2 + workingKey[keyOff++], out, outOff + 2); /* NB: Order */
162: wordToBytes(x1 + workingKey[keyOff++], out, outOff + 4);
163: wordToBytes(mul(x3, workingKey[keyOff]), out, outOff + 6);
164: }
165:
166: /**
167: * The following function is used to expand the user key to the encryption
168: * subkey. The first 16 bytes are the user key, and the rest of the subkey
169: * is calculated by rotating the previous 16 bytes by 25 bits to the left,
170: * and so on until the subkey is completed.
171: */
172: private int[] expandKey(byte[] uKey) {
173: int[] key = new int[52];
174:
175: if (uKey.length < 16) {
176: byte[] tmp = new byte[16];
177:
178: System.arraycopy(uKey, 0, tmp, tmp.length - uKey.length,
179: uKey.length);
180:
181: uKey = tmp;
182: }
183:
184: for (int i = 0; i < 8; i++) {
185: key[i] = bytesToWord(uKey, i * 2);
186: }
187:
188: for (int i = 8; i < 52; i++) {
189: if ((i & 7) < 6) {
190: key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7)
191: & MASK;
192: } else if ((i & 7) == 6) {
193: key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7)
194: & MASK;
195: } else {
196: key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7)
197: & MASK;
198: }
199: }
200:
201: return key;
202: }
203:
204: /**
205: * This function computes multiplicative inverse using Euclid's Greatest
206: * Common Divisor algorithm. Zero and one are self inverse.
207: * <p>
208: * i.e. x * mulInv(x) == 1 (modulo BASE)
209: */
210: private int mulInv(int x) {
211: int t0, t1, q, y;
212:
213: if (x < 2) {
214: return x;
215: }
216:
217: t0 = 1;
218: t1 = BASE / x;
219: y = BASE % x;
220:
221: while (y != 1) {
222: q = x / y;
223: x = x % y;
224: t0 = (t0 + (t1 * q)) & MASK;
225: if (x == 1) {
226: return t0;
227: }
228: q = y / x;
229: y = y % x;
230: t1 = (t1 + (t0 * q)) & MASK;
231: }
232:
233: return (1 - t1) & MASK;
234: }
235:
236: /**
237: * Return the additive inverse of x.
238: * <p>
239: * i.e. x + addInv(x) == 0
240: */
241: int addInv(int x) {
242: return (0 - x) & MASK;
243: }
244:
245: /**
246: * The function to invert the encryption subkey to the decryption subkey.
247: * It also involves the multiplicative inverse and the additive inverse functions.
248: */
249: private int[] invertKey(int[] inKey) {
250: int t1, t2, t3, t4;
251: int p = 52; /* We work backwards */
252: int[] key = new int[52];
253: int inOff = 0;
254:
255: t1 = mulInv(inKey[inOff++]);
256: t2 = addInv(inKey[inOff++]);
257: t3 = addInv(inKey[inOff++]);
258: t4 = mulInv(inKey[inOff++]);
259: key[--p] = t4;
260: key[--p] = t3;
261: key[--p] = t2;
262: key[--p] = t1;
263:
264: for (int round = 1; round < 8; round++) {
265: t1 = inKey[inOff++];
266: t2 = inKey[inOff++];
267: key[--p] = t2;
268: key[--p] = t1;
269:
270: t1 = mulInv(inKey[inOff++]);
271: t2 = addInv(inKey[inOff++]);
272: t3 = addInv(inKey[inOff++]);
273: t4 = mulInv(inKey[inOff++]);
274: key[--p] = t4;
275: key[--p] = t2; /* NB: Order */
276: key[--p] = t3;
277: key[--p] = t1;
278: }
279:
280: t1 = inKey[inOff++];
281: t2 = inKey[inOff++];
282: key[--p] = t2;
283: key[--p] = t1;
284:
285: t1 = mulInv(inKey[inOff++]);
286: t2 = addInv(inKey[inOff++]);
287: t3 = addInv(inKey[inOff++]);
288: t4 = mulInv(inKey[inOff]);
289: key[--p] = t4;
290: key[--p] = t3;
291: key[--p] = t2;
292: key[--p] = t1;
293:
294: return key;
295: }
296:
297: private int[] generateWorkingKey(boolean forEncryption,
298: byte[] userKey) {
299: if (forEncryption) {
300: return expandKey(userKey);
301: } else {
302: return invertKey(expandKey(userKey));
303: }
304: }
305: }
|