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