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.modes.CBCBlockCipher;
007: import org.bouncycastle.crypto.paddings.BlockCipherPadding;
008:
009: /**
010: * standard CBC Block Cipher MAC - if no padding is specified the default of
011: * pad of zeroes is used.
012: */
013: public class CBCBlockCipherMac implements Mac {
014: private byte[] mac;
015:
016: private byte[] buf;
017: private int bufOff;
018: private BlockCipher cipher;
019: private BlockCipherPadding padding;
020:
021: private int macSize;
022:
023: /**
024: * create a standard MAC based on a CBC block cipher. This will produce an
025: * authentication code half the length of the block size of the cipher.
026: *
027: * @param cipher the cipher to be used as the basis of the MAC generation.
028: */
029: public CBCBlockCipherMac(BlockCipher cipher) {
030: this (cipher, (cipher.getBlockSize() * 8) / 2, null);
031: }
032:
033: /**
034: * create a standard MAC based on a CBC block cipher. This will produce an
035: * authentication code half the length of the block size of the cipher.
036: *
037: * @param cipher the cipher to be used as the basis of the MAC generation.
038: * @param padding the padding to be used to complete the last block.
039: */
040: public CBCBlockCipherMac(BlockCipher cipher,
041: BlockCipherPadding padding) {
042: this (cipher, (cipher.getBlockSize() * 8) / 2, padding);
043: }
044:
045: /**
046: * create a standard MAC based on a block cipher with the size of the
047: * MAC been given in bits. This class uses CBC mode as the basis for the
048: * MAC generation.
049: * <p>
050: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
051: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
052: * and in general should be less than the size of the block cipher as it reduces
053: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
054: *
055: * @param cipher the cipher to be used as the basis of the MAC generation.
056: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
057: */
058: public CBCBlockCipherMac(BlockCipher cipher, int macSizeInBits) {
059: this (cipher, macSizeInBits, null);
060: }
061:
062: /**
063: * create a standard MAC based on a block cipher with the size of the
064: * MAC been given in bits. This class uses CBC mode as the basis for the
065: * MAC generation.
066: * <p>
067: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
068: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
069: * and in general should be less than the size of the block cipher as it reduces
070: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
071: *
072: * @param cipher the cipher to be used as the basis of the MAC generation.
073: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
074: * @param padding the padding to be used to complete the last block.
075: */
076: public CBCBlockCipherMac(BlockCipher cipher, int macSizeInBits,
077: BlockCipherPadding padding) {
078: if ((macSizeInBits % 8) != 0) {
079: throw new IllegalArgumentException(
080: "MAC size must be multiple of 8");
081: }
082:
083: this .cipher = new CBCBlockCipher(cipher);
084: this .padding = padding;
085: this .macSize = macSizeInBits / 8;
086:
087: mac = new byte[cipher.getBlockSize()];
088:
089: buf = new byte[cipher.getBlockSize()];
090: bufOff = 0;
091: }
092:
093: public String getAlgorithmName() {
094: return cipher.getAlgorithmName();
095: }
096:
097: public void init(CipherParameters params) {
098: reset();
099:
100: cipher.init(true, params);
101: }
102:
103: public int getMacSize() {
104: return macSize;
105: }
106:
107: public void update(byte in) {
108: if (bufOff == buf.length) {
109: cipher.processBlock(buf, 0, mac, 0);
110: bufOff = 0;
111: }
112:
113: buf[bufOff++] = in;
114: }
115:
116: public void update(byte[] in, int inOff, int len) {
117: if (len < 0) {
118: throw new IllegalArgumentException(
119: "Can't have a negative input length!");
120: }
121:
122: int blockSize = cipher.getBlockSize();
123: int resultLen = 0;
124: int gapLen = blockSize - bufOff;
125:
126: if (len > gapLen) {
127: System.arraycopy(in, inOff, buf, bufOff, gapLen);
128:
129: resultLen += cipher.processBlock(buf, 0, mac, 0);
130:
131: bufOff = 0;
132: len -= gapLen;
133: inOff += gapLen;
134:
135: while (len > blockSize) {
136: resultLen += cipher.processBlock(in, inOff, mac, 0);
137:
138: len -= blockSize;
139: inOff += blockSize;
140: }
141: }
142:
143: System.arraycopy(in, inOff, buf, bufOff, len);
144:
145: bufOff += len;
146: }
147:
148: public int doFinal(byte[] out, int outOff) {
149: int blockSize = cipher.getBlockSize();
150:
151: if (padding == null) {
152: //
153: // pad with zeroes
154: //
155: while (bufOff < blockSize) {
156: buf[bufOff] = 0;
157: bufOff++;
158: }
159: } else {
160: if (bufOff == blockSize) {
161: cipher.processBlock(buf, 0, mac, 0);
162: bufOff = 0;
163: }
164:
165: padding.addPadding(buf, bufOff);
166: }
167:
168: cipher.processBlock(buf, 0, mac, 0);
169:
170: System.arraycopy(mac, 0, out, outOff, macSize);
171:
172: reset();
173:
174: return macSize;
175: }
176:
177: /**
178: * Reset the mac generator.
179: */
180: public void reset() {
181: /*
182: * clean the buffer.
183: */
184: for (int i = 0; i < buf.length; i++) {
185: buf[i] = 0;
186: }
187:
188: bufOff = 0;
189:
190: /*
191: * reset the underlying cipher.
192: */
193: cipher.reset();
194: }
195: }
|