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.ISO7816d4Padding;
008:
009: /**
010: * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
011: * <p>
012: * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC
013: * </p><p>
014: * CMAC is a NIST recomendation - see
015: * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
016: * </p><p>
017: * CMAC/OMAC1 is a blockcipher-based message authentication code designed and
018: * analyzed by Tetsu Iwata and Kaoru Kurosawa.
019: * </p><p>
020: * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message
021: * Authentication Code). OMAC stands for One-Key CBC MAC.
022: * </p><p>
023: * It supports 128- or 64-bits block ciphers, with any key size, and returns
024: * a MAC with dimension less or equal to the block size of the underlying
025: * cipher.
026: * </p>
027: */
028: public class CMac implements Mac {
029: private static final byte CONSTANT_128 = (byte) 0x87;
030: private static final byte CONSTANT_64 = (byte) 0x1b;
031:
032: private byte[] ZEROES;
033:
034: private byte[] mac;
035:
036: private byte[] buf;
037: private int bufOff;
038: private BlockCipher cipher;
039:
040: private int macSize;
041:
042: private byte[] L, Lu, Lu2;
043:
044: /**
045: * create a standard MAC based on a CBC block cipher (64 or 128 bit block).
046: * This will produce an authentication code the length of the block size
047: * of the cipher.
048: *
049: * @param cipher the cipher to be used as the basis of the MAC generation.
050: */
051: public CMac(BlockCipher cipher) {
052: this (cipher, cipher.getBlockSize() * 8);
053: }
054:
055: /**
056: * create a standard MAC based on a block cipher with the size of the
057: * MAC been given in bits.
058: * <p/>
059: * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
060: * or 16 bits if being used as a data authenticator (FIPS Publication 113),
061: * and in general should be less than the size of the block cipher as it reduces
062: * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
063: *
064: * @param cipher the cipher to be used as the basis of the MAC generation.
065: * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and <= 128.
066: */
067: public CMac(BlockCipher cipher, int macSizeInBits) {
068: if ((macSizeInBits % 8) != 0) {
069: throw new IllegalArgumentException(
070: "MAC size must be multiple of 8");
071: }
072:
073: if (macSizeInBits > (cipher.getBlockSize() * 8)) {
074: throw new IllegalArgumentException(
075: "MAC size must be less or equal to "
076: + (cipher.getBlockSize() * 8));
077: }
078:
079: if (cipher.getBlockSize() != 8 && cipher.getBlockSize() != 16) {
080: throw new IllegalArgumentException(
081: "Block size must be either 64 or 128 bits");
082: }
083:
084: this .cipher = new CBCBlockCipher(cipher);
085: this .macSize = macSizeInBits / 8;
086:
087: mac = new byte[cipher.getBlockSize()];
088:
089: buf = new byte[cipher.getBlockSize()];
090:
091: ZEROES = new byte[cipher.getBlockSize()];
092:
093: bufOff = 0;
094: }
095:
096: public String getAlgorithmName() {
097: return cipher.getAlgorithmName();
098: }
099:
100: private final byte[] doubleLu(byte[] in) {
101: int FirstBit = (in[0] & 0xFF) >> 7;
102: byte[] ret = new byte[in.length];
103: for (int i = 0; i < in.length - 1; i++) {
104: ret[i] = (byte) ((in[i] << 1) + ((in[i + 1] & 0xFF) >> 7));
105: }
106: ret[in.length - 1] = (byte) (in[in.length - 1] << 1);
107: if (FirstBit == 1) {
108: ret[in.length - 1] ^= in.length == 16 ? CONSTANT_128
109: : CONSTANT_64;
110: }
111: return ret;
112: }
113:
114: public void init(CipherParameters params) {
115: reset();
116:
117: cipher.init(true, params);
118:
119: //initializes the L, Lu, Lu2 numbers
120: L = new byte[ZEROES.length];
121: cipher.processBlock(ZEROES, 0, L, 0);
122: Lu = doubleLu(L);
123: Lu2 = doubleLu(Lu);
124:
125: cipher.init(true, params);
126: }
127:
128: public int getMacSize() {
129: return macSize;
130: }
131:
132: public void update(byte in) {
133: if (bufOff == buf.length) {
134: cipher.processBlock(buf, 0, mac, 0);
135: bufOff = 0;
136: }
137:
138: buf[bufOff++] = in;
139: }
140:
141: public void update(byte[] in, int inOff, int len) {
142: if (len < 0) {
143: throw new IllegalArgumentException(
144: "Can't have a negative input length!");
145: }
146:
147: int blockSize = cipher.getBlockSize();
148: int gapLen = blockSize - bufOff;
149:
150: if (len > gapLen) {
151: System.arraycopy(in, inOff, buf, bufOff, gapLen);
152:
153: cipher.processBlock(buf, 0, mac, 0);
154:
155: bufOff = 0;
156: len -= gapLen;
157: inOff += gapLen;
158:
159: while (len > blockSize) {
160: cipher.processBlock(in, inOff, mac, 0);
161:
162: len -= blockSize;
163: inOff += blockSize;
164: }
165: }
166:
167: System.arraycopy(in, inOff, buf, bufOff, len);
168:
169: bufOff += len;
170: }
171:
172: public int doFinal(byte[] out, int outOff) {
173: int blockSize = cipher.getBlockSize();
174:
175: byte[] lu;
176: if (bufOff == blockSize) {
177: lu = Lu;
178: } else {
179: new ISO7816d4Padding().addPadding(buf, bufOff);
180: lu = Lu2;
181: }
182:
183: for (int i = 0; i < mac.length; i++) {
184: buf[i] ^= lu[i];
185: }
186:
187: cipher.processBlock(buf, 0, mac, 0);
188:
189: System.arraycopy(mac, 0, out, outOff, macSize);
190:
191: reset();
192:
193: return macSize;
194: }
195:
196: /**
197: * Reset the mac generator.
198: */
199: public void reset() {
200: /*
201: * clean the buffer.
202: */
203: for (int i = 0; i < buf.length; i++) {
204: buf[i] = 0;
205: }
206:
207: bufOff = 0;
208:
209: /*
210: * reset the underlying cipher.
211: */
212: cipher.reset();
213: }
214: }
|