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