1package org.bouncycastle.crypto.macs; 2 3import org.bouncycastle.crypto.BlockCipher; 4import org.bouncycastle.crypto.CipherParameters; 5import org.bouncycastle.crypto.Mac; 6import org.bouncycastle.crypto.modes.CBCBlockCipher; 7import org.bouncycastle.crypto.paddings.BlockCipherPadding; 8 9/** 10 * standard CBC Block Cipher MAC - if no padding is specified the default of 11 * pad of zeroes is used. 12 */ 13public class CBCBlockCipherMac 14 implements Mac 15{ 16 private byte[] mac; 17 18 private byte[] buf; 19 private int bufOff; 20 private BlockCipher cipher; 21 private BlockCipherPadding padding; 22 23 private int macSize; 24 25 /** 26 * create a standard MAC based on a CBC block cipher. This will produce an 27 * authentication code half the length of the block size of the cipher. 28 * 29 * @param cipher the cipher to be used as the basis of the MAC generation. 30 */ 31 public CBCBlockCipherMac( 32 BlockCipher cipher) 33 { 34 this(cipher, (cipher.getBlockSize() * 8) / 2, null); 35 } 36 37 /** 38 * create a standard MAC based on a CBC block cipher. This will produce an 39 * authentication code half the length of the block size of the cipher. 40 * 41 * @param cipher the cipher to be used as the basis of the MAC generation. 42 * @param padding the padding to be used to complete the last block. 43 */ 44 public CBCBlockCipherMac( 45 BlockCipher cipher, 46 BlockCipherPadding padding) 47 { 48 this(cipher, (cipher.getBlockSize() * 8) / 2, padding); 49 } 50 51 /** 52 * create a standard MAC based on a block cipher with the size of the 53 * MAC been given in bits. This class uses CBC mode as the basis for the 54 * MAC generation. 55 * <p> 56 * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81), 57 * or 16 bits if being used as a data authenticator (FIPS Publication 113), 58 * and in general should be less than the size of the block cipher as it reduces 59 * the chance of an exhaustive attack (see Handbook of Applied Cryptography). 60 * 61 * @param cipher the cipher to be used as the basis of the MAC generation. 62 * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. 63 */ 64 public CBCBlockCipherMac( 65 BlockCipher cipher, 66 int macSizeInBits) 67 { 68 this(cipher, macSizeInBits, null); 69 } 70 71 /** 72 * create a standard MAC based on a block cipher with the size of the 73 * MAC been given in bits. This class uses CBC mode as the basis for the 74 * MAC generation. 75 * <p> 76 * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81), 77 * or 16 bits if being used as a data authenticator (FIPS Publication 113), 78 * and in general should be less than the size of the block cipher as it reduces 79 * the chance of an exhaustive attack (see Handbook of Applied Cryptography). 80 * 81 * @param cipher the cipher to be used as the basis of the MAC generation. 82 * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. 83 * @param padding the padding to be used to complete the last block. 84 */ 85 public CBCBlockCipherMac( 86 BlockCipher cipher, 87 int macSizeInBits, 88 BlockCipherPadding padding) 89 { 90 if ((macSizeInBits % 8) != 0) 91 { 92 throw new IllegalArgumentException("MAC size must be multiple of 8"); 93 } 94 95 this.cipher = new CBCBlockCipher(cipher); 96 this.padding = padding; 97 this.macSize = macSizeInBits / 8; 98 99 mac = new byte[cipher.getBlockSize()]; 100 101 buf = new byte[cipher.getBlockSize()]; 102 bufOff = 0; 103 } 104 105 public String getAlgorithmName() 106 { 107 return cipher.getAlgorithmName(); 108 } 109 110 public void init( 111 CipherParameters params) 112 { 113 reset(); 114 115 cipher.init(true, params); 116 } 117 118 public int getMacSize() 119 { 120 return macSize; 121 } 122 123 public void update( 124 byte in) 125 { 126 if (bufOff == buf.length) 127 { 128 cipher.processBlock(buf, 0, mac, 0); 129 bufOff = 0; 130 } 131 132 buf[bufOff++] = in; 133 } 134 135 public void update( 136 byte[] in, 137 int inOff, 138 int len) 139 { 140 if (len < 0) 141 { 142 throw new IllegalArgumentException("Can't have a negative input length!"); 143 } 144 145 int blockSize = cipher.getBlockSize(); 146 int gapLen = blockSize - bufOff; 147 148 if (len > gapLen) 149 { 150 System.arraycopy(in, inOff, buf, bufOff, gapLen); 151 152 cipher.processBlock(buf, 0, mac, 0); 153 154 bufOff = 0; 155 len -= gapLen; 156 inOff += gapLen; 157 158 while (len > blockSize) 159 { 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( 173 byte[] out, 174 int outOff) 175 { 176 int blockSize = cipher.getBlockSize(); 177 178 if (padding == null) 179 { 180 // 181 // pad with zeroes 182 // 183 while (bufOff < blockSize) 184 { 185 buf[bufOff] = 0; 186 bufOff++; 187 } 188 } 189 else 190 { 191 if (bufOff == blockSize) 192 { 193 cipher.processBlock(buf, 0, mac, 0); 194 bufOff = 0; 195 } 196 197 padding.addPadding(buf, bufOff); 198 } 199 200 cipher.processBlock(buf, 0, mac, 0); 201 202 System.arraycopy(mac, 0, out, outOff, macSize); 203 204 reset(); 205 206 return macSize; 207 } 208 209 /** 210 * Reset the mac generator. 211 */ 212 public void reset() 213 { 214 /* 215 * clean the buffer. 216 */ 217 for (int i = 0; i < buf.length; i++) 218 { 219 buf[i] = 0; 220 } 221 222 bufOff = 0; 223 224 /* 225 * reset the underlying cipher. 226 */ 227 cipher.reset(); 228 } 229} 230