diff options
author | Prashant Patil <patilprashant@google.com> | 2022-04-04 17:03:06 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-04-04 17:03:06 +0000 |
commit | f370f7911b2f90fc299db116b6e3a623dca91398 (patch) | |
tree | 171da275a686c68bd5a5347e43ffd8d12156b42f /keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java | |
parent | 79f88d6cbdbce9979c2a5f9ef8614a421000087f (diff) | |
parent | 0650b8071d04f0f34112a6d78c2a06795e361245 (diff) | |
download | wycheproof-f370f7911b2f90fc299db116b6e3a623dca91398.tar.gz |
KeyStore: Wycheproof github files am: 0978999584 am: 3883de5832 am: d341490638 am: cc03cf106b am: 0650b8071dandroid-13.0.0_r83android-13.0.0_r82android-13.0.0_r81android-13.0.0_r80android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r74android-13.0.0_r73android-13.0.0_r72android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r66android-13.0.0_r65android-13.0.0_r64android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r60android-13.0.0_r59android-13.0.0_r58android-13.0.0_r56android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android-13.0.0_r49android-13.0.0_r48android-13.0.0_r47android-13.0.0_r46android-13.0.0_r45android-13.0.0_r44android-13.0.0_r43android-13.0.0_r42android-13.0.0_r41android-13.0.0_r40android-13.0.0_r39android-13.0.0_r38android-13.0.0_r37android-13.0.0_r36android-13.0.0_r35android-13.0.0_r34android-13.0.0_r33android-13.0.0_r32android13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-releaseandroid13-qpr3-c-s8-releaseandroid13-qpr3-c-s7-releaseandroid13-qpr3-c-s6-releaseandroid13-qpr3-c-s5-releaseandroid13-qpr3-c-s4-releaseandroid13-qpr3-c-s3-releaseandroid13-qpr3-c-s2-releaseandroid13-qpr3-c-s12-releaseandroid13-qpr3-c-s11-releaseandroid13-qpr3-c-s10-releaseandroid13-qpr3-c-s1-releaseandroid13-qpr2-s9-releaseandroid13-qpr2-s8-releaseandroid13-qpr2-s7-releaseandroid13-qpr2-s6-releaseandroid13-qpr2-s5-releaseandroid13-qpr2-s3-releaseandroid13-qpr2-s2-releaseandroid13-qpr2-s12-releaseandroid13-qpr2-s11-releaseandroid13-qpr2-s10-releaseandroid13-qpr2-s1-releaseandroid13-qpr2-releaseandroid13-qpr2-b-s1-releaseandroid13-d4-s2-releaseandroid13-d4-s1-releaseandroid13-d4-release
Original change: https://android-review.googlesource.com/c/platform/external/wycheproof/+/2051321
Change-Id: I2fb46e3b9a23541b0416dcd21b61a47118203ea4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java')
-rw-r--r-- | keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java new file mode 100644 index 0000000..eeb48ec --- /dev/null +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java @@ -0,0 +1,332 @@ +/** + * 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 + * + * <p>http://www.apache.org/licenses/LICENSE-2.0 + * + * <p>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.security.wycheproof; + +import static org.junit.Assert.assertEquals; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Locale; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** This test uses test vectors in JSON format to test MAC primitives. */ +@RunWith(JUnit4.class) +public class JsonMacTest { + + /** Convenience method to get a byte array from an JsonObject */ + protected static byte[] getBytes(JsonObject obj, String name) throws Exception { + return JsonUtil.asByteArray(obj.get(name)); + } + + protected static boolean arrayEquals(byte[] a, byte[] b) { + if (a.length != b.length) { + return false; + } + byte res = 0; + for (int i = 0; i < a.length; i++) { + res |= (byte) (a[i] ^ b[i]); + } + return res == 0; + } + + /** + * Computes a MAC. + * + * @param algorithm the algorithm. + * @param key the key bytes + * @param msg the message to MAC. + * @param tagSize the expected size of the tag in bits. + * @return the tag + * @throws GeneralSecurityException if the algorithm or the parameter sizes are not supported or + * if the initialization failed. For example one case are GMACs with a tag size othe than 128 + * bits, since the JCE interface does not seem to support such a specification. + */ + protected static byte[] computeMac(String algorithm, byte[] key, byte[] msg, int tagSize) + throws GeneralSecurityException { + Mac mac = Mac.getInstance(algorithm); + algorithm = algorithm.toUpperCase(Locale.ENGLISH); + if (algorithm.startsWith("HMAC")) { + SecretKeySpec keySpec = new SecretKeySpec(key, algorithm); + // TODO(bleichen): Is there a provider independent truncation? + // The class javax.xml.crypto.dsig.spec.HMACParameterSpec would allow to + // truncate HMAC tags as follows: + // <pre> + // HMACParameterSpec params = new HMACParameterSpec(tagSize); + // mac.init(keySpec, params); + // mac.update(msg); + // return mac.doFinal(); + // </pre> + // But this class is often not supported. Hence the computation here, just computes a + // full length tag and truncates it. The drawback of having to truncate tags is that + // the caller has to compare truncated tags during verification. + mac.init(keySpec); + mac.update(msg); + byte[] tag = mac.doFinal(); + return Arrays.copyOf(tag, tagSize / 8); + } else { + throw new NoSuchAlgorithmException(algorithm); + } + } + + /** + * Tests a randomized MAC (i.e. a message authetication that takes an additional IV as parameter) + * against test vectors. + * + * @param filename the JSON file with the test vectors. + */ + public void testMac(String filename) throws Exception { + // Checking preconditions. + JsonObject test = JsonUtil.getTestVectors(filename); + String algorithm = test.get("algorithm").getAsString(); + try { + Mac.getInstance(algorithm); + } catch (NoSuchAlgorithmException ex) { + System.out.println("Algorithm is not supported. Skipping test for " + algorithm); + return; + } + + int numTests = test.get("numberOfTests").getAsInt(); + int cntTests = 0; + int passedTests = 0; + int errors = 0; + for (JsonElement g : test.getAsJsonArray("testGroups")) { + JsonObject group = g.getAsJsonObject(); + int tagSize = group.get("tagSize").getAsInt(); + for (JsonElement t : group.getAsJsonArray("tests")) { + cntTests++; + JsonObject testcase = t.getAsJsonObject(); + int tcid = testcase.get("tcId").getAsInt(); + String tc = "tcId: " + tcid + " " + testcase.get("comment").getAsString(); + byte[] key = getBytes(testcase, "key"); + byte[] msg = getBytes(testcase, "msg"); + byte[] expectedTag = getBytes(testcase, "tag"); + // Result is one of "valid", "invalid", "acceptable". + // "valid" are test vectors with matching plaintext, ciphertext and tag. + // "invalid" are test vectors with invalid parameters or invalid ciphertext and tag. + // "acceptable" are test vectors with weak parameters or legacy formats. + String result = testcase.get("result").getAsString(); + + byte[] computedTag = null; + try { + computedTag = computeMac(algorithm, key, msg, tagSize); + } catch (GeneralSecurityException ex) { + // Some libraries restrict key size or tag size. Hence valid MACs might be + // rejected. + continue; + } catch (IllegalArgumentException ex) { + // Thrown by javax.crypto.spec.SecretKeySpec (e.g. when the key is empty). + continue; + } + + boolean eq = arrayEquals(expectedTag, computedTag); + if (result.equals("invalid")) { + if (eq) { + // Some test vectors use invalid parameters that should be rejected. + // E.g. an implementation must not allow AES-GMAC with an IV of length 0, + // since this leaks the authentication key. + System.out.println("Computed mac for test case " + tc); + errors++; + } + } else { + if (eq) { + passedTests++; + } else { + System.out.println( + "Incorrect tag for " + + tc + + " expected:" + + TestUtil.bytesToHex(expectedTag) + + " computed:" + + TestUtil.bytesToHex(computedTag)); + errors++; + } + } + } + } + System.out.println("passed Tests for " + algorithm + ":" + passedTests); + assertEquals(0, errors); + assertEquals(numTests, cntTests); + } + + /** + * Returns an initialized instance of a randomized MAC. + * + * @param algorithm the algorithm. + * @param key the key bytes + * @param iv the bytes of the initialization vector + * @param tagSize the expected size of the tag in bits. + * @return an initialized instance of a MAC. + * @throws GeneralSecurityException if the algorithm or the parameter sizes are not supported or + * if the initialization failed. For example one case are GMACs with a tag size othe than 128 + * bits, since the JCE interface does not seem to support such a specification. + */ + protected static Mac getInitializedMacWithIv(String algorithm, byte[] key, byte[] iv, int tagSize) + throws GeneralSecurityException { + Mac mac = Mac.getInstance(algorithm); + algorithm = algorithm.toUpperCase(Locale.ENGLISH); + if (algorithm.equals("AES-GMAC")) { + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + if (tagSize != 128) { + throw new InvalidAlgorithmParameterException("only 128-bit tag is supported"); + } + IvParameterSpec params = new IvParameterSpec(iv); + // TODO(bleichen): I'm unaware of a method that allows to specify the tag size in JCE. + // E.g. the following parameter specification does not work (at least not in BC): + // GCMParameterSpec params = new GCMParameterSpec(tagSize, iv); + mac.init(keySpec, params); + return mac; + } else { + throw new NoSuchAlgorithmException(algorithm); + } + } + + /** + * Tests a randomized MAC (i.e. a message authetication that takes an additional IV as + * parameter) against test vectors. + * + * @param filename the JSON file with the test vectors. + * @param algorithm the JCE name of the algorithm to test. + */ + public void testMacWithIv(String filename, String algorithm) throws Exception { + // Checking preconditions. + try { + Mac.getInstance(algorithm); + } catch (NoSuchAlgorithmException ex) { + System.out.println("Algorithm is not supported. Skipping test for " + algorithm); + return; + } + + JsonObject test = JsonUtil.getTestVectors(filename); + int numTests = test.get("numberOfTests").getAsInt(); + int cntTests = 0; + int passedTests = 0; + int errors = 0; + for (JsonElement g : test.getAsJsonArray("testGroups")) { + JsonObject group = g.getAsJsonObject(); + int tagSize = group.get("tagSize").getAsInt(); + for (JsonElement t : group.getAsJsonArray("tests")) { + cntTests++; + JsonObject testcase = t.getAsJsonObject(); + int tcid = testcase.get("tcId").getAsInt(); + String tc = "tcId: " + tcid + " " + testcase.get("comment").getAsString(); + byte[] key = getBytes(testcase, "key"); + byte[] iv = getBytes(testcase, "iv"); + byte[] msg = getBytes(testcase, "msg"); + byte[] expectedTag = getBytes(testcase, "tag"); + // Result is one of "valid", "invalid", "acceptable". + // "valid" are test vectors with matching plaintext, ciphertext and tag. + // "invalid" are test vectors with invalid parameters or invalid ciphertext and tag. + // "acceptable" are test vectors with weak parameters or legacy formats. + String result = testcase.get("result").getAsString(); + + Mac mac; + try { + mac = getInitializedMacWithIv(algorithm, key, iv, tagSize); + } catch (GeneralSecurityException ex) { + // Some libraries restrict key size, iv size and tag size. + // Because of the initialization of the Mac might fail. + continue; + } catch (IllegalArgumentException ex) { + // Thrown by javax.crypto.spec.SecretKeySpec (e.g. when the key is empty). + continue; + } + + byte[] computedTag = mac.doFinal(msg); + boolean eq = arrayEquals(expectedTag, computedTag); + if (result.equals("invalid")) { + if (eq) { + // Some test vectors use invalid parameters that should be rejected. + // E.g. an implementation must not allow AES-GMAC with an IV of length 0, + // since this leaks the authentication key. + System.out.println("Computed mac for test case " + tc); + errors++; + } + } else { + if (eq) { + passedTests++; + } else { + System.out.println( + "Incorrect tag for " + + tc + + " expected:" + + TestUtil.bytesToHex(expectedTag) + + " computed:" + + TestUtil.bytesToHex(computedTag)); + errors++; + } + } + } + } + System.out.println("passed Tests for " + algorithm + ":" + passedTests); + assertEquals(0, errors); + assertEquals(numTests, cntTests); + } + + @Test + public void testHmacSha1() throws Exception { + testMac("hmac_sha1_test.json"); + } + + @Test + public void testHmacSha224() throws Exception { + testMac("hmac_sha224_test.json"); + } + + @Test + public void testHmacSha256() throws Exception { + testMac("hmac_sha256_test.json"); + } + + @Test + public void testHmacSha384() throws Exception { + testMac("hmac_sha384_test.json"); + } + + @Test + public void testHmacSha512() throws Exception { + testMac("hmac_sha512_test.json"); + } + + @Test + public void testHmacSha3_224() throws Exception { + testMac("hmac_sha3_224_test.json"); + } + + @Test + public void testHmacSha3_256() throws Exception { + testMac("hmac_sha3_256_test.json"); + } + + @Test + public void testHmacSha3_384() throws Exception { + testMac("hmac_sha3_384_test.json"); + } + + @Test + public void testHmacSha3_512() throws Exception { + testMac("hmac_sha3_512_test.json"); + } + + @Test + public void testAesGmac() throws Exception { + testMacWithIv("gmac_test.json", "AES-GMAC"); + } +} |