• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.subtle;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertArrayEquals;
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertThrows;
23 import static org.junit.Assert.fail;
24 
25 import com.google.crypto.tink.InsecureSecretKeyAccess;
26 import com.google.crypto.tink.Mac;
27 import com.google.crypto.tink.config.TinkFips;
28 import com.google.crypto.tink.config.internal.TinkFipsUtil;
29 import com.google.crypto.tink.prf.HmacPrfKey;
30 import com.google.crypto.tink.prf.HmacPrfParameters;
31 import com.google.crypto.tink.prf.HmacPrfParameters.HashType;
32 import com.google.crypto.tink.prf.Prf;
33 import com.google.crypto.tink.testing.TestUtil;
34 import com.google.crypto.tink.util.SecretBytes;
35 import java.security.GeneralSecurityException;
36 import java.security.InvalidAlgorithmParameterException;
37 import java.security.Security;
38 import java.util.Arrays;
39 import javax.crypto.spec.SecretKeySpec;
40 import org.conscrypt.Conscrypt;
41 import org.junit.Assume;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.junit.runners.JUnit4;
46 
47 /**
48  * Unit tests for PrfHmacJce. Note that this used to be a Mac primitive, so all these tests first
49  * convert the Prf to a Mac.
50  */
51 @RunWith(JUnit4.class)
52 public class PrfHmacJceTest {
53   private static class MacTestVector {
54     String algName;
55     public byte[] key;
56     public byte[] message;
57     public byte[] tag;
58 
MacTestVector(String algName, String key, String message, String tag)59     public MacTestVector(String algName, String key, String message, String tag) {
60       this.algName = algName;
61       this.key = Hex.decode(key);
62       this.message = Hex.decode(message);
63       this.tag = Hex.decode(tag);
64     }
65   }
66 
67   // Test data from http://csrc.nist.gov/groups/STM/cavp/message-authentication.html#testing
68   // and https://tools.ietf.org/html/rfc4231.
69   private static final MacTestVector[] HMAC_TEST_VECTORS = {
70     new MacTestVector(
71         "HMACSHA1",
72         "816aa4c3ee066310ac1e6666cf830c375355c3c8ba18cfe1f50a48c988b46272",
73         "220248f5e6d7a49335b3f91374f18bb8b0ff5e8b9a5853f3cfb293855d78301d837a0a2eb9e4f056f06c08361"
74             + "bd07180ee802651e69726c28910d2baef379606815dcbab01d0dc7acb0ba8e65a2928130da0522f2b2b3d05260"
75             + "885cf1c64f14ca3145313c685b0274bf6a1cb38e4f99895c6a8cc72fbe0e52c01766fede78a1a",
76         "17cb2e9e98b748b5ae0f7078ea5519e5"),
77     new MacTestVector(
78         "HMACSHA256",
79         "6f35628d65813435534b5d67fbdb54cb33403d04e843103e6399f806cb5df95febbdd61236f33245",
80         "752cff52e4b90768558e5369e75d97c69643509a5e5904e0a386cbe4d0970ef73f918f675945a9aefe26daea27"
81             + "587e8dc909dd56fd0468805f834039b345f855cfe19c44b55af241fff3ffcd8045cd5c288e6c4e284c3720570b"
82             + "58e4d47b8feeedc52fd1401f698a209fccfa3b4c0d9a797b046a2759f82a54c41ccd7b5f592b",
83         "05d1243e6465ed9620c9aec1c351a186"),
84     new MacTestVector(
85         "HMACSHA384",
86         "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
87         "4869205468657265",
88         "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6"),
89     new MacTestVector(
90         "HMACSHA512",
91         "726374c4b8df517510db9159b730f93431e0cd468d4f3821eab0edb93abd0fba46ab4f1ef35d54fec3d85fa89e"
92             + "f72ff3d35f22cf5ab69e205c10afcdf4aaf11338dbb12073474fddb556e60b8ee52f91163ba314303ee0c910e6"
93             + "4e87fbf302214edbe3f2",
94         "ac939659dc5f668c9969c0530422e3417a462c8b665e8db25a883a625f7aa59b89c5ad0ece5712ca17442d1798"
95             + "c6dea25d82c5db260cb59c75ae650be56569c1bd2d612cc57e71315917f116bbfa65a0aeb8af7840ee83d3e710"
96             + "1c52cf652d2773531b7a6bdd690b846a741816c860819270522a5b0cdfa1d736c501c583d916",
97         "bd3d2df6f9d284b421a43e5f9cb94bc4ff88a88243f1f0133bad0fb1791f6569"),
98   };
99 
100   @Before
useConscrypt()101   public void useConscrypt() throws Exception {
102     // If Tink is build in FIPS-only mode, then we register Conscrypt for the tests.
103     if (TinkFips.useOnlyFips()) {
104       try {
105         Conscrypt.checkAvailability();
106         Security.addProvider(Conscrypt.newProvider());
107       } catch (Throwable cause) {
108         throw new IllegalStateException(
109             "Cannot test HMAC in FIPS-mode without Conscrypt Provider", cause);
110       }
111     }
112   }
113 
114   @Test
testMacTestVectors()115   public void testMacTestVectors() throws Exception {
116     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
117 
118     for (MacTestVector t : HMAC_TEST_VECTORS) {
119       Mac mac =
120           new PrfMac(new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC")), t.tag.length);
121       assertArrayEquals(t.tag, mac.computeMac(t.message));
122       try {
123         mac.verifyMac(t.tag, t.message);
124       } catch (GeneralSecurityException e) {
125         throw new AssertionError("Valid MAC, should not throw exception", e);
126       }
127     }
128   }
129 
130   @Test
testPrfUniformity()131   public void testPrfUniformity() throws GeneralSecurityException {
132     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
133 
134     for (MacTestVector t : HMAC_TEST_VECTORS) {
135       Prf prf = new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC"));
136       // We need a string of bytes identical in size to the tag output size for the given algorithm
137       // so we can test cross correlation. We're not actually validating the output contents of the
138       // HMAC in this function. Therefore - just feed the test tag into the HMAC.
139       byte[] prBytes = prf.compute(t.tag, t.tag.length);
140       TestUtil.ztestUniformString(prBytes);
141       TestUtil.ztestAutocorrelationUniformString(prBytes);
142       TestUtil.ztestCrossCorrelationUniformStrings(prBytes, t.tag);
143     }
144   }
145 
146   @Test
testPrfPrefixOfMac()147   public void testPrfPrefixOfMac() throws Exception {
148     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
149 
150     for (MacTestVector t : HMAC_TEST_VECTORS) {
151       Prf prf = new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC"));
152       Mac mac = new PrfMac(prf, t.tag.length);
153       byte[] prBytes = prf.compute(t.message, t.tag.length - 1);
154       byte[] tag = mac.computeMac(t.message);
155 
156       assertEquals(prBytes.length, t.tag.length - 1);
157       assertArrayEquals(prBytes, Arrays.copyOf(tag, prBytes.length));
158     }
159   }
160 
161   @Test
testTagTruncation()162   public void testTagTruncation() throws Exception {
163     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
164 
165     for (MacTestVector t : HMAC_TEST_VECTORS) {
166       Mac mac =
167           new PrfMac(new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC")), t.tag.length);
168       for (int j = 1; j < t.tag.length; j++) {
169         byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length - j);
170         assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
171       }
172     }
173     // Test with random keys.
174     for (MacTestVector t : HMAC_TEST_VECTORS) {
175       Mac mac =
176           new PrfMac(
177               new PrfHmacJce(t.algName, new SecretKeySpec(Random.randBytes(t.key.length), "HMAC")),
178               t.tag.length);
179       for (int j = 1; j < t.tag.length; j++) {
180         byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length - j);
181         assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
182       }
183     }
184   }
185 
186   @Test
testBitFlipMessage()187   public void testBitFlipMessage() throws Exception {
188     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
189 
190     for (MacTestVector t : HMAC_TEST_VECTORS) {
191       Mac mac =
192           new PrfMac(new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC")), t.tag.length);
193       for (int b = 0; b < t.message.length; b++) {
194         for (int bit = 0; bit < 8; bit++) {
195           byte[] modifiedMessage = Arrays.copyOf(t.message, t.message.length);
196           modifiedMessage[b] = (byte) (modifiedMessage[b] ^ (1 << bit));
197           assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(t.tag, modifiedMessage));
198         }
199       }
200     }
201     // Test with random keys.
202     for (MacTestVector t : HMAC_TEST_VECTORS) {
203       Mac mac =
204           new PrfMac(
205               new PrfHmacJce(t.algName, new SecretKeySpec(Random.randBytes(t.key.length), "HMAC")),
206               t.tag.length);
207       for (int j = 1; j < t.tag.length; j++) {
208         byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length - j);
209         assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
210       }
211     }
212   }
213 
214   @Test
testBitFlipTag()215   public void testBitFlipTag() throws Exception {
216     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
217 
218     for (MacTestVector t : HMAC_TEST_VECTORS) {
219       Mac mac =
220           new PrfMac(new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC")), t.tag.length);
221       for (int b = 0; b < t.tag.length; b++) {
222         for (int bit = 0; bit < 8; bit++) {
223           byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length);
224           modifiedTag[b] = (byte) (modifiedTag[b] ^ (1 << bit));
225           assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
226         }
227       }
228     }
229     // Test with random keys.
230     for (MacTestVector t : HMAC_TEST_VECTORS) {
231       Mac mac =
232           new PrfMac(
233               new PrfHmacJce(t.algName, new SecretKeySpec(Random.randBytes(t.key.length), "HMAC")),
234               t.tag.length);
235       for (int b = 0; b < t.tag.length; b++) {
236         for (int bit = 0; bit < 8; bit++) {
237           byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length);
238           modifiedTag[b] = (byte) (modifiedTag[b] ^ (1 << bit));
239           assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
240         }
241       }
242     }
243   }
244 
245   @Test
testThrowExceptionIfKeySizeIsTooSmall()246   public void testThrowExceptionIfKeySizeIsTooSmall() throws Exception {
247     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
248 
249     assertThrows(
250         InvalidAlgorithmParameterException.class,
251         () ->
252             new PrfMac(
253                 new PrfHmacJce("HMACSHA1", new SecretKeySpec(Random.randBytes(15), "HMAC")), 16));
254   }
255 
256   @Test
testThrowExceptionIfTagSizeIsTooSmall()257   public void testThrowExceptionIfTagSizeIsTooSmall() throws Exception {
258     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
259 
260     testThrowExceptionIfTagSizeIsTooSmall("HMACSHA1");
261     testThrowExceptionIfTagSizeIsTooSmall("HMACSHA256");
262     testThrowExceptionIfTagSizeIsTooSmall("HMACSHA512");
263   }
264 
testThrowExceptionIfTagSizeIsTooSmall(String algoName)265   private static void testThrowExceptionIfTagSizeIsTooSmall(String algoName) throws Exception {
266     for (int i = 0; i < PrfMac.MIN_TAG_SIZE_IN_BYTES; i++) {
267       try {
268         new PrfMac(new PrfHmacJce(algoName, new SecretKeySpec(Random.randBytes(16), "HMAC")), i);
269         fail("Expected InvalidAlgorithmParameterException");
270       } catch (InvalidAlgorithmParameterException ex) {
271         // expected.
272       }
273     }
274   }
275 
276   @Test
testPrfAllowsSmallTagSizeCompute()277   public void testPrfAllowsSmallTagSizeCompute() throws Exception {
278     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
279 
280     testPrfNoExceptionIfTagSizeIsTooSmall("HMACSHA1");
281     testPrfNoExceptionIfTagSizeIsTooSmall("HMACSHA256");
282     testThrowExceptionIfTagSizeIsTooSmall("HMACSHA384");
283     testPrfNoExceptionIfTagSizeIsTooSmall("HMACSHA512");
284   }
285 
testPrfNoExceptionIfTagSizeIsTooSmall(String algoName)286   private static void testPrfNoExceptionIfTagSizeIsTooSmall(String algoName) throws Exception {
287     for (int i = 0; i < PrfMac.MIN_TAG_SIZE_IN_BYTES; i++) {
288       Object unused =
289           new PrfHmacJce(algoName, new SecretKeySpec(Random.randBytes(16), "HMAC"))
290               .compute(new byte[100], i);
291     }
292   }
293 
294   @Test
testThrowExceptionIfTagSizeIsTooLarge()295   public void testThrowExceptionIfTagSizeIsTooLarge() throws Exception {
296     Assume.assumeTrue(!TinkFips.useOnlyFips() || TinkFipsUtil.fipsModuleAvailable());
297 
298     testThrowExceptionIfTagSizeIsTooLarge("HMACSHA1", 21);
299     testThrowExceptionIfTagSizeIsTooLarge("HMACSHA256", 33);
300     testThrowExceptionIfTagSizeIsTooLarge("HMACSHA384", 49);
301     testThrowExceptionIfTagSizeIsTooLarge("HMACSHA512", 65);
302     testPrfThrowsExceptionIfTagSizeIsTooLarge("HMACSHA1", 21);
303     testPrfThrowsExceptionIfTagSizeIsTooLarge("HMACSHA256", 33);
304     testPrfThrowsExceptionIfTagSizeIsTooLarge("HMACSHA384", 49);
305     testPrfThrowsExceptionIfTagSizeIsTooLarge("HMACSHA512", 65);
306   }
307 
testThrowExceptionIfTagSizeIsTooLarge(String algoName, int tagSize)308   private static void testThrowExceptionIfTagSizeIsTooLarge(String algoName, int tagSize)
309       throws Exception {
310     try {
311       new PrfMac(
312           new PrfHmacJce(algoName, new SecretKeySpec(Random.randBytes(16), "HMAC")), tagSize);
313       fail("Expected InvalidAlgorithmParameterException");
314     } catch (InvalidAlgorithmParameterException ex) {
315       // expected.
316     }
317   }
318 
testPrfThrowsExceptionIfTagSizeIsTooLarge(String algoName, int tagSize)319   public void testPrfThrowsExceptionIfTagSizeIsTooLarge(String algoName, int tagSize)
320       throws Exception {
321     try {
322       Prf r = new PrfHmacJce(algoName, new SecretKeySpec(Random.randBytes(16), "HMAC"));
323       r.compute(new byte[30], tagSize);
324       fail("Expected InvalidAlgorithmParameterException");
325     } catch (InvalidAlgorithmParameterException ex) {
326       // expected.
327     }
328   }
329 
330   @Test
testFailIfFipsModuleNotAvailable()331   public void testFailIfFipsModuleNotAvailable() throws Exception {
332     Assume.assumeTrue(TinkFips.useOnlyFips() && !TinkFipsUtil.fipsModuleAvailable());
333 
334     assertThrows(
335         GeneralSecurityException.class,
336         () -> new PrfHmacJce("HMACSHA256", new SecretKeySpec(Random.randBytes(16), "HMAC")));
337   }
338 
339   @Test
createWithHmacPrfKey_equivalentToByteArray()340   public void createWithHmacPrfKey_equivalentToByteArray() throws Exception {
341     Assume.assumeFalse(TinkFips.useOnlyFips());
342     for (MacTestVector t : HMAC_TEST_VECTORS) {
343       HmacPrfParameters.HashType hashType;
344       switch (t.algName) {
345         case "HMACSHA1":
346           hashType = HashType.SHA1;
347           break;
348         case "HMACSHA224":
349           hashType = HashType.SHA224;
350           break;
351         case "HMACSHA256":
352           hashType = HashType.SHA256;
353           break;
354         case "HMACSHA384":
355           hashType = HashType.SHA384;
356           break;
357         case "HMACSHA512":
358           hashType = HashType.SHA512;
359           break;
360         default:
361           // Should not happen
362           throw new IllegalStateException("Unknown algorithm: " + t.algName);
363       }
364       Prf hmacPrfKeyPrf =
365           PrfHmacJce.create(
366               HmacPrfKey.builder()
367                   .setKeyBytes(SecretBytes.copyFrom(t.key, InsecureSecretKeyAccess.get()))
368                   .setParameters(
369                       HmacPrfParameters.builder()
370                           .setKeySizeBytes(t.key.length)
371                           .setHashType(hashType)
372                           .build())
373                   .build());
374 
375       Prf byteArrayPrf = new PrfHmacJce(t.algName, new SecretKeySpec(t.key, "HMAC"));
376 
377       assertThat(hmacPrfKeyPrf.compute(t.message, t.tag.length))
378           .isEqualTo(byteArrayPrf.compute(t.message, t.tag.length));
379     }
380   }
381 }
382