| /* |
| * Copyright (C) 2015 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.hash; |
| |
| import static com.google.common.base.Charsets.UTF_8; |
| import static com.google.common.io.BaseEncoding.base16; |
| |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ImmutableTable; |
| import com.google.common.collect.Table; |
| import com.google.common.testing.NullPointerTester; |
| import java.security.Key; |
| import java.util.Arrays; |
| import javax.crypto.Mac; |
| import javax.crypto.SecretKey; |
| import javax.crypto.spec.SecretKeySpec; |
| import junit.framework.TestCase; |
| import sun.security.jca.ProviderList; |
| import sun.security.jca.Providers; |
| |
| /** |
| * Tests for the MacHashFunction. |
| * |
| * @author Kurt Alfred Kluever |
| */ |
| public class MacHashFunctionTest extends TestCase { |
| |
| private static final ImmutableSet<String> INPUTS = ImmutableSet.of("", "Z", "foobar"); |
| |
| private static final SecretKey MD5_KEY = |
| new SecretKeySpec("secret key".getBytes(UTF_8), "HmacMD5"); |
| private static final SecretKey SHA1_KEY = |
| new SecretKeySpec("secret key".getBytes(UTF_8), "HmacSHA1"); |
| private static final SecretKey SHA256_KEY = |
| new SecretKeySpec("secret key".getBytes(UTF_8), "HmacSHA256"); |
| private static final SecretKey SHA512_KEY = |
| new SecretKeySpec("secret key".getBytes(UTF_8), "HmacSHA512"); |
| |
| // From http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Mac |
| private static final ImmutableTable<String, SecretKey, HashFunction> ALGORITHMS = |
| new ImmutableTable.Builder<String, SecretKey, HashFunction>() |
| .put("HmacMD5", MD5_KEY, Hashing.hmacMd5(MD5_KEY)) |
| .put("HmacSHA1", SHA1_KEY, Hashing.hmacSha1(SHA1_KEY)) |
| .put("HmacSHA256", SHA256_KEY, Hashing.hmacSha256(SHA256_KEY)) |
| .put("HmacSHA512", SHA512_KEY, Hashing.hmacSha512(SHA512_KEY)) |
| .build(); |
| |
| public void testNulls() { |
| NullPointerTester tester = |
| new NullPointerTester().setDefault(String.class, "HmacMD5").setDefault(Key.class, MD5_KEY); |
| tester.testAllPublicConstructors(MacHashFunction.class); |
| tester.testAllPublicInstanceMethods(new MacHashFunction("HmacMD5", MD5_KEY, "toString")); |
| } |
| |
| public void testHashing() throws Exception { |
| for (String stringToTest : INPUTS) { |
| for (Table.Cell<String, SecretKey, HashFunction> cell : ALGORITHMS.cellSet()) { |
| String algorithm = cell.getRowKey(); |
| SecretKey key = cell.getColumnKey(); |
| HashFunction hashFunc = cell.getValue(); |
| assertMacHashing(HashTestUtils.ascii(stringToTest), algorithm, key, hashFunc); |
| } |
| } |
| } |
| |
| @AndroidIncompatible // sun.security |
| public void testNoProviders() { |
| ProviderList providers = Providers.getProviderList(); |
| Providers.setProviderList(ProviderList.newList()); |
| try { |
| Hashing.hmacMd5(MD5_KEY); |
| fail("expected ISE"); |
| } catch (IllegalStateException expected) { |
| } finally { |
| Providers.setProviderList(providers); |
| } |
| } |
| |
| public void testMultipleUpdates() throws Exception { |
| Mac mac = Mac.getInstance("HmacSHA1"); |
| mac.init(SHA1_KEY); |
| mac.update("hello".getBytes(UTF_8)); |
| mac.update("world".getBytes(UTF_8)); |
| |
| assertEquals( |
| HashCode.fromBytes(mac.doFinal()), |
| Hashing.hmacSha1(SHA1_KEY) |
| .newHasher() |
| .putString("hello", UTF_8) |
| .putString("world", UTF_8) |
| .hash()); |
| } |
| |
| public void testMultipleUpdatesDoFinal() throws Exception { |
| Mac mac = Mac.getInstance("HmacSHA1"); |
| mac.init(SHA1_KEY); |
| mac.update("hello".getBytes(UTF_8)); |
| mac.update("world".getBytes(UTF_8)); |
| |
| assertEquals( |
| HashCode.fromBytes(mac.doFinal("!!!".getBytes(UTF_8))), |
| Hashing.hmacSha1(SHA1_KEY) |
| .newHasher() |
| .putString("hello", UTF_8) |
| .putString("world", UTF_8) |
| .putString("!!!", UTF_8) |
| .hash()); |
| } |
| |
| public void testCustomKey() throws Exception { |
| SecretKey customKey = |
| new SecretKey() { |
| @Override |
| public String getAlgorithm() { |
| return "HmacMD5"; |
| } |
| |
| @Override |
| public byte[] getEncoded() { |
| return new byte[8]; |
| } |
| |
| @Override |
| public String getFormat() { |
| return "RAW"; |
| } |
| }; |
| assertEquals( |
| "ad262969c53bc16032f160081c4a07a0", |
| Hashing.hmacMd5(customKey) |
| .hashString("The quick brown fox jumps over the lazy dog", UTF_8) |
| .toString()); |
| } |
| |
| public void testBadKey_emptyKey() throws Exception { |
| SecretKey badKey = |
| new SecretKey() { |
| @Override |
| public String getAlgorithm() { |
| return "HmacMD5"; |
| } |
| |
| @Override |
| public byte[] getEncoded() { |
| return null; |
| } |
| |
| @Override |
| public String getFormat() { |
| return "RAW"; |
| } |
| }; |
| try { |
| Hashing.hmacMd5(badKey); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } catch (NullPointerException toleratedOnAndroid) { |
| // TODO(cpovirk): In an ideal world, we'd check here that we're running on Android. |
| } |
| } |
| |
| public void testEmptyInputs() throws Exception { |
| String knownOutput = "8cbf764cbe2e4623d99a41354adfd390"; |
| |
| Mac mac = Mac.getInstance("HmacMD5"); |
| mac.init(MD5_KEY); |
| assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString()); |
| assertEquals(knownOutput, Hashing.hmacMd5(MD5_KEY).newHasher().hash().toString()); |
| } |
| |
| public void testEmptyInputs_mixedAlgorithms() throws Exception { |
| String knownOutput = "8cbf764cbe2e4623d99a41354adfd390"; |
| |
| Mac mac = Mac.getInstance("HmacMD5"); |
| mac.init(SHA1_KEY); |
| assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString()); |
| assertEquals(knownOutput, Hashing.hmacMd5(SHA1_KEY).newHasher().hash().toString()); |
| } |
| |
| public void testKnownInputs() throws Exception { |
| String knownOutput = "9753980fe94daa8ecaa82216519393a9"; |
| String input = "The quick brown fox jumps over the lazy dog"; |
| |
| Mac mac = Mac.getInstance("HmacMD5"); |
| mac.init(MD5_KEY); |
| mac.update(input.getBytes(UTF_8)); |
| assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString()); |
| assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal(input.getBytes(UTF_8))).toString()); |
| assertEquals(knownOutput, Hashing.hmacMd5(MD5_KEY).hashString(input, UTF_8).toString()); |
| assertEquals(knownOutput, Hashing.hmacMd5(MD5_KEY).hashBytes(input.getBytes(UTF_8)).toString()); |
| } |
| |
| public void testKnownInputs_mixedAlgorithms() throws Exception { |
| String knownOutput = "9753980fe94daa8ecaa82216519393a9"; |
| String input = "The quick brown fox jumps over the lazy dog"; |
| |
| Mac mac = Mac.getInstance("HmacMD5"); |
| mac.init(SHA1_KEY); |
| mac.update(input.getBytes(UTF_8)); |
| assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal()).toString()); |
| assertEquals(knownOutput, HashCode.fromBytes(mac.doFinal(input.getBytes(UTF_8))).toString()); |
| assertEquals(knownOutput, Hashing.hmacMd5(SHA1_KEY).hashString(input, UTF_8).toString()); |
| assertEquals( |
| knownOutput, Hashing.hmacMd5(SHA1_KEY).hashBytes(input.getBytes(UTF_8)).toString()); |
| } |
| |
| public void testPutAfterHash() { |
| Hasher hasher = Hashing.hmacMd5(MD5_KEY).newHasher(); |
| |
| assertEquals( |
| "9753980fe94daa8ecaa82216519393a9", |
| hasher.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString()); |
| try { |
| hasher.putInt(42); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| public void testHashTwice() { |
| Hasher hasher = Hashing.hmacMd5(MD5_KEY).newHasher(); |
| |
| assertEquals( |
| "9753980fe94daa8ecaa82216519393a9", |
| hasher.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString()); |
| try { |
| hasher.hash(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| public void testToString() { |
| byte[] keyData = "secret key".getBytes(UTF_8); |
| |
| assertEquals( |
| "Hashing.hmacMd5(Key[algorithm=HmacMD5, format=RAW])", Hashing.hmacMd5(MD5_KEY).toString()); |
| assertEquals( |
| "Hashing.hmacMd5(Key[algorithm=HmacMD5, format=RAW])", Hashing.hmacMd5(keyData).toString()); |
| |
| assertEquals( |
| "Hashing.hmacSha1(Key[algorithm=HmacSHA1, format=RAW])", |
| Hashing.hmacSha1(SHA1_KEY).toString()); |
| assertEquals( |
| "Hashing.hmacSha1(Key[algorithm=HmacSHA1, format=RAW])", |
| Hashing.hmacSha1(keyData).toString()); |
| |
| assertEquals( |
| "Hashing.hmacSha256(Key[algorithm=HmacSHA256, format=RAW])", |
| Hashing.hmacSha256(SHA256_KEY).toString()); |
| assertEquals( |
| "Hashing.hmacSha256(Key[algorithm=HmacSHA256, format=RAW])", |
| Hashing.hmacSha256(keyData).toString()); |
| |
| assertEquals( |
| "Hashing.hmacSha512(Key[algorithm=HmacSHA512, format=RAW])", |
| Hashing.hmacSha512(SHA512_KEY).toString()); |
| assertEquals( |
| "Hashing.hmacSha512(Key[algorithm=HmacSHA512, format=RAW])", |
| Hashing.hmacSha512(keyData).toString()); |
| } |
| |
| private static void assertMacHashing( |
| byte[] input, String algorithm, SecretKey key, HashFunction hashFunc) throws Exception { |
| Mac mac = Mac.getInstance(algorithm); |
| mac.init(key); |
| mac.update(input); |
| |
| assertEquals(HashCode.fromBytes(mac.doFinal()), hashFunc.hashBytes(input)); |
| assertEquals(HashCode.fromBytes(mac.doFinal(input)), hashFunc.hashBytes(input)); |
| } |
| |
| // Tests from RFC2022: https://tools.ietf.org/html/rfc2202 |
| |
| public void testRfc2202_hmacSha1_case1() { |
| byte[] key = fillByteArray(20, 0x0b); |
| String data = "Hi There"; |
| |
| checkSha1("b617318655057264e28bc0b6fb378c8ef146be00", key, data); |
| } |
| |
| public void testRfc2202_hmacSha1_case2() { |
| byte[] key = "Jefe".getBytes(UTF_8); |
| String data = "what do ya want for nothing?"; |
| |
| checkSha1("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", key, data); |
| } |
| |
| public void testRfc2202_hmacSha1_case3() { |
| byte[] key = fillByteArray(20, 0xaa); |
| byte[] data = fillByteArray(50, 0xdd); |
| |
| checkSha1("125d7342b9ac11cd91a39af48aa17b4f63f175d3", key, data); |
| } |
| |
| public void testRfc2202_hmacSha1_case4() { |
| byte[] key = base16().lowerCase().decode("0102030405060708090a0b0c0d0e0f10111213141516171819"); |
| byte[] data = fillByteArray(50, 0xcd); |
| |
| checkSha1("4c9007f4026250c6bc8414f9bf50c86c2d7235da", key, data); |
| } |
| |
| public void testRfc2202_hmacSha1_case5() { |
| byte[] key = fillByteArray(20, 0x0c); |
| String data = "Test With Truncation"; |
| |
| checkSha1("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", key, data); |
| } |
| |
| public void testRfc2202_hmacSha1_case6() { |
| byte[] key = fillByteArray(80, 0xaa); |
| String data = "Test Using Larger Than Block-Size Key - Hash Key First"; |
| |
| checkSha1("aa4ae5e15272d00e95705637ce8a3b55ed402112", key, data); |
| } |
| |
| public void testRfc2202_hmacSha1_case7() { |
| byte[] key = fillByteArray(80, 0xaa); |
| String data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"; |
| |
| checkSha1("e8e99d0f45237d786d6bbaa7965c7808bbff1a91", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case1() { |
| byte[] key = fillByteArray(16, 0x0b); |
| String data = "Hi There"; |
| |
| checkMd5("9294727a3638bb1c13f48ef8158bfc9d", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case2() { |
| byte[] key = "Jefe".getBytes(UTF_8); |
| String data = "what do ya want for nothing?"; |
| |
| checkMd5("750c783e6ab0b503eaa86e310a5db738", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case3() { |
| byte[] key = fillByteArray(16, 0xaa); |
| byte[] data = fillByteArray(50, 0xdd); |
| |
| checkMd5("56be34521d144c88dbb8c733f0e8b3f6", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case4() { |
| byte[] key = base16().lowerCase().decode("0102030405060708090a0b0c0d0e0f10111213141516171819"); |
| byte[] data = fillByteArray(50, 0xcd); |
| |
| checkMd5("697eaf0aca3a3aea3a75164746ffaa79", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case5() { |
| byte[] key = fillByteArray(16, 0x0c); |
| String data = "Test With Truncation"; |
| |
| checkMd5("56461ef2342edc00f9bab995690efd4c", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case6() { |
| byte[] key = fillByteArray(80, 0xaa); |
| String data = "Test Using Larger Than Block-Size Key - Hash Key First"; |
| |
| checkMd5("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd", key, data); |
| } |
| |
| public void testRfc2202_hmacMd5_case7() { |
| byte[] key = fillByteArray(80, 0xaa); |
| String data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"; |
| |
| checkMd5("6f630fad67cda0ee1fb1f562db3aa53e", key, data); |
| } |
| |
| private static void checkSha1(String expected, byte[] key, String data) { |
| checkSha1(expected, key, data.getBytes(UTF_8)); |
| } |
| |
| private static void checkSha1(String expected, byte[] key, byte[] data) { |
| checkHmac(expected, Hashing.hmacSha1(key), data); |
| } |
| |
| private static void checkMd5(String expected, byte[] key, String data) { |
| checkMd5(expected, key, data.getBytes(UTF_8)); |
| } |
| |
| private static void checkMd5(String expected, byte[] key, byte[] data) { |
| checkHmac(expected, Hashing.hmacMd5(key), data); |
| } |
| |
| private static void checkHmac(String expected, HashFunction hashFunc, byte[] data) { |
| assertEquals(HashCode.fromString(expected), hashFunc.hashBytes(data)); |
| } |
| |
| private static byte[] fillByteArray(int size, int toFillWith) { |
| byte[] array = new byte[size]; |
| Arrays.fill(array, (byte) toFillWith); |
| return array; |
| } |
| } |