1 package org.bouncycastle.crypto.macs;
2 
3 import org.bouncycastle.crypto.BlockCipher;
4 import org.bouncycastle.crypto.CipherParameters;
5 import org.bouncycastle.crypto.Mac;
6 import org.bouncycastle.crypto.modes.CBCBlockCipher;
7 
8 public 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      */
BlockCipherMac( BlockCipher cipher)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      */
BlockCipherMac( BlockCipher cipher, int macSizeInBits)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 
getAlgorithmName()62     public String getAlgorithmName()
63     {
64         return cipher.getAlgorithmName();
65     }
66 
init( CipherParameters params)67     public void init(
68         CipherParameters    params)
69     {
70         reset();
71 
72         cipher.init(true, params);
73     }
74 
getMacSize()75     public int getMacSize()
76     {
77         return macSize;
78     }
79 
update( byte in)80     public void update(
81         byte        in)
82     {
83         if (bufOff == buf.length)
84         {
85             cipher.processBlock(buf, 0, mac, 0);
86             bufOff = 0;
87         }
88 
89         buf[bufOff++] = in;
90     }
91 
update( byte[] in, int inOff, int len)92     public void update(
93         byte[]      in,
94         int         inOff,
95         int         len)
96     {
97         if (len < 0)
98         {
99             throw new IllegalArgumentException("Can't have a negative input length!");
100         }
101 
102         int blockSize = cipher.getBlockSize();
103         int resultLen = 0;
104         int gapLen = blockSize - bufOff;
105 
106         if (len > gapLen)
107         {
108             System.arraycopy(in, inOff, buf, bufOff, gapLen);
109 
110             resultLen += cipher.processBlock(buf, 0, mac, 0);
111 
112             bufOff = 0;
113             len -= gapLen;
114             inOff += gapLen;
115 
116             while (len > blockSize)
117             {
118                 resultLen += cipher.processBlock(in, inOff, mac, 0);
119 
120                 len -= blockSize;
121                 inOff += blockSize;
122             }
123         }
124 
125         System.arraycopy(in, inOff, buf, bufOff, len);
126 
127         bufOff += len;
128     }
129 
doFinal( byte[] out, int outOff)130     public int doFinal(
131         byte[]  out,
132         int     outOff)
133     {
134         int blockSize = cipher.getBlockSize();
135 
136         //
137         // pad with zeroes
138         //
139         while (bufOff < blockSize)
140         {
141             buf[bufOff] = 0;
142             bufOff++;
143         }
144 
145         cipher.processBlock(buf, 0, mac, 0);
146 
147         System.arraycopy(mac, 0, out, outOff, macSize);
148 
149         reset();
150 
151         return macSize;
152     }
153 
154     /**
155      * Reset the mac generator.
156      */
reset()157     public void reset()
158     {
159         /*
160          * clean the buffer.
161          */
162         for (int i = 0; i < buf.length; i++)
163         {
164             buf[i] = 0;
165         }
166 
167         bufOff = 0;
168 
169         /*
170          * reset the underlying cipher.
171          */
172         cipher.reset();
173     }
174 }
175