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