001: package org.bouncycastle.crypto.engines;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.params.RC5Parameters;
006:
007: /**
008: * The specification for RC5 came from the <code>RC5 Encryption Algorithm</code>
009: * publication in RSA CryptoBytes, Spring of 1995.
010: * <em>http://www.rsasecurity.com/rsalabs/cryptobytes</em>.
011: * <p>
012: * This implementation is set to work with a 64 bit word size.
013: * <p>
014: * Implementation courtesy of Tito Pena.
015: */
016: public class RC564Engine implements BlockCipher {
017: private static final int wordSize = 64;
018: private static final int bytesPerWord = wordSize / 8;
019:
020: /*
021: * the number of rounds to perform
022: */
023: private int _noRounds;
024:
025: /*
026: * the expanded key array of size 2*(rounds + 1)
027: */
028: private long _S[];
029:
030: /*
031: * our "magic constants" for wordSize 62
032: *
033: * Pw = Odd((e-2) * 2^wordsize)
034: * Qw = Odd((o-2) * 2^wordsize)
035: *
036: * where e is the base of natural logarithms (2.718281828...)
037: * and o is the golden ratio (1.61803398...)
038: */
039: private static final long P64 = 0xb7e151628aed2a6bL;
040: private static final long Q64 = 0x9e3779b97f4a7c15L;
041:
042: private boolean forEncryption;
043:
044: /**
045: * Create an instance of the RC5 encryption algorithm
046: * and set some defaults
047: */
048: public RC564Engine() {
049: _noRounds = 12;
050: _S = null;
051: }
052:
053: public String getAlgorithmName() {
054: return "RC5-64";
055: }
056:
057: public int getBlockSize() {
058: return 2 * bytesPerWord;
059: }
060:
061: /**
062: * initialise a RC5-64 cipher.
063: *
064: * @param forEncryption whether or not we are for encryption.
065: * @param params the parameters required to set up the cipher.
066: * @exception IllegalArgumentException if the params argument is
067: * inappropriate.
068: */
069: public void init(boolean forEncryption, CipherParameters params) {
070: if (!(params instanceof RC5Parameters)) {
071: throw new IllegalArgumentException(
072: "invalid parameter passed to RC564 init - "
073: + params.getClass().getName());
074: }
075:
076: RC5Parameters p = (RC5Parameters) params;
077:
078: this .forEncryption = forEncryption;
079:
080: _noRounds = p.getRounds();
081:
082: setKey(p.getKey());
083: }
084:
085: public int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
086: return (forEncryption) ? encryptBlock(in, inOff, out, outOff)
087: : decryptBlock(in, inOff, out, outOff);
088: }
089:
090: public void reset() {
091: }
092:
093: /**
094: * Re-key the cipher.
095: * <p>
096: * @param key the key to be used
097: */
098: private void setKey(byte[] key) {
099: //
100: // KEY EXPANSION:
101: //
102: // There are 3 phases to the key expansion.
103: //
104: // Phase 1:
105: // Copy the secret key K[0...b-1] into an array L[0..c-1] of
106: // c = ceil(b/u), where u = wordSize/8 in little-endian order.
107: // In other words, we fill up L using u consecutive key bytes
108: // of K. Any unfilled byte positions in L are zeroed. In the
109: // case that b = c = 0, set c = 1 and L[0] = 0.
110: //
111: long[] L = new long[(key.length + (bytesPerWord - 1))
112: / bytesPerWord];
113:
114: for (int i = 0; i != key.length; i++) {
115: L[i / bytesPerWord] += (long) (key[i] & 0xff) << (8 * (i % bytesPerWord));
116: }
117:
118: //
119: // Phase 2:
120: // Initialize S to a particular fixed pseudo-random bit pattern
121: // using an arithmetic progression modulo 2^wordsize determined
122: // by the magic numbers, Pw & Qw.
123: //
124: _S = new long[2 * (_noRounds + 1)];
125:
126: _S[0] = P64;
127: for (int i = 1; i < _S.length; i++) {
128: _S[i] = (_S[i - 1] + Q64);
129: }
130:
131: //
132: // Phase 3:
133: // Mix in the user's secret key in 3 passes over the arrays S & L.
134: // The max of the arrays sizes is used as the loop control
135: //
136: int iter;
137:
138: if (L.length > _S.length) {
139: iter = 3 * L.length;
140: } else {
141: iter = 3 * _S.length;
142: }
143:
144: long A = 0, B = 0;
145: int i = 0, j = 0;
146:
147: for (int k = 0; k < iter; k++) {
148: A = _S[i] = rotateLeft(_S[i] + A + B, 3);
149: B = L[j] = rotateLeft(L[j] + A + B, A + B);
150: i = (i + 1) % _S.length;
151: j = (j + 1) % L.length;
152: }
153: }
154:
155: /**
156: * Encrypt the given block starting at the given offset and place
157: * the result in the provided buffer starting at the given offset.
158: * <p>
159: * @param in in byte buffer containing data to encrypt
160: * @param inOff offset into src buffer
161: * @param out out buffer where encrypted data is written
162: * @param outOff offset into out buffer
163: */
164: private int encryptBlock(byte[] in, int inOff, byte[] out,
165: int outOff) {
166: long A = bytesToWord(in, inOff) + _S[0];
167: long B = bytesToWord(in, inOff + bytesPerWord) + _S[1];
168:
169: for (int i = 1; i <= _noRounds; i++) {
170: A = rotateLeft(A ^ B, B) + _S[2 * i];
171: B = rotateLeft(B ^ A, A) + _S[2 * i + 1];
172: }
173:
174: wordToBytes(A, out, outOff);
175: wordToBytes(B, out, outOff + bytesPerWord);
176:
177: return 2 * bytesPerWord;
178: }
179:
180: private int decryptBlock(byte[] in, int inOff, byte[] out,
181: int outOff) {
182: long A = bytesToWord(in, inOff);
183: long B = bytesToWord(in, inOff + bytesPerWord);
184:
185: for (int i = _noRounds; i >= 1; i--) {
186: B = rotateRight(B - _S[2 * i + 1], A) ^ A;
187: A = rotateRight(A - _S[2 * i], B) ^ B;
188: }
189:
190: wordToBytes(A - _S[0], out, outOff);
191: wordToBytes(B - _S[1], out, outOff + bytesPerWord);
192:
193: return 2 * bytesPerWord;
194: }
195:
196: //////////////////////////////////////////////////////////////
197: //
198: // PRIVATE Helper Methods
199: //
200: //////////////////////////////////////////////////////////////
201:
202: /**
203: * Perform a left "spin" of the word. The rotation of the given
204: * word <em>x</em> is rotated left by <em>y</em> bits.
205: * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
206: * are used to determine the rotation amount. Here it is
207: * assumed that the wordsize used is a power of 2.
208: * <p>
209: * @param x word to rotate
210: * @param y number of bits to rotate % wordSize
211: */
212: private long rotateLeft(long x, long y) {
213: return ((x << (y & (wordSize - 1))) | (x >>> (wordSize - (y & (wordSize - 1)))));
214: }
215:
216: /**
217: * Perform a right "spin" of the word. The rotation of the given
218: * word <em>x</em> is rotated left by <em>y</em> bits.
219: * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
220: * are used to determine the rotation amount. Here it is
221: * assumed that the wordsize used is a power of 2.
222: * <p>
223: * @param x word to rotate
224: * @param y number of bits to rotate % wordSize
225: */
226: private long rotateRight(long x, long y) {
227: return ((x >>> (y & (wordSize - 1))) | (x << (wordSize - (y & (wordSize - 1)))));
228: }
229:
230: private long bytesToWord(byte[] src, int srcOff) {
231: long word = 0;
232:
233: for (int i = bytesPerWord - 1; i >= 0; i--) {
234: word = (word << 8) + (src[i + srcOff] & 0xff);
235: }
236:
237: return word;
238: }
239:
240: private void wordToBytes(long word, byte[] dst, int dstOff) {
241: for (int i = 0; i < bytesPerWord; i++) {
242: dst[i + dstOff] = (byte) word;
243: word >>>= 8;
244: }
245: }
246: }
|