HMac.java revision a198e1ecc615e26a167d0f2dca9fa7e5fc62de10
1package org.bouncycastle.crypto.macs;
2
3import java.util.Hashtable;
4
5import org.bouncycastle.crypto.CipherParameters;
6import org.bouncycastle.crypto.Digest;
7import org.bouncycastle.crypto.ExtendedDigest;
8import org.bouncycastle.crypto.Mac;
9import org.bouncycastle.crypto.params.KeyParameter;
10import org.bouncycastle.util.Integers;
11import org.bouncycastle.util.Memoable;
12
13/**
14 * HMAC implementation based on RFC2104
15 *
16 * H(K XOR opad, H(K XOR ipad, text))
17 */
18public class HMac
19    implements Mac
20{
21    private final static byte IPAD = (byte)0x36;
22    private final static byte OPAD = (byte)0x5C;
23
24    private Digest digest;
25    private int digestSize;
26    private int blockLength;
27    private Memoable ipadState;
28    private Memoable opadState;
29
30    private byte[] inputPad;
31    private byte[] outputBuf;
32
33    private static Hashtable blockLengths;
34
35    static
36    {
37        blockLengths = new Hashtable();
38
39        // BEGIN android-removed
40        // blockLengths.put("GOST3411", Integers.valueOf(32));
41        //
42        // blockLengths.put("MD2", Integers.valueOf(16));
43        // blockLengths.put("MD4", Integers.valueOf(64));
44        // END android-removed
45        blockLengths.put("MD5", Integers.valueOf(64));
46
47        // BEGIN android-removed
48        // blockLengths.put("RIPEMD128", Integers.valueOf(64));
49        // blockLengths.put("RIPEMD160", Integers.valueOf(64));
50        // END android-removed
51
52        blockLengths.put("SHA-1", Integers.valueOf(64));
53        // BEGIN android-removed
54        // blockLengths.put("SHA-224", Integers.valueOf(64));
55        // END android-removed
56        blockLengths.put("SHA-256", Integers.valueOf(64));
57        blockLengths.put("SHA-384", Integers.valueOf(128));
58        blockLengths.put("SHA-512", Integers.valueOf(128));
59
60        // BEGIN android-removed
61        // blockLengths.put("Tiger", Integers.valueOf(64));
62        // blockLengths.put("Whirlpool", Integers.valueOf(64));
63        // END android-removed
64    }
65
66    private static int getByteLength(
67        Digest digest)
68    {
69        if (digest instanceof ExtendedDigest)
70        {
71            return ((ExtendedDigest)digest).getByteLength();
72        }
73
74        Integer  b = (Integer)blockLengths.get(digest.getAlgorithmName());
75
76        if (b == null)
77        {
78            throw new IllegalArgumentException("unknown digest passed: " + digest.getAlgorithmName());
79        }
80
81        return b.intValue();
82    }
83
84    /**
85     * Base constructor for one of the standard digest algorithms that the
86     * byteLength of the algorithm is know for.
87     *
88     * @param digest the digest.
89     */
90    public HMac(
91        Digest digest)
92    {
93        this(digest, getByteLength(digest));
94    }
95
96    private HMac(
97        Digest digest,
98        int    byteLength)
99    {
100        this.digest = digest;
101        this.digestSize = digest.getDigestSize();
102        this.blockLength = byteLength;
103        this.inputPad = new byte[blockLength];
104        this.outputBuf = new byte[blockLength + digestSize];
105    }
106
107    public String getAlgorithmName()
108    {
109        return digest.getAlgorithmName() + "/HMAC";
110    }
111
112    public Digest getUnderlyingDigest()
113    {
114        return digest;
115    }
116
117    public void init(
118        CipherParameters params)
119    {
120        digest.reset();
121
122        byte[] key = ((KeyParameter)params).getKey();
123        int keyLength = key.length;
124
125        if (keyLength > blockLength)
126        {
127            digest.update(key, 0, keyLength);
128            digest.doFinal(inputPad, 0);
129
130            keyLength = digestSize;
131        }
132        else
133        {
134            System.arraycopy(key, 0, inputPad, 0, keyLength);
135        }
136
137        for (int i = keyLength; i < inputPad.length; i++)
138        {
139            inputPad[i] = 0;
140        }
141
142        System.arraycopy(inputPad, 0, outputBuf, 0, blockLength);
143
144        xorPad(inputPad, blockLength, IPAD);
145        xorPad(outputBuf, blockLength, OPAD);
146
147        if (digest instanceof Memoable)
148        {
149            opadState = ((Memoable)digest).copy();
150
151            ((Digest)opadState).update(outputBuf, 0, blockLength);
152        }
153
154        digest.update(inputPad, 0, inputPad.length);
155
156        if (digest instanceof Memoable)
157        {
158            ipadState = ((Memoable)digest).copy();
159        }
160    }
161
162    public int getMacSize()
163    {
164        return digestSize;
165    }
166
167    public void update(
168        byte in)
169    {
170        digest.update(in);
171    }
172
173    public void update(
174        byte[] in,
175        int inOff,
176        int len)
177    {
178        digest.update(in, inOff, len);
179    }
180
181    public int doFinal(
182        byte[] out,
183        int outOff)
184    {
185        digest.doFinal(outputBuf, blockLength);
186
187        if (opadState != null)
188        {
189            ((Memoable)digest).reset(opadState);
190            digest.update(outputBuf, blockLength, digest.getDigestSize());
191        }
192        else
193        {
194            digest.update(outputBuf, 0, outputBuf.length);
195        }
196
197        int len = digest.doFinal(out, outOff);
198
199        for (int i = blockLength; i < outputBuf.length; i++)
200        {
201            outputBuf[i] = 0;
202        }
203
204        if (ipadState != null)
205        {
206            ((Memoable)digest).reset(ipadState);
207        }
208        else
209        {
210            digest.update(inputPad, 0, inputPad.length);
211        }
212
213        return len;
214    }
215
216    /**
217     * Reset the mac generator.
218     */
219    public void reset()
220    {
221        /*
222         * reset the underlying digest.
223         */
224        digest.reset();
225
226        /*
227         * reinitialize the digest.
228         */
229        digest.update(inputPad, 0, inputPad.length);
230    }
231
232    private static void xorPad(byte[] pad, int len, byte n)
233    {
234        for (int i = 0; i < len; ++i)
235        {
236            pad[i] ^= n;
237        }
238    }
239}
240