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: * An RC6 engine.
010: */
011: public class RC6Engine implements BlockCipher {
012: private static final int wordSize = 32;
013: private static final int bytesPerWord = wordSize / 8;
014:
015: /*
016: * the number of rounds to perform
017: */
018: private static final int _noRounds = 20;
019:
020: /*
021: * the expanded key array of size 2*(rounds + 1)
022: */
023: private int _S[];
024:
025: /*
026: * our "magic constants" for wordSize 32
027: *
028: * Pw = Odd((e-2) * 2^wordsize)
029: * Qw = Odd((o-2) * 2^wordsize)
030: *
031: * where e is the base of natural logarithms (2.718281828...)
032: * and o is the golden ratio (1.61803398...)
033: */
034: private static final int P32 = 0xb7e15163;
035: private static final int Q32 = 0x9e3779b9;
036:
037: private static final int LGW = 5; // log2(32)
038:
039: private boolean forEncryption;
040:
041: /**
042: * Create an instance of the RC6 encryption algorithm
043: * and set some defaults
044: */
045: public RC6Engine() {
046: _S = null;
047: }
048:
049: public String getAlgorithmName() {
050: return "RC6";
051: }
052:
053: public int getBlockSize() {
054: return 4 * bytesPerWord;
055: }
056:
057: /**
058: * initialise a RC5-32 cipher.
059: *
060: * @param forEncryption whether or not we are for encryption.
061: * @param params the parameters required to set up the cipher.
062: * @exception IllegalArgumentException if the params argument is
063: * inappropriate.
064: */
065: public void init(boolean forEncryption, CipherParameters params) {
066: if (!(params instanceof KeyParameter)) {
067: throw new IllegalArgumentException(
068: "invalid parameter passed to RC6 init - "
069: + params.getClass().getName());
070: }
071:
072: KeyParameter p = (KeyParameter) params;
073: this .forEncryption = forEncryption;
074: setKey(p.getKey());
075: }
076:
077: public int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
078: int blockSize = getBlockSize();
079: if (_S == null) {
080: throw new IllegalStateException(
081: "RC6 engine not initialised");
082: }
083: if ((inOff + blockSize) > in.length) {
084: throw new DataLengthException("input buffer too short");
085: }
086: if ((outOff + blockSize) > out.length) {
087: throw new DataLengthException("output buffer too short");
088: }
089:
090: return (forEncryption) ? encryptBlock(in, inOff, out, outOff)
091: : decryptBlock(in, inOff, out, outOff);
092: }
093:
094: public void reset() {
095: }
096:
097: /**
098: * Re-key the cipher.
099: * <p>
100: * @param inKey the key to be used
101: */
102: private void setKey(byte[] key) {
103:
104: //
105: // KEY EXPANSION:
106: //
107: // There are 3 phases to the key expansion.
108: //
109: // Phase 1:
110: // Copy the secret key K[0...b-1] into an array L[0..c-1] of
111: // c = ceil(b/u), where u = wordSize/8 in little-endian order.
112: // In other words, we fill up L using u consecutive key bytes
113: // of K. Any unfilled byte positions in L are zeroed. In the
114: // case that b = c = 0, set c = 1 and L[0] = 0.
115: //
116: // compute number of dwords
117: int c = (key.length + (bytesPerWord - 1)) / bytesPerWord;
118: if (c == 0) {
119: c = 1;
120: }
121: int[] L = new int[(key.length + bytesPerWord - 1)
122: / bytesPerWord];
123:
124: // load all key bytes into array of key dwords
125: for (int i = key.length - 1; i >= 0; i--) {
126: L[i / bytesPerWord] = (L[i / bytesPerWord] << 8)
127: + (key[i] & 0xff);
128: }
129:
130: //
131: // Phase 2:
132: // Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords.
133: // Initialize S to a particular fixed pseudo-random bit pattern
134: // using an arithmetic progression modulo 2^wordsize determined
135: // by the magic numbers, Pw & Qw.
136: //
137: _S = new int[2 + 2 * _noRounds + 2];
138:
139: _S[0] = P32;
140: for (int i = 1; i < _S.length; i++) {
141: _S[i] = (_S[i - 1] + Q32);
142: }
143:
144: //
145: // Phase 3:
146: // Mix in the user's secret key in 3 passes over the arrays S & L.
147: // The max of the arrays sizes is used as the loop control
148: //
149: int iter;
150:
151: if (L.length > _S.length) {
152: iter = 3 * L.length;
153: } else {
154: iter = 3 * _S.length;
155: }
156:
157: int A = 0;
158: int B = 0;
159: int i = 0, j = 0;
160:
161: for (int k = 0; k < iter; k++) {
162: A = _S[i] = rotateLeft(_S[i] + A + B, 3);
163: B = L[j] = rotateLeft(L[j] + A + B, A + B);
164: i = (i + 1) % _S.length;
165: j = (j + 1) % L.length;
166: }
167: }
168:
169: private int encryptBlock(byte[] in, int inOff, byte[] out,
170: int outOff) {
171: // load A,B,C and D registers from in.
172: int A = bytesToWord(in, inOff);
173: int B = bytesToWord(in, inOff + bytesPerWord);
174: int C = bytesToWord(in, inOff + bytesPerWord * 2);
175: int D = bytesToWord(in, inOff + bytesPerWord * 3);
176:
177: // Do pseudo-round #0: pre-whitening of B and D
178: B += _S[0];
179: D += _S[1];
180:
181: // perform round #1,#2 ... #ROUNDS of encryption
182: for (int i = 1; i <= _noRounds; i++) {
183: int t = 0, u = 0;
184:
185: t = B * (2 * B + 1);
186: t = rotateLeft(t, 5);
187:
188: u = D * (2 * D + 1);
189: u = rotateLeft(u, 5);
190:
191: A ^= t;
192: A = rotateLeft(A, u);
193: A += _S[2 * i];
194:
195: C ^= u;
196: C = rotateLeft(C, t);
197: C += _S[2 * i + 1];
198:
199: int temp = A;
200: A = B;
201: B = C;
202: C = D;
203: D = temp;
204: }
205: // do pseudo-round #(ROUNDS+1) : post-whitening of A and C
206: A += _S[2 * _noRounds + 2];
207: C += _S[2 * _noRounds + 3];
208:
209: // store A, B, C and D registers to out
210: wordToBytes(A, out, outOff);
211: wordToBytes(B, out, outOff + bytesPerWord);
212: wordToBytes(C, out, outOff + bytesPerWord * 2);
213: wordToBytes(D, out, outOff + bytesPerWord * 3);
214:
215: return 4 * bytesPerWord;
216: }
217:
218: private int decryptBlock(byte[] in, int inOff, byte[] out,
219: int outOff) {
220: // load A,B,C and D registers from out.
221: int A = bytesToWord(in, inOff);
222: int B = bytesToWord(in, inOff + bytesPerWord);
223: int C = bytesToWord(in, inOff + bytesPerWord * 2);
224: int D = bytesToWord(in, inOff + bytesPerWord * 3);
225:
226: // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
227: C -= _S[2 * _noRounds + 3];
228: A -= _S[2 * _noRounds + 2];
229:
230: // Undo round #ROUNDS, .., #2,#1 of encryption
231: for (int i = _noRounds; i >= 1; i--) {
232: int t = 0, u = 0;
233:
234: int temp = D;
235: D = C;
236: C = B;
237: B = A;
238: A = temp;
239:
240: t = B * (2 * B + 1);
241: t = rotateLeft(t, LGW);
242:
243: u = D * (2 * D + 1);
244: u = rotateLeft(u, LGW);
245:
246: C -= _S[2 * i + 1];
247: C = rotateRight(C, t);
248: C ^= u;
249:
250: A -= _S[2 * i];
251: A = rotateRight(A, u);
252: A ^= t;
253:
254: }
255: // Undo pseudo-round #0: pre-whitening of B and D
256: D -= _S[1];
257: B -= _S[0];
258:
259: wordToBytes(A, out, outOff);
260: wordToBytes(B, out, outOff + bytesPerWord);
261: wordToBytes(C, out, outOff + bytesPerWord * 2);
262: wordToBytes(D, out, outOff + bytesPerWord * 3);
263:
264: return 4 * bytesPerWord;
265: }
266:
267: //////////////////////////////////////////////////////////////
268: //
269: // PRIVATE Helper Methods
270: //
271: //////////////////////////////////////////////////////////////
272:
273: /**
274: * Perform a left "spin" of the word. The rotation of the given
275: * word <em>x</em> is rotated left by <em>y</em> bits.
276: * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
277: * are used to determine the rotation amount. Here it is
278: * assumed that the wordsize used is 32.
279: * <p>
280: * @param x word to rotate
281: * @param y number of bits to rotate % wordSize
282: */
283: private int rotateLeft(int x, int y) {
284: return (x << y) | (x >>> -y);
285: }
286:
287: /**
288: * Perform a right "spin" of the word. The rotation of the given
289: * word <em>x</em> is rotated left by <em>y</em> bits.
290: * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
291: * are used to determine the rotation amount. Here it is
292: * assumed that the wordsize used is a power of 2.
293: * <p>
294: * @param x word to rotate
295: * @param y number of bits to rotate % wordSize
296: */
297: private int rotateRight(int x, int y) {
298: return (x >>> y) | (x << -y);
299: }
300:
301: private int bytesToWord(byte[] src, int srcOff) {
302: int word = 0;
303:
304: for (int i = bytesPerWord - 1; i >= 0; i--) {
305: word = (word << 8) + (src[i + srcOff] & 0xff);
306: }
307:
308: return word;
309: }
310:
311: private void wordToBytes(int word, byte[] dst, int dstOff) {
312: for (int i = 0; i < bytesPerWord; i++) {
313: dst[i + dstOff] = (byte) word;
314: word >>>= 8;
315: }
316: }
317: }
|