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