Adding test vectors for AES-GMAC to Wycheproof.
AES-GMAC is equivalent to encrypting with AES-GCM an empty plaintext, using passing the data to authenticate as additional data. AES-GMAC is supported by keymaster and BouncyCastle. keymaster has fixed nonce sizes and tag sizes (128-bits). BouncyCastle has a fixed tag size (128-bit) when used through the JCE interface. NOKEYCHECK=True PiperOrigin-RevId: 257385648 GitOrigin-RevId: d6947fd3604ca782078b7a02bab2a95160df2501
This commit is contained in:
@@ -39,6 +39,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 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.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getInitializedMac(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 = getInitializedMac(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 testAesGmac() throws Exception {
|
||||
testMacWithIv("gmac_test.json", "AES-GMAC");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user