001: package org.bouncycastle.crypto.macs;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.DataLengthException;
006: import org.bouncycastle.crypto.Mac;
007: import org.bouncycastle.crypto.paddings.BlockCipherPadding;
008: import org.bouncycastle.crypto.params.ParametersWithIV;
009:
010: /**
011: * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
012: */
013: class MacCFBBlockCipher {
014: private byte[] IV;
015: private byte[] cfbV;
016: private byte[] cfbOutV;
017:
018: private int blockSize;
019: private BlockCipher cipher = null;
020:
021: /**
022: * Basic constructor.
023: *
024: * @param cipher the block cipher to be used as the basis of the
025: * feedback mode.
026: * @param blockSize the block size in bits (note: a multiple of 8)
027: */
028: public MacCFBBlockCipher(BlockCipher cipher, int bitBlockSize) {
029: this .cipher = cipher;
030: this .blockSize = bitBlockSize / 8;
031:
032: this .IV = new byte[cipher.getBlockSize()];
033: this .cfbV = new byte[cipher.getBlockSize()];
034: this .cfbOutV = new byte[cipher.getBlockSize()];
035: }
036:
037: /**
038: * Initialise the cipher and, possibly, the initialisation vector (IV).
039: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
040: * An IV which is too short is handled in FIPS compliant fashion.
041: *
042: * @param param the key and other data required by the cipher.
043: * @exception IllegalArgumentException if the params argument is
044: * inappropriate.
045: */
046: public void init(CipherParameters params)
047: throws IllegalArgumentException {
048: if (params instanceof ParametersWithIV) {
049: ParametersWithIV ivParam = (ParametersWithIV) params;
050: byte[] iv = ivParam.getIV();
051:
052: if (iv.length < IV.length) {
053: System.arraycopy(iv, 0, IV, IV.length - iv.length,
054: iv.length);
055: } else {
056: System.arraycopy(iv, 0, IV, 0, IV.length);
057: }
058:
059: reset();
060:
061: cipher.init(true, ivParam.getParameters());
062: } else {
063: reset();
064:
065: cipher.init(true, params);
066: }
067: }
068:
069: /**
070: * return the algorithm name and mode.
071: *
072: * @return the name of the underlying algorithm followed by "/CFB"
073: * and the block size in bits.
074: */
075: public String getAlgorithmName() {
076: return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
077: }
078:
079: /**
080: * return the block size we are operating at.
081: *
082: * @return the block size we are operating at (in bytes).
083: */
084: public int getBlockSize() {
085: return blockSize;
086: }
087:
088: /**
089: * Process one block of input from the array in and write it to
090: * the out array.
091: *
092: * @param in the array containing the input data.
093: * @param inOff offset into the in array the data starts at.
094: * @param out the array the output data will be copied into.
095: * @param outOff the offset into the out array the output will start at.
096: * @exception DataLengthException if there isn't enough data in in, or
097: * space in out.
098: * @exception IllegalStateException if the cipher isn't initialised.
099: * @return the number of bytes processed and produced.
100: */
101: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
102: throws DataLengthException, IllegalStateException {
103: if ((inOff + blockSize) > in.length) {
104: throw new DataLengthException("input buffer too short");
105: }
106:
107: if ((outOff + blockSize) > out.length) {
108: throw new DataLengthException("output buffer too short");
109: }
110:
111: cipher.processBlock(cfbV, 0, cfbOutV, 0);
112:
113: //
114: // XOR the cfbV with the plaintext producing the cipher text
115: //
116: for (int i = 0; i < blockSize; i++) {
117: out[outOff + i] = (byte) (cfbOutV[i] ^ in[inOff + i]);
118: }
119:
120: //
121: // change over the input block.
122: //
123: System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length
124: - blockSize);
125: System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize,
126: blockSize);
127:
128: return blockSize;
129: }
130:
131: /**
132: * reset the chaining vector back to the IV and reset the underlying
133: * cipher.
134: */
135: public void reset() {
136: System.arraycopy(IV, 0, cfbV, 0, IV.length);
137:
138: cipher.reset();
139: }
140:
141: void getMacBlock(byte[] mac) {
142: cipher.processBlock(cfbV, 0, mac, 0);
143: }
144: }
145:
146: public class CFBBlockCipherMac implements Mac {
147: private byte[] mac;
148:
149: private byte[] buf;
150: private int bufOff;
151: private MacCFBBlockCipher cipher;
152: private BlockCipherPadding padding = null;
153:
154: private int macSize;
155:
156: /**
157: * create a standard MAC based on a CFB block cipher. This will produce an
158: * authentication code half the length of the block size of the cipher, with
159: * the CFB mode set to 8 bits.
160: *
161: * @param cipher the cipher to be used as the basis of the MAC generation.
162: */
163: public CFBBlockCipherMac(BlockCipher cipher) {
164: this (cipher, 8, (cipher.getBlockSize() * 8) / 2, null);
165: }
166:
167: /**
168: * create a standard MAC based on a CFB block cipher. This will produce an
169: * authentication code half the length of the block size of the cipher, with
170: * the CFB mode set to 8 bits.
171: *
172: * @param cipher the cipher to be used as the basis of the MAC generation.
173: * @param padding the padding to be used.
174: */
175: public CFBBlockCipherMac(BlockCipher cipher,
176: BlockCipherPadding padding) {
177: this (cipher, 8, (cipher.getBlockSize() * 8) / 2, padding);
178: }
179:
180: /**
181: * create a standard MAC based on a block cipher with the size of the
182: * MAC been given in bits. This class uses CFB mode as the basis for the
183: * MAC generation.
184: * <p>
185: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
186: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
187: * and in general should be less than the size of the block cipher as it reduces
188: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
189: *
190: * @param cipher the cipher to be used as the basis of the MAC generation.
191: * @param cfbBitSize the size of an output block produced by the CFB mode.
192: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
193: */
194: public CFBBlockCipherMac(BlockCipher cipher, int cfbBitSize,
195: int macSizeInBits) {
196: this (cipher, cfbBitSize, macSizeInBits, null);
197: }
198:
199: /**
200: * create a standard MAC based on a block cipher with the size of the
201: * MAC been given in bits. This class uses CFB mode as the basis for the
202: * MAC generation.
203: * <p>
204: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
205: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
206: * and in general should be less than the size of the block cipher as it reduces
207: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
208: *
209: * @param cipher the cipher to be used as the basis of the MAC generation.
210: * @param cfbBitSize the size of an output block produced by the CFB mode.
211: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
212: * @param padding a padding to be used.
213: */
214: public CFBBlockCipherMac(BlockCipher cipher, int cfbBitSize,
215: int macSizeInBits, BlockCipherPadding padding) {
216: if ((macSizeInBits % 8) != 0) {
217: throw new IllegalArgumentException(
218: "MAC size must be multiple of 8");
219: }
220:
221: mac = new byte[cipher.getBlockSize()];
222:
223: this .cipher = new MacCFBBlockCipher(cipher, cfbBitSize);
224: this .padding = padding;
225: this .macSize = macSizeInBits / 8;
226:
227: buf = new byte[this .cipher.getBlockSize()];
228: bufOff = 0;
229: }
230:
231: public String getAlgorithmName() {
232: return cipher.getAlgorithmName();
233: }
234:
235: public void init(CipherParameters params) {
236: reset();
237:
238: cipher.init(params);
239: }
240:
241: public int getMacSize() {
242: return macSize;
243: }
244:
245: public void update(byte in) {
246: if (bufOff == buf.length) {
247: cipher.processBlock(buf, 0, mac, 0);
248: bufOff = 0;
249: }
250:
251: buf[bufOff++] = in;
252: }
253:
254: public void update(byte[] in, int inOff, int len) {
255: if (len < 0) {
256: throw new IllegalArgumentException(
257: "Can't have a negative input length!");
258: }
259:
260: int blockSize = cipher.getBlockSize();
261: int resultLen = 0;
262: int gapLen = blockSize - bufOff;
263:
264: if (len > gapLen) {
265: System.arraycopy(in, inOff, buf, bufOff, gapLen);
266:
267: resultLen += cipher.processBlock(buf, 0, mac, 0);
268:
269: bufOff = 0;
270: len -= gapLen;
271: inOff += gapLen;
272:
273: while (len > blockSize) {
274: resultLen += cipher.processBlock(in, inOff, mac, 0);
275:
276: len -= blockSize;
277: inOff += blockSize;
278: }
279: }
280:
281: System.arraycopy(in, inOff, buf, bufOff, len);
282:
283: bufOff += len;
284: }
285:
286: public int doFinal(byte[] out, int outOff) {
287: int blockSize = cipher.getBlockSize();
288:
289: //
290: // pad with zeroes
291: //
292: if (this .padding == null) {
293: while (bufOff < blockSize) {
294: buf[bufOff] = 0;
295: bufOff++;
296: }
297: } else {
298: padding.addPadding(buf, bufOff);
299: }
300:
301: cipher.processBlock(buf, 0, mac, 0);
302:
303: cipher.getMacBlock(mac);
304:
305: System.arraycopy(mac, 0, out, outOff, macSize);
306:
307: reset();
308:
309: return macSize;
310: }
311:
312: /**
313: * Reset the mac generator.
314: */
315: public void reset() {
316: /*
317: * clean the buffer.
318: */
319: for (int i = 0; i < buf.length; i++) {
320: buf[i] = 0;
321: }
322:
323: bufOff = 0;
324:
325: /*
326: * reset the underlying cipher.
327: */
328: cipher.reset();
329: }
330: }
|