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