• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.hash;
18 
19 import static com.google.common.base.Charsets.UTF_8;
20 import static com.google.common.io.BaseEncoding.base16;
21 
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.ImmutableTable;
24 import com.google.common.collect.Table;
25 import com.google.common.testing.NullPointerTester;
26 import java.security.Key;
27 import java.util.Arrays;
28 import javax.crypto.Mac;
29 import javax.crypto.SecretKey;
30 import javax.crypto.spec.SecretKeySpec;
31 import junit.framework.TestCase;
32 import sun.security.jca.ProviderList;
33 import sun.security.jca.Providers;
34 
35 /**
36  * Tests for the MacHashFunction.
37  *
38  * @author Kurt Alfred Kluever
39  */
40 public class MacHashFunctionTest extends TestCase {
41 
42   private static final ImmutableSet<String> INPUTS = ImmutableSet.of("", "Z", "foobar");
43 
44   private static final SecretKey MD5_KEY =
45       new SecretKeySpec("secret key".getBytes(UTF_8), "HmacMD5");
46   private static final SecretKey SHA1_KEY =
47       new SecretKeySpec("secret key".getBytes(UTF_8), "HmacSHA1");
48   private static final SecretKey SHA256_KEY =
49       new SecretKeySpec("secret key".getBytes(UTF_8), "HmacSHA256");
50   private static final SecretKey SHA512_KEY =
51       new SecretKeySpec("secret key".getBytes(UTF_8), "HmacSHA512");
52 
53   // From http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Mac
54   private static final ImmutableTable<String, SecretKey, HashFunction> ALGORITHMS =
55       new ImmutableTable.Builder<String, SecretKey, HashFunction>()
56           .put("HmacMD5", MD5_KEY, Hashing.hmacMd5(MD5_KEY))
57           .put("HmacSHA1", SHA1_KEY, Hashing.hmacSha1(SHA1_KEY))
58           .put("HmacSHA256", SHA256_KEY, Hashing.hmacSha256(SHA256_KEY))
59           .put("HmacSHA512", SHA512_KEY, Hashing.hmacSha512(SHA512_KEY))
60           .build();
61 
testNulls()62   public void testNulls() {
63     NullPointerTester tester =
64         new NullPointerTester().setDefault(String.class, "HmacMD5").setDefault(Key.class, MD5_KEY);
65     tester.testAllPublicConstructors(MacHashFunction.class);
66     tester.testAllPublicInstanceMethods(new MacHashFunction("HmacMD5", MD5_KEY, "toString"));
67   }
68 
testHashing()69   public void testHashing() throws Exception {
70     for (String stringToTest : INPUTS) {
71       for (Table.Cell<String, SecretKey, HashFunction> cell : ALGORITHMS.cellSet()) {
72         String algorithm = cell.getRowKey();
73         SecretKey key = cell.getColumnKey();
74         HashFunction hashFunc = cell.getValue();
75         assertMacHashing(HashTestUtils.ascii(stringToTest), algorithm, key, hashFunc);
76       }
77     }
78   }
79 
80   @AndroidIncompatible // sun.security
testNoProviders()81   public void testNoProviders() {
82     ProviderList providers = Providers.getProviderList();
83     Providers.setProviderList(ProviderList.newList());
84     try {
85       Hashing.hmacMd5(MD5_KEY);
86       fail("expected ISE");
87     } catch (IllegalStateException expected) {
88     } finally {
89       Providers.setProviderList(providers);
90     }
91   }
92 
testMultipleUpdates()93   public void testMultipleUpdates() throws Exception {
94     Mac mac = Mac.getInstance("HmacSHA1");
95     mac.init(SHA1_KEY);
96     mac.update("hello".getBytes(UTF_8));
97     mac.update("world".getBytes(UTF_8));
98 
99     assertEquals(
100         HashCode.fromBytes(mac.doFinal()),
101         Hashing.hmacSha1(SHA1_KEY)
102             .newHasher()
103             .putString("hello", UTF_8)
104             .putString("world", UTF_8)
105             .hash());
106   }
107 
testMultipleUpdatesDoFinal()108   public void testMultipleUpdatesDoFinal() throws Exception {
109     Mac mac = Mac.getInstance("HmacSHA1");
110     mac.init(SHA1_KEY);
111     mac.update("hello".getBytes(UTF_8));
112     mac.update("world".getBytes(UTF_8));
113 
114     assertEquals(
115         HashCode.fromBytes(mac.doFinal("!!!".getBytes(UTF_8))),
116         Hashing.hmacSha1(SHA1_KEY)
117             .newHasher()
118             .putString("hello", UTF_8)
119             .putString("world", UTF_8)
120             .putString("!!!", UTF_8)
121             .hash());
122   }
123 
testCustomKey()124   public void testCustomKey() throws Exception {
125     SecretKey customKey =
126         new SecretKey() {
127           @Override
128           public String getAlgorithm() {
129             return "HmacMD5";
130           }
131 
132           @Override
133           public byte[] getEncoded() {
134             return new byte[8];
135           }
136 
137           @Override
138           public String getFormat() {
139             return "RAW";
140           }
141         };
142     assertEquals(
143         "ad262969c53bc16032f160081c4a07a0",
144         Hashing.hmacMd5(customKey)
145             .hashString("The quick brown fox jumps over the lazy dog", UTF_8)
146             .toString());
147   }
148 
testBadKey_emptyKey()149   public void testBadKey_emptyKey() throws Exception {
150     SecretKey badKey =
151         new SecretKey() {
152           @Override
153           public String getAlgorithm() {
154             return "HmacMD5";
155           }
156 
157           @Override
158           public byte[] getEncoded() {
159             return null;
160           }
161 
162           @Override
163           public String getFormat() {
164             return "RAW";
165           }
166         };
167     try {
168       Hashing.hmacMd5(badKey);
169       fail();
170     } catch (IllegalArgumentException expected) {
171     } catch (NullPointerException toleratedOnAndroid) {
172       // TODO(cpovirk): In an ideal world, we'd check here that we're running on Android.
173     }
174   }
175 
testEmptyInputs()176   public void testEmptyInputs() throws Exception {
177     String knownOutput = "8cbf764cbe2e4623d99a41354adfd390";
178 
179     Mac mac = Mac.getInstance("HmacMD5");
180     mac.init(MD5_KEY);
181     assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString());
182     assertEquals(knownOutput, Hashing.hmacMd5(MD5_KEY).newHasher().hash().toString());
183   }
184 
testEmptyInputs_mixedAlgorithms()185   public void testEmptyInputs_mixedAlgorithms() throws Exception {
186     String knownOutput = "8cbf764cbe2e4623d99a41354adfd390";
187 
188     Mac mac = Mac.getInstance("HmacMD5");
189     mac.init(SHA1_KEY);
190     assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString());
191     assertEquals(knownOutput, Hashing.hmacMd5(SHA1_KEY).newHasher().hash().toString());
192   }
193 
testKnownInputs()194   public void testKnownInputs() throws Exception {
195     String knownOutput = "9753980fe94daa8ecaa82216519393a9";
196     String input = "The quick brown fox jumps over the lazy dog";
197 
198     Mac mac = Mac.getInstance("HmacMD5");
199     mac.init(MD5_KEY);
200     mac.update(input.getBytes(UTF_8));
201     assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString());
202     assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal(input.getBytes(UTF_8))).toString());
203     assertEquals(knownOutput, Hashing.hmacMd5(MD5_KEY).hashString(input, UTF_8).toString());
204     assertEquals(knownOutput, Hashing.hmacMd5(MD5_KEY).hashBytes(input.getBytes(UTF_8)).toString());
205   }
206 
testKnownInputs_mixedAlgorithms()207   public void testKnownInputs_mixedAlgorithms() throws Exception {
208     String knownOutput = "9753980fe94daa8ecaa82216519393a9";
209     String input = "The quick brown fox jumps over the lazy dog";
210 
211     Mac mac = Mac.getInstance("HmacMD5");
212     mac.init(SHA1_KEY);
213     mac.update(input.getBytes(UTF_8));
214     assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString());
215     assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal(input.getBytes(UTF_8))).toString());
216     assertEquals(knownOutput, Hashing.hmacMd5(SHA1_KEY).hashString(input, UTF_8).toString());
217     assertEquals(
218         knownOutput, Hashing.hmacMd5(SHA1_KEY).hashBytes(input.getBytes(UTF_8)).toString());
219   }
220 
testPutAfterHash()221   public void testPutAfterHash() {
222     Hasher hasher = Hashing.hmacMd5(MD5_KEY).newHasher();
223 
224     assertEquals(
225         "9753980fe94daa8ecaa82216519393a9",
226         hasher.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString());
227     try {
228       hasher.putInt(42);
229       fail();
230     } catch (IllegalStateException expected) {
231     }
232   }
233 
testHashTwice()234   public void testHashTwice() {
235     Hasher hasher = Hashing.hmacMd5(MD5_KEY).newHasher();
236 
237     assertEquals(
238         "9753980fe94daa8ecaa82216519393a9",
239         hasher.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString());
240     try {
241       hasher.hash();
242       fail();
243     } catch (IllegalStateException expected) {
244     }
245   }
246 
testToString()247   public void testToString() {
248     byte[] keyData = "secret key".getBytes(UTF_8);
249 
250     assertEquals(
251         "Hashing.hmacMd5(Key[algorithm=HmacMD5, format=RAW])", Hashing.hmacMd5(MD5_KEY).toString());
252     assertEquals(
253         "Hashing.hmacMd5(Key[algorithm=HmacMD5, format=RAW])", Hashing.hmacMd5(keyData).toString());
254 
255     assertEquals(
256         "Hashing.hmacSha1(Key[algorithm=HmacSHA1, format=RAW])",
257         Hashing.hmacSha1(SHA1_KEY).toString());
258     assertEquals(
259         "Hashing.hmacSha1(Key[algorithm=HmacSHA1, format=RAW])",
260         Hashing.hmacSha1(keyData).toString());
261 
262     assertEquals(
263         "Hashing.hmacSha256(Key[algorithm=HmacSHA256, format=RAW])",
264         Hashing.hmacSha256(SHA256_KEY).toString());
265     assertEquals(
266         "Hashing.hmacSha256(Key[algorithm=HmacSHA256, format=RAW])",
267         Hashing.hmacSha256(keyData).toString());
268 
269     assertEquals(
270         "Hashing.hmacSha512(Key[algorithm=HmacSHA512, format=RAW])",
271         Hashing.hmacSha512(SHA512_KEY).toString());
272     assertEquals(
273         "Hashing.hmacSha512(Key[algorithm=HmacSHA512, format=RAW])",
274         Hashing.hmacSha512(keyData).toString());
275   }
276 
assertMacHashing( byte[] input, String algorithm, SecretKey key, HashFunction hashFunc)277   private static void assertMacHashing(
278       byte[] input, String algorithm, SecretKey key, HashFunction hashFunc) throws Exception {
279     Mac mac = Mac.getInstance(algorithm);
280     mac.init(key);
281     mac.update(input);
282 
283     assertEquals(HashCode.fromBytes(mac.doFinal()), hashFunc.hashBytes(input));
284     assertEquals(HashCode.fromBytes(mac.doFinal(input)), hashFunc.hashBytes(input));
285   }
286 
287   // Tests from RFC2022: https://tools.ietf.org/html/rfc2202
288 
testRfc2202_hmacSha1_case1()289   public void testRfc2202_hmacSha1_case1() {
290     byte[] key = fillByteArray(20, 0x0b);
291     String data = "Hi There";
292 
293     checkSha1("b617318655057264e28bc0b6fb378c8ef146be00", key, data);
294   }
295 
testRfc2202_hmacSha1_case2()296   public void testRfc2202_hmacSha1_case2() {
297     byte[] key = "Jefe".getBytes(UTF_8);
298     String data = "what do ya want for nothing?";
299 
300     checkSha1("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", key, data);
301   }
302 
testRfc2202_hmacSha1_case3()303   public void testRfc2202_hmacSha1_case3() {
304     byte[] key = fillByteArray(20, 0xaa);
305     byte[] data = fillByteArray(50, 0xdd);
306 
307     checkSha1("125d7342b9ac11cd91a39af48aa17b4f63f175d3", key, data);
308   }
309 
testRfc2202_hmacSha1_case4()310   public void testRfc2202_hmacSha1_case4() {
311     byte[] key = base16().lowerCase().decode("0102030405060708090a0b0c0d0e0f10111213141516171819");
312     byte[] data = fillByteArray(50, 0xcd);
313 
314     checkSha1("4c9007f4026250c6bc8414f9bf50c86c2d7235da", key, data);
315   }
316 
testRfc2202_hmacSha1_case5()317   public void testRfc2202_hmacSha1_case5() {
318     byte[] key = fillByteArray(20, 0x0c);
319     String data = "Test With Truncation";
320 
321     checkSha1("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", key, data);
322   }
323 
testRfc2202_hmacSha1_case6()324   public void testRfc2202_hmacSha1_case6() {
325     byte[] key = fillByteArray(80, 0xaa);
326     String data = "Test Using Larger Than Block-Size Key - Hash Key First";
327 
328     checkSha1("aa4ae5e15272d00e95705637ce8a3b55ed402112", key, data);
329   }
330 
testRfc2202_hmacSha1_case7()331   public void testRfc2202_hmacSha1_case7() {
332     byte[] key = fillByteArray(80, 0xaa);
333     String data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data";
334 
335     checkSha1("e8e99d0f45237d786d6bbaa7965c7808bbff1a91", key, data);
336   }
337 
testRfc2202_hmacMd5_case1()338   public void testRfc2202_hmacMd5_case1() {
339     byte[] key = fillByteArray(16, 0x0b);
340     String data = "Hi There";
341 
342     checkMd5("9294727a3638bb1c13f48ef8158bfc9d", key, data);
343   }
344 
testRfc2202_hmacMd5_case2()345   public void testRfc2202_hmacMd5_case2() {
346     byte[] key = "Jefe".getBytes(UTF_8);
347     String data = "what do ya want for nothing?";
348 
349     checkMd5("750c783e6ab0b503eaa86e310a5db738", key, data);
350   }
351 
testRfc2202_hmacMd5_case3()352   public void testRfc2202_hmacMd5_case3() {
353     byte[] key = fillByteArray(16, 0xaa);
354     byte[] data = fillByteArray(50, 0xdd);
355 
356     checkMd5("56be34521d144c88dbb8c733f0e8b3f6", key, data);
357   }
358 
testRfc2202_hmacMd5_case4()359   public void testRfc2202_hmacMd5_case4() {
360     byte[] key = base16().lowerCase().decode("0102030405060708090a0b0c0d0e0f10111213141516171819");
361     byte[] data = fillByteArray(50, 0xcd);
362 
363     checkMd5("697eaf0aca3a3aea3a75164746ffaa79", key, data);
364   }
365 
testRfc2202_hmacMd5_case5()366   public void testRfc2202_hmacMd5_case5() {
367     byte[] key = fillByteArray(16, 0x0c);
368     String data = "Test With Truncation";
369 
370     checkMd5("56461ef2342edc00f9bab995690efd4c", key, data);
371   }
372 
testRfc2202_hmacMd5_case6()373   public void testRfc2202_hmacMd5_case6() {
374     byte[] key = fillByteArray(80, 0xaa);
375     String data = "Test Using Larger Than Block-Size Key - Hash Key First";
376 
377     checkMd5("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd", key, data);
378   }
379 
testRfc2202_hmacMd5_case7()380   public void testRfc2202_hmacMd5_case7() {
381     byte[] key = fillByteArray(80, 0xaa);
382     String data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data";
383 
384     checkMd5("6f630fad67cda0ee1fb1f562db3aa53e", key, data);
385   }
386 
checkSha1(String expected, byte[] key, String data)387   private static void checkSha1(String expected, byte[] key, String data) {
388     checkSha1(expected, key, data.getBytes(UTF_8));
389   }
390 
checkSha1(String expected, byte[] key, byte[] data)391   private static void checkSha1(String expected, byte[] key, byte[] data) {
392     checkHmac(expected, Hashing.hmacSha1(key), data);
393   }
394 
checkMd5(String expected, byte[] key, String data)395   private static void checkMd5(String expected, byte[] key, String data) {
396     checkMd5(expected, key, data.getBytes(UTF_8));
397   }
398 
checkMd5(String expected, byte[] key, byte[] data)399   private static void checkMd5(String expected, byte[] key, byte[] data) {
400     checkHmac(expected, Hashing.hmacMd5(key), data);
401   }
402 
checkHmac(String expected, HashFunction hashFunc, byte[] data)403   private static void checkHmac(String expected, HashFunction hashFunc, byte[] data) {
404     assertEquals(HashCode.fromString(expected), hashFunc.hashBytes(data));
405   }
406 
fillByteArray(int size, int toFillWith)407   private static byte[] fillByteArray(int size, int toFillWith) {
408     byte[] array = new byte[size];
409     Arrays.fill(array, (byte) toFillWith);
410     return array;
411   }
412 }
413