001: package org.bouncycastle.crypto.macs;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.Mac;
006: import org.bouncycastle.crypto.engines.DESEngine;
007: import org.bouncycastle.crypto.modes.CBCBlockCipher;
008: import org.bouncycastle.crypto.paddings.BlockCipherPadding;
009: import org.bouncycastle.crypto.params.KeyParameter;
010:
011: /**
012: * DES based CBC Block Cipher MAC according to ISO9797, algorithm 3 (ANSI X9.19 Retail MAC)
013: *
014: * This could as well be derived from CBCBlockCipherMac, but then the property mac in the base
015: * class must be changed to protected
016: */
017:
018: public class ISO9797Alg3Mac implements Mac {
019: private byte[] mac;
020:
021: private byte[] buf;
022: private int bufOff;
023: private BlockCipher cipher;
024: private BlockCipherPadding padding;
025:
026: private int macSize;
027: private KeyParameter lastKey2;
028: private KeyParameter lastKey3;
029:
030: /**
031: * create a Retail-MAC based on a CBC block cipher. This will produce an
032: * authentication code of the length of the block size of the cipher.
033: *
034: * @param cipher the cipher to be used as the basis of the MAC generation. This must
035: * be DESEngine.
036: */
037: public ISO9797Alg3Mac(BlockCipher cipher) {
038: this (cipher, cipher.getBlockSize() * 8, null);
039: }
040:
041: /**
042: * create a Retail-MAC based on a CBC block cipher. This will produce an
043: * authentication code of the length of the block size of the cipher.
044: *
045: * @param cipher the cipher to be used as the basis of the MAC generation.
046: * @param padding the padding to be used to complete the last block.
047: */
048: public ISO9797Alg3Mac(BlockCipher cipher, BlockCipherPadding padding) {
049: this (cipher, cipher.getBlockSize() * 8, padding);
050: }
051:
052: /**
053: * create a Retail-MAC based on a block cipher with the size of the
054: * MAC been given in bits. This class uses single DES CBC mode as the basis for the
055: * MAC generation.
056: * <p>
057: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
058: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
059: * and in general should be less than the size of the block cipher as it reduces
060: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
061: *
062: * @param cipher the cipher to be used as the basis of the MAC generation.
063: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
064: */
065: public ISO9797Alg3Mac(BlockCipher cipher, int macSizeInBits) {
066: this (cipher, macSizeInBits, null);
067: }
068:
069: /**
070: * create a standard MAC based on a block cipher with the size of the
071: * MAC been given in bits. This class uses single DES CBC mode as the basis for the
072: * MAC generation. The final block is decrypted and then encrypted using the
073: * middle and right part of the key.
074: * <p>
075: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
076: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
077: * and in general should be less than the size of the block cipher as it reduces
078: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
079: *
080: * @param cipher the cipher to be used as the basis of the MAC generation.
081: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
082: * @param padding the padding to be used to complete the last block.
083: */
084: public ISO9797Alg3Mac(BlockCipher cipher, int macSizeInBits,
085: BlockCipherPadding padding) {
086: if ((macSizeInBits % 8) != 0) {
087: throw new IllegalArgumentException(
088: "MAC size must be multiple of 8");
089: }
090:
091: if (!(cipher instanceof DESEngine)) {
092: throw new IllegalArgumentException(
093: "cipher must be instance of DESEngine");
094: }
095:
096: this .cipher = new CBCBlockCipher(cipher);
097: this .padding = padding;
098: this .macSize = macSizeInBits / 8;
099:
100: mac = new byte[cipher.getBlockSize()];
101:
102: buf = new byte[cipher.getBlockSize()];
103: bufOff = 0;
104: }
105:
106: public String getAlgorithmName() {
107: return "ISO9797Alg3";
108: }
109:
110: public void init(CipherParameters params) {
111: reset();
112:
113: if (!(params instanceof KeyParameter)) {
114: throw new IllegalArgumentException(
115: "params must be an instance of KeyParameter");
116: }
117:
118: // KeyParameter must contain a double or triple length DES key,
119: // however the underlying cipher is a single DES. The middle and
120: // right key are used only in the final step.
121:
122: KeyParameter kp = (KeyParameter) params;
123: KeyParameter key1;
124: byte[] keyvalue = kp.getKey();
125:
126: if (keyvalue.length == 16) { // Double length DES key
127: key1 = new KeyParameter(keyvalue, 0, 8);
128: this .lastKey2 = new KeyParameter(keyvalue, 8, 8);
129: this .lastKey3 = key1;
130: } else if (keyvalue.length == 24) { // Triple length DES key
131: key1 = new KeyParameter(keyvalue, 0, 8);
132: this .lastKey2 = new KeyParameter(keyvalue, 8, 8);
133: this .lastKey3 = new KeyParameter(keyvalue, 16, 8);
134: } else {
135: throw new IllegalArgumentException(
136: "Key must be either 112 or 168 bit long");
137: }
138:
139: cipher.init(true, key1);
140: }
141:
142: public int getMacSize() {
143: return macSize;
144: }
145:
146: public void update(byte in) {
147: if (bufOff == buf.length) {
148: cipher.processBlock(buf, 0, mac, 0);
149: bufOff = 0;
150: }
151:
152: buf[bufOff++] = in;
153: }
154:
155: public void update(byte[] in, int inOff, int len) {
156: if (len < 0) {
157: throw new IllegalArgumentException(
158: "Can't have a negative input length!");
159: }
160:
161: int blockSize = cipher.getBlockSize();
162: int resultLen = 0;
163: int gapLen = blockSize - bufOff;
164:
165: if (len > gapLen) {
166: System.arraycopy(in, inOff, buf, bufOff, gapLen);
167:
168: resultLen += cipher.processBlock(buf, 0, mac, 0);
169:
170: bufOff = 0;
171: len -= gapLen;
172: inOff += gapLen;
173:
174: while (len > blockSize) {
175: resultLen += cipher.processBlock(in, inOff, mac, 0);
176:
177: len -= blockSize;
178: inOff += blockSize;
179: }
180: }
181:
182: System.arraycopy(in, inOff, buf, bufOff, len);
183:
184: bufOff += len;
185: }
186:
187: public int doFinal(byte[] out, int outOff) {
188: int blockSize = cipher.getBlockSize();
189:
190: if (padding == null) {
191: //
192: // pad with zeroes
193: //
194: while (bufOff < blockSize) {
195: buf[bufOff] = 0;
196: bufOff++;
197: }
198: } else {
199: if (bufOff == blockSize) {
200: cipher.processBlock(buf, 0, mac, 0);
201: bufOff = 0;
202: }
203:
204: padding.addPadding(buf, bufOff);
205: }
206:
207: cipher.processBlock(buf, 0, mac, 0);
208:
209: // Added to code from base class
210: DESEngine deseng = new DESEngine();
211:
212: deseng.init(false, this .lastKey2);
213: deseng.processBlock(mac, 0, mac, 0);
214:
215: deseng.init(true, this .lastKey3);
216: deseng.processBlock(mac, 0, mac, 0);
217: // ****
218:
219: System.arraycopy(mac, 0, out, outOff, macSize);
220:
221: reset();
222:
223: return macSize;
224: }
225:
226: /**
227: * Reset the mac generator.
228: */
229: public void reset() {
230: /*
231: * clean the buffer.
232: */
233: for (int i = 0; i < buf.length; i++) {
234: buf[i] = 0;
235: }
236:
237: bufOff = 0;
238:
239: /*
240: * reset the underlying cipher.
241: */
242: cipher.reset();
243: }
244: }
|