1 /** 2 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 3 * in compliance with the License. You may obtain a copy of the License at 4 * 5 * <p>http://www.apache.org/licenses/LICENSE-2.0 6 * 7 * <p>Unless required by applicable law or agreed to in writing, software distributed under the 8 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 9 * express or implied. See the License for the specific language governing permissions and 10 * limitations under the License. 11 */ 12 package com.google.security.wycheproof; 13 14 import static java.nio.charset.StandardCharsets.UTF_8; 15 import static org.junit.Assert.assertEquals; 16 import static org.junit.Assert.fail; 17 18 import java.nio.ByteBuffer; 19 import java.security.GeneralSecurityException; 20 import java.security.Key; 21 import java.security.KeyStore; 22 import java.security.NoSuchAlgorithmException; 23 import java.security.SecureRandom; 24 import javax.crypto.Mac; 25 import javax.crypto.spec.SecretKeySpec; 26 import org.junit.After; 27 import org.junit.Test; 28 import org.junit.Ignore; 29 import android.security.keystore.KeyProtection; 30 import android.security.keystore.KeyProperties; 31 import android.keystore.cts.util.KeyStoreUtil; 32 33 /** 34 * Tests for MACs. 35 * 36 * <p>TODO(bleichen): The tests are quite incomplete. Some of the missing stuff: More test vectors 37 * with known results are necessary. So far only simple test vectors for long messages are 38 * available. 39 */ 40 public class MacTest { 41 private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; 42 private static final String KEY_ALIAS_1 = "TestKey"; 43 44 @After tearDown()45 public void tearDown() throws Exception { 46 KeyStoreUtil.cleanUpKeyStore(); 47 } 48 getKeyStoreSecretKey(byte[] keyMaterial, String algorithm, boolean isStrongBox)49 private static Key getKeyStoreSecretKey(byte[] keyMaterial, String algorithm, boolean isStrongBox) 50 throws Exception { 51 KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1, 52 new SecretKeySpec(keyMaterial, algorithm), 53 new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) 54 .setIsStrongBoxBacked(isStrongBox).build()); 55 return keyStore.getKey(KEY_ALIAS_1, null); 56 } 57 58 /** 59 * Computes the maximum of an array with at least one element. 60 * 61 * @param values the values from which the max is computed. 62 * @return the maximum 63 * @throws IllegalArgumentException if values is empty of null. 64 */ max(int[] values)65 private static int max(int[] values) { 66 if (values == null || values.length == 0) { 67 throw new IllegalArgumentException("Expecting an array with at least one element"); 68 } 69 int result = Integer.MIN_VALUE; 70 for (int value : values) { 71 result = Math.max(result, value); 72 } 73 return result; 74 } 75 arrayEquals(byte[] a, byte[] b)76 protected static boolean arrayEquals(byte[] a, byte[] b) { 77 if (a.length != b.length) { 78 return false; 79 } 80 byte res = 0; 81 for (int i = 0; i < a.length; i++) { 82 res |= (byte) (a[i] ^ b[i]); 83 } 84 return res == 0; 85 } 86 87 /** 88 * Tests computing a MAC by computing it multiple times. The test passes all the results are the 89 * same in all cases. 90 * 91 * @param algorithm the name of the MAC (e.g. "HMACSHA1") 92 * @param key the key of the MAC 93 * @param data input data for the MAC. The size of the data must be at least as long as the sum of 94 * all chunkSizes. 95 * @param chunkSizes the sizes of the chunks used in the calls of update 96 */ testUpdateWithChunks(String algorithm, Key key, byte[] data, int... chunkSizes)97 private void testUpdateWithChunks(String algorithm, Key key, byte[] data, int... chunkSizes) 98 throws Exception { 99 Mac mac = Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); 100 101 // First evaluation: compute MAC in one piece. 102 int totalLength = 0; 103 for (int chunkSize : chunkSizes) { 104 totalLength += chunkSize; 105 } 106 mac.init(key); 107 mac.update(data, 0, totalLength); 108 byte[] mac1 = mac.doFinal(); 109 110 // Second evaluation: using multiple chunks 111 mac.init(key); 112 int start = 0; 113 for (int chunkSize : chunkSizes) { 114 mac.update(data, start, chunkSize); 115 start += chunkSize; 116 } 117 byte[] mac2 = mac.doFinal(); 118 if (!arrayEquals(mac1, mac2)) { 119 fail( 120 "Different MACs for same input:" 121 + " computed as one piece:" 122 + TestUtil.bytesToHex(mac1) 123 + " computed with multiple array segments:" 124 + TestUtil.bytesToHex(mac2)); 125 } 126 // Third evaluation: using ByteBuffers 127 mac.init(key); 128 start = 0; 129 for (int chunkSize : chunkSizes) { 130 ByteBuffer chunk = ByteBuffer.wrap(data, start, chunkSize); 131 mac.update(chunk); 132 start += chunkSize; 133 } 134 byte[] mac3 = mac.doFinal(); 135 if (!arrayEquals(mac1, mac3)) { 136 fail( 137 "Different MACs for same input:" 138 + " computed as one piece:" 139 + TestUtil.bytesToHex(mac1) 140 + " computed with wrapped chunks:" 141 + TestUtil.bytesToHex(mac3)); 142 } 143 // Forth evaluation: using ByteBuffer slices. 144 // The effect of using slice() is that the resulting ByteBuffer has 145 // position 0, but possibly an non-zero value for arrayOffset(). 146 mac.init(key); 147 start = 0; 148 for (int chunkSize : chunkSizes) { 149 ByteBuffer chunk = ByteBuffer.wrap(data, start, chunkSize).slice(); 150 mac.update(chunk); 151 start += chunkSize; 152 } 153 byte[] mac4 = mac.doFinal(); 154 if (!arrayEquals(mac1, mac4)) { 155 fail( 156 "Different MACs for same input:" 157 + " computed as one piece:" 158 + TestUtil.bytesToHex(mac1) 159 + " computed with ByteBuffer slices:" 160 + TestUtil.bytesToHex(mac4)); 161 } 162 } 163 164 /** 165 * The paper "Finding Bugs in Cryptographic Hash Function Implementations" by Mouha, Raunak, Kuhn, 166 * and Kacker, https://eprint.iacr.org/2017/891.pdf contains an analysis of implementations 167 * submitted to the SHA-3 competition. Many of the implementations contain bugs. The authors 168 * propose some tests for cryptographic libraries. The test here implements a check for 169 * incremental updates with the values proposed in Table 3. 170 */ testUpdate(String algorithm, Key key)171 private void testUpdate(String algorithm, Key key) throws Exception { 172 int[] chunkSize1 = {0, 8, 16, 24, 32, 40, 48, 56, 64}; 173 int[] chunkSize2 = {0, 8, 16, 24, 32, 40, 48, 56, 64}; 174 int[] chunkSize3 = {0, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}; 175 int[] chunkSize4 = { 176 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 177 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 178 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 127, 128, 129, 255, 256, 179 257, 511, 512, 513 180 }; 181 int maxSize = max(chunkSize1) + max(chunkSize2) + max(chunkSize3) + max(chunkSize4); 182 byte[] data = new byte[maxSize]; 183 SecureRandom rand = new SecureRandom(); 184 rand.nextBytes(data); 185 for (int size1 : chunkSize1) { 186 for (int size2 : chunkSize2) { 187 for (int size3 : chunkSize3) { 188 for (int size4 : chunkSize4) { 189 testUpdateWithChunks(algorithm, key, data, size1, size2, size3, size4); 190 } 191 } 192 } 193 } 194 } 195 testMac(String algorithm, int keySize)196 public void testMac(String algorithm, int keySize) throws Exception { 197 testMac(algorithm, keySize, false); 198 } testMac(String algorithm, int keySize, boolean isStrongBox)199 public void testMac(String algorithm, int keySize, boolean isStrongBox) throws Exception { 200 try { 201 Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); 202 } catch (NoSuchAlgorithmException ex) { 203 fail("Algorithm " + algorithm + " is not supported."); 204 } 205 byte[] key = new byte[keySize]; 206 SecureRandom rand = new SecureRandom(); 207 rand.nextBytes(key); 208 testUpdate(algorithm, getKeyStoreSecretKey(key, algorithm, isStrongBox)); 209 } 210 211 @Test 212 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 213 // performance requirements can be defined. See http://b/296367623 214 @Ignore testHmacSha1()215 public void testHmacSha1() throws Exception { 216 testMac("HMACSHA1", 20); 217 } 218 219 @Test 220 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 221 // performance requirements can be defined. See http://b/296367623 222 @Ignore testHmacSha224()223 public void testHmacSha224() throws Exception { 224 testMac("HMACSHA224", 28); 225 } 226 227 @Test 228 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 229 // performance requirements can be defined. See http://b/296367623 230 @Ignore testHmacSha256()231 public void testHmacSha256() throws Exception { 232 testMac("HMACSHA256", 32); 233 } 234 235 @Test 236 @Ignore // StrongBox takes very long time to complete this test and CTS timed out (b/242028608), hence ignoring it. testHmacSha256_StrongBox()237 public void testHmacSha256_StrongBox() throws Exception { 238 KeyStoreUtil.assumeStrongBox(); 239 testMac("HMACSHA256", 32, true); 240 } 241 242 @Test 243 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 244 // performance requirements can be defined. See http://b/296367623 245 @Ignore testHmacSha384()246 public void testHmacSha384() throws Exception { 247 testMac("HMACSHA384", 48); 248 } 249 250 @Test 251 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 252 // performance requirements can be defined. See http://b/296367623 253 @Ignore testHmacSha512()254 public void testHmacSha512() throws Exception { 255 testMac("HMACSHA512", 64); 256 } 257 258 @Test 259 @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore testHmacSha3_224()260 public void testHmacSha3_224() throws Exception { 261 testMac("HMACSHA3-224", 28); 262 } 263 264 @Test 265 @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore testHmacSha3_256()266 public void testHmacSha3_256() throws Exception { 267 testMac("HMACSHA3-256", 32); 268 } 269 270 @Test 271 @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore testHmacSha3_384()272 public void testHmacSha3_384() throws Exception { 273 testMac("HMACSHA3-384", 48); 274 } 275 276 @Test 277 @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore testHmacSha3_512()278 public void testHmacSha3_512() throws Exception { 279 testMac("HMACSHA3-512", 64); 280 } 281 282 /** 283 * Computes the mac of a message repeated multiple times. 284 * 285 * @param algorithm the message digest (e.g. "HMACSHA1") 286 * @param message the bytes to mac 287 * @param repetitions the number of repetitions of the message 288 * @return the digest 289 * @throws GeneralSecurityException if the computation of the mac fails (e.g. because the 290 * algorithm is unknown). 291 */ macRepeatedMessage(String algorithm, Key key, byte[] message, long repetitions)292 public byte[] macRepeatedMessage(String algorithm, Key key, byte[] message, long repetitions) 293 throws Exception { 294 Mac mac = Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); 295 mac.init(key); 296 // If the message is short then it is more efficient to collect multiple copies 297 // of the message in one chunk and call update with the larger chunk. 298 final int maxChunkSize = 1 << 16; 299 if (message.length != 0 && 2 * message.length < maxChunkSize) { 300 int repetitionsPerChunk = maxChunkSize / message.length; 301 byte[] chunk = new byte[message.length * repetitionsPerChunk]; 302 for (int i = 0; i < repetitionsPerChunk; i++) { 303 System.arraycopy(message, 0, chunk, i * message.length, message.length); 304 } 305 while (repetitions >= repetitionsPerChunk) { 306 mac.update(chunk); 307 repetitions -= repetitionsPerChunk; 308 } 309 } 310 311 for (int i = 0; i < repetitions; i++) { 312 mac.update(message); 313 } 314 return mac.doFinal(); 315 } 316 317 /** 318 * A test for hashing long messages. 319 * 320 * <p>Java does not allow strings or arrays of size 2^31 or longer. However, it is still possible 321 * to compute a MAC of a long message by repeatedly calling Mac.update(). To compute correct MACs 322 * the total message length must be known. This length can be bigger than 2^32 bytes. 323 * 324 * <p>Reference: http://www-01.ibm.com/support/docview.wss?uid=swg1PK62549 IBMJCE SHA-1 325 * IMPLEMENTATION RETURNS INCORRECT HASH FOR LARGE SETS OF DATA 326 */ testLongMac( String algorithm, String keyhex, String message, long repetitions, String expected)327 private void testLongMac( 328 String algorithm, String keyhex, String message, long repetitions, String expected) 329 throws Exception { 330 testLongMac(algorithm, keyhex, message, repetitions, expected, false); 331 } testLongMac( String algorithm, String keyhex, String message, long repetitions, String expected, boolean isStrongBox)332 private void testLongMac( 333 String algorithm, String keyhex, String message, long repetitions, String expected, 334 boolean isStrongBox) throws Exception { 335 336 Key key = getKeyStoreSecretKey(TestUtil.hexToBytes(keyhex), algorithm, isStrongBox); 337 byte[] bytes = message.getBytes(UTF_8); 338 byte[] mac = null; 339 try { 340 mac = macRepeatedMessage(algorithm, key, bytes, repetitions); 341 } catch (NoSuchAlgorithmException ex) { 342 fail("Algorithm " + algorithm + " is not supported."); 343 } 344 String hexmac = TestUtil.bytesToHex(mac); 345 assertEquals(expected, hexmac); 346 } 347 348 @Test 349 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 350 // performance requirements can be defined. See http://b/288588810#comment27 351 @Ignore testLongMacSha1()352 public void testLongMacSha1() throws Exception { 353 // b/244609904#comment64 354 KeyStoreUtil.assumeKeyMintV1OrNewer(false); 355 356 testLongMac( 357 "HMACSHA1", 358 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 359 "a", 360 2147483647L, 361 "703925f6dceb9c602969ad39bba9b1eb49472071"); 362 testLongMac( 363 "HMACSHA1", 364 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 365 "a", 366 5000000000L, 367 "d7f4c387f2237ea119fcc27cd7520fc5132b6230"); 368 } 369 370 @Test 371 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 372 // performance requirements can be defined. See http://b/288588810#comment27 373 @Ignore testLongMacSha256()374 public void testLongMacSha256() throws Exception { 375 // b/244609904#comment64 376 KeyStoreUtil.assumeKeyMintV1OrNewer(false); 377 testLongMacSha256(false); 378 } 379 @Test 380 @Ignore // StrongBox takes very long time to complete this test and CTS timed out (b/242028608), hence ignoring it. testLongMacSha256_StrongBox()381 public void testLongMacSha256_StrongBox() throws Exception { 382 KeyStoreUtil.assumeStrongBox(); 383 testLongMacSha256(true); 384 } testLongMacSha256(boolean isStrongBox)385 private void testLongMacSha256(boolean isStrongBox) throws Exception { 386 testLongMac( 387 "HMACSHA256", 388 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 389 "a", 390 2147483647L, 391 "84f213c9bb5b329d547bc31dabed41939754b1af7482365ec74380c45f6ea0a7", 392 isStrongBox); 393 testLongMac( 394 "HMACSHA256", 395 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 396 "a", 397 5000000000L, 398 "59a75754df7093fa4339aa618b64b104f153a5b42cc85394fdb8735b13ea684a", 399 isStrongBox); 400 } 401 402 @Test 403 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 404 // performance requirements can be defined. See http://b/288588810#comment27 405 @Ignore testLongMacSha384()406 public void testLongMacSha384() throws Exception { 407 // b/244609904#comment64 408 KeyStoreUtil.assumeKeyMintV1OrNewer(false); 409 410 testLongMac( 411 "HMACSHA384", 412 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" 413 + "202122232425262728292a2b2c2d2e2f", 414 "a", 415 2147483647L, 416 "aea987905f64791691b3fdea06f8e4125f396ebb73f37894e961b1a7522a55da" 417 + "ecd856a70c92c6646e6f8c3fcb935528"); 418 testLongMac( 419 "HMACSHA384", 420 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" 421 + "202122232425262728292a2b2c2d2e2f", 422 "a", 423 5000000000L, 424 "88485c9c5714d43a99dacbc861988c7ea39c02d82104bf93e55ec1b8a24fe15a" 425 + "a477e6a84d159d8b7a3daaa89c4f2372"); 426 } 427 428 @Test 429 // Long-running MAC tests expose inefficiencies on some devices. Ignore the test until 430 // performance requirements can be defined. See http://b/288588810#comment27 431 @Ignore testLongMacSha512()432 public void testLongMacSha512() throws Exception { 433 // b/244609904#comment64 434 KeyStoreUtil.assumeKeyMintV1OrNewer(false); 435 436 testLongMac( 437 "HMACSHA512", 438 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" 439 + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", 440 "a", 441 2147483647L, 442 "fc68fbc294951c691e5bc085c3af026099f39a57230b242aaf1fc5ca691e05da" 443 + "d1a5de7d4f30e1c958c6a2cee6159218dab683187e6d56bab824a3adefde9102"); 444 testLongMac( 445 "HMACSHA512", 446 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" 447 + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", 448 "a", 449 5000000000L, 450 "31b1d721b958203bff7d7ddf50d48b17fc760a80a99a7f23ec966ce3bbefff29" 451 + "0d176eebbb6a440960024be0726c94960bbf75816548a7fd4552c7baba4585ee"); 452 } 453 } 454