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