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; 7 8public class BlockCipherMac 9 implements Mac 10{ 11 private byte[] mac; 12 13 private byte[] buf; 14 private int bufOff; 15 private BlockCipher cipher; 16 17 private int macSize; 18 19 /** 20 * create a standard MAC based on a block cipher. This will produce an 21 * authentication code half the length of the block size of the cipher. 22 * 23 * @param cipher the cipher to be used as the basis of the MAC generation. 24 * @deprecated use CBCBlockCipherMac 25 */ 26 public BlockCipherMac( 27 BlockCipher cipher) 28 { 29 this(cipher, (cipher.getBlockSize() * 8) / 2); 30 } 31 32 /** 33 * create a standard MAC based on a block cipher with the size of the 34 * MAC been given in bits. 35 * <p> 36 * Note: the size of the MAC must be at least 16 bits (FIPS Publication 113), 37 * and in general should be less than the size of the block cipher as it reduces 38 * the chance of an exhaustive attack (see Handbook of Applied Cryptography). 39 * 40 * @param cipher the cipher to be used as the basis of the MAC generation. 41 * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. 42 * @deprecated use CBCBlockCipherMac 43 */ 44 public BlockCipherMac( 45 BlockCipher cipher, 46 int macSizeInBits) 47 { 48 if ((macSizeInBits % 8) != 0) 49 { 50 throw new IllegalArgumentException("MAC size must be multiple of 8"); 51 } 52 53 this.cipher = new CBCBlockCipher(cipher); 54 this.macSize = macSizeInBits / 8; 55 56 mac = new byte[cipher.getBlockSize()]; 57 58 buf = new byte[cipher.getBlockSize()]; 59 bufOff = 0; 60 } 61 62 public String getAlgorithmName() 63 { 64 return cipher.getAlgorithmName(); 65 } 66 67 public void init( 68 CipherParameters params) 69 { 70 reset(); 71 72 cipher.init(true, params); 73 } 74 75 public int getMacSize() 76 { 77 return macSize; 78 } 79 80 public void update( 81 byte in) 82 { 83 int resultLen = 0; 84 85 if (bufOff == buf.length) 86 { 87 resultLen = cipher.processBlock(buf, 0, mac, 0); 88 bufOff = 0; 89 } 90 91 buf[bufOff++] = in; 92 } 93 94 public void update( 95 byte[] in, 96 int inOff, 97 int len) 98 { 99 if (len < 0) 100 { 101 throw new IllegalArgumentException("Can't have a negative input length!"); 102 } 103 104 int blockSize = cipher.getBlockSize(); 105 int resultLen = 0; 106 int gapLen = blockSize - bufOff; 107 108 if (len > gapLen) 109 { 110 System.arraycopy(in, inOff, buf, bufOff, gapLen); 111 112 resultLen += cipher.processBlock(buf, 0, mac, 0); 113 114 bufOff = 0; 115 len -= gapLen; 116 inOff += gapLen; 117 118 while (len > blockSize) 119 { 120 resultLen += cipher.processBlock(in, inOff, mac, 0); 121 122 len -= blockSize; 123 inOff += blockSize; 124 } 125 } 126 127 System.arraycopy(in, inOff, buf, bufOff, len); 128 129 bufOff += len; 130 } 131 132 public int doFinal( 133 byte[] out, 134 int outOff) 135 { 136 int blockSize = cipher.getBlockSize(); 137 138 // 139 // pad with zeroes 140 // 141 while (bufOff < blockSize) 142 { 143 buf[bufOff] = 0; 144 bufOff++; 145 } 146 147 cipher.processBlock(buf, 0, mac, 0); 148 149 System.arraycopy(mac, 0, out, outOff, macSize); 150 151 reset(); 152 153 return macSize; 154 } 155 156 /** 157 * Reset the mac generator. 158 */ 159 public void reset() 160 { 161 /* 162 * clean the buffer. 163 */ 164 for (int i = 0; i < buf.length; i++) 165 { 166 buf[i] = 0; 167 } 168 169 bufOff = 0; 170 171 /* 172 * reset the underlying cipher. 173 */ 174 cipher.reset(); 175 } 176} 177