Adding a test for HMACs to Wycheproof.
The main focus are:
* incremental updates
* long inputs
* using ByteBuffers
Tested providers: jdk8, jdk11, BouncyCastle, Conscrypt, hazmat, pythons implementation of
hmac.
Resuts:
- all implementations are compatible.
- all implementations support HMAC with SHA-1 and SHA-2.
- BouncyCastle and python 3.7 support SHA-3
- jdk8 doesn't support SHA-3 and hence not HMAC with SHA-3
- jdk11 implements SHA-3, but not HMAC with SHA-3
- hazmat would support HMAC with SHA-3 if the backend were supporting the hashes,
but the defaultbackend does not.
NOKEYCHECK=True
PiperOrigin-RevId: 285161934
GitOrigin-RevId: 604eac41416d9dffc916c474a19ec7690401b22c
This commit is contained in:
@@ -42,6 +42,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
JsonXdhTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
JsonXdhTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -36,7 +36,9 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -37,7 +37,10 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonAeadTest.class,
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -19,10 +19,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
/**
|
||||
* Tests for OpenJDK's providers: SunJCE, SunEC, etc.
|
||||
* OpenJDKAllTests runs all tests.
|
||||
*/
|
||||
/** Tests for OpenJDK's providers: SunJCE, SunEC, etc. OpenJDKAllTests runs all tests. */
|
||||
@RunWith(WycheproofRunner.class)
|
||||
@SuiteClasses({
|
||||
AesGcmTest.class,
|
||||
@@ -38,8 +35,10 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
JsonXdhTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -18,10 +18,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
/**
|
||||
* Tests for OpenJDK's providers: SunJCE, SunEC, etc.
|
||||
* OpenJDKTest excludes slow tests.
|
||||
*/
|
||||
/** Tests for OpenJDK's providers: SunJCE, SunEC, etc. OpenJDKTest excludes slow tests. */
|
||||
@RunWith(WycheproofRunner.class)
|
||||
@SuiteClasses({
|
||||
AesGcmTest.class,
|
||||
@@ -37,8 +34,10 @@ import org.junit.runners.Suite.SuiteClasses;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
JsonXdhTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -39,7 +39,10 @@ import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
RsaOaepTest.class,
|
||||
|
||||
@@ -43,7 +43,9 @@ import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
JsonCipherTest.class,
|
||||
JsonEcdhTest.class,
|
||||
JsonKeyWrapTest.class,
|
||||
JsonMacTest.class,
|
||||
JsonSignatureTest.class,
|
||||
MacTest.class,
|
||||
MessageDigestTest.class,
|
||||
RsaEncryptionTest.class,
|
||||
RsaKeyTest.class,
|
||||
|
||||
@@ -263,7 +263,7 @@ public class EcdsaTest {
|
||||
if (bias2 > threshold) {
|
||||
fail("Bias for k detected. bias2 = " + bias2);
|
||||
}
|
||||
double bias3 = bias(kList, q, qHalf);
|
||||
double bias3 = bias(kList, q, qHalf);
|
||||
if (bias3 > threshold) {
|
||||
fail("Bias for k detected. bias3 = " + bias3);
|
||||
}
|
||||
@@ -271,7 +271,9 @@ public class EcdsaTest {
|
||||
for (int bits : new int[] {8, 16, 32, 64}) {
|
||||
BigInteger multiplier = BigInteger.ONE.shiftLeft(bits).subtract(BigInteger.ONE);
|
||||
double bias4 = bias(kList, q, multiplier);
|
||||
fail("Bias for k detected. bits = " + bits + " bias4 = " + bias4);
|
||||
if (bias4 > threshold) {
|
||||
fail("Bias for k detected. bits = " + bits + " bias4 = " + bias4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ 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;
|
||||
@@ -46,6 +47,125 @@ public class JsonMacTest {
|
||||
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.
|
||||
*
|
||||
@@ -58,7 +178,7 @@ public class JsonMacTest {
|
||||
* 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)
|
||||
protected static Mac getInitializedMacWithIv(String algorithm, byte[] key, byte[] iv, int tagSize)
|
||||
throws GeneralSecurityException {
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
algorithm = algorithm.toUpperCase(Locale.ENGLISH);
|
||||
@@ -119,7 +239,7 @@ public class JsonMacTest {
|
||||
|
||||
Mac mac;
|
||||
try {
|
||||
mac = getInitializedMac(algorithm, key, iv, tagSize);
|
||||
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.
|
||||
@@ -160,6 +280,51 @@ public class JsonMacTest {
|
||||
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");
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
* 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 java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.security.wycheproof.WycheproofRunner.ProviderType;
|
||||
import com.google.security.wycheproof.WycheproofRunner.SlowTest;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/**
|
||||
* Tests for MACs.
|
||||
*
|
||||
* <p>TODO(bleichen): The tests are quite incomplete. Some of the missing stuff: More test vectors
|
||||
* with known results are necessary. So far only simple test vectors for long messages are
|
||||
* available.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class MacTest {
|
||||
|
||||
/**
|
||||
* Computes the maximum of an array with at least one element.
|
||||
*
|
||||
* @param values the values from which the max is computed.
|
||||
* @return the maximum
|
||||
* @throws IllegalArgumentException if values is empty of null.
|
||||
*/
|
||||
private static int max(int[] values) {
|
||||
if (values == null || values.length == 0) {
|
||||
throw new IllegalArgumentException("Expecting an array with at least one element");
|
||||
}
|
||||
int result = Integer.MIN_VALUE;
|
||||
for (int value : values) {
|
||||
result = Math.max(result, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests computing a MAC by computing it multiple times. The test passes all the results are the
|
||||
* same in all cases.
|
||||
*
|
||||
* @param algorithm the name of the MAC (e.g. "HMACSHA1")
|
||||
* @param key the key of the MAC
|
||||
* @param data input data for the MAC. The size of the data must be at least as long as the sum of
|
||||
* all chunkSizes.
|
||||
* @param chunkSizes the sizes of the chunks used in the calls of update
|
||||
*/
|
||||
private void testUpdateWithChunks(String algorithm, Key key, byte[] data, int... chunkSizes)
|
||||
throws Exception {
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
|
||||
// First evaluation: compute MAC in one piece.
|
||||
int totalLength = 0;
|
||||
for (int chunkSize : chunkSizes) {
|
||||
totalLength += chunkSize;
|
||||
}
|
||||
mac.init(key);
|
||||
mac.update(data, 0, totalLength);
|
||||
byte[] mac1 = mac.doFinal();
|
||||
|
||||
// Second evaluation: using multiple chunks
|
||||
mac.init(key);
|
||||
int start = 0;
|
||||
for (int chunkSize : chunkSizes) {
|
||||
mac.update(data, start, chunkSize);
|
||||
start += chunkSize;
|
||||
}
|
||||
byte[] mac2 = mac.doFinal();
|
||||
if (!arrayEquals(mac1, mac2)) {
|
||||
fail(
|
||||
"Different MACs for same input:"
|
||||
+ " computed as one piece:"
|
||||
+ TestUtil.bytesToHex(mac1)
|
||||
+ " computed with multiple array segments:"
|
||||
+ TestUtil.bytesToHex(mac2));
|
||||
}
|
||||
// Third evaluation: using ByteBuffers
|
||||
mac.init(key);
|
||||
start = 0;
|
||||
for (int chunkSize : chunkSizes) {
|
||||
ByteBuffer chunk = ByteBuffer.wrap(data, start, chunkSize);
|
||||
mac.update(chunk);
|
||||
start += chunkSize;
|
||||
}
|
||||
byte[] mac3 = mac.doFinal();
|
||||
if (!arrayEquals(mac1, mac3)) {
|
||||
fail(
|
||||
"Different MACs for same input:"
|
||||
+ " computed as one piece:"
|
||||
+ TestUtil.bytesToHex(mac1)
|
||||
+ " computed with wrapped chunks:"
|
||||
+ TestUtil.bytesToHex(mac3));
|
||||
}
|
||||
// Forth evaluation: using ByteBuffer slices.
|
||||
// The effect of using slice() is that the resulting ByteBuffer has
|
||||
// position 0, but possibly an non-zero value for arrayOffset().
|
||||
mac.init(key);
|
||||
start = 0;
|
||||
for (int chunkSize : chunkSizes) {
|
||||
ByteBuffer chunk = ByteBuffer.wrap(data, start, chunkSize).slice();
|
||||
mac.update(chunk);
|
||||
start += chunkSize;
|
||||
}
|
||||
byte[] mac4 = mac.doFinal();
|
||||
if (!arrayEquals(mac1, mac4)) {
|
||||
fail(
|
||||
"Different MACs for same input:"
|
||||
+ " computed as one piece:"
|
||||
+ TestUtil.bytesToHex(mac1)
|
||||
+ " computed with ByteBuffer slices:"
|
||||
+ TestUtil.bytesToHex(mac4));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The paper "Finding Bugs in Cryptographic Hash Function Implementations" by Mouha, Raunak, Kuhn,
|
||||
* and Kacker, https://eprint.iacr.org/2017/891.pdf contains an analysis of implementations
|
||||
* submitted to the SHA-3 competition. Many of the implementations contain bugs. The authors
|
||||
* propose some tests for cryptographic libraries. The test here implements a check for
|
||||
* incremental updates with the values proposed in Table 3.
|
||||
*/
|
||||
private void testUpdate(String algorithm, Key key) throws Exception {
|
||||
int[] chunkSize1 = {0, 8, 16, 24, 32, 40, 48, 56, 64};
|
||||
int[] chunkSize2 = {0, 8, 16, 24, 32, 40, 48, 56, 64};
|
||||
int[] chunkSize3 = {0, 8, 16, 32, 64, 128, 256, 512, 1024, 2048};
|
||||
int[] chunkSize4 = {
|
||||
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,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 127, 128, 129, 255, 256,
|
||||
257, 511, 512, 513
|
||||
};
|
||||
int maxSize = max(chunkSize1) + max(chunkSize2) + max(chunkSize3) + max(chunkSize4);
|
||||
byte[] data = new byte[maxSize];
|
||||
SecureRandom rand = new SecureRandom();
|
||||
rand.nextBytes(data);
|
||||
for (int size1 : chunkSize1) {
|
||||
for (int size2 : chunkSize2) {
|
||||
for (int size3 : chunkSize3) {
|
||||
for (int size4 : chunkSize4) {
|
||||
testUpdateWithChunks(algorithm, key, data, size1, size2, size3, size4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testMac(String algorithm, int keySize) throws Exception {
|
||||
try {
|
||||
Mac.getInstance(algorithm);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
System.out.println("Algorithm " + algorithm + " is not supported. Skipping test.");
|
||||
return;
|
||||
}
|
||||
byte[] key = new byte[keySize];
|
||||
SecureRandom rand = new SecureRandom();
|
||||
rand.nextBytes(key);
|
||||
testUpdate(algorithm, new SecretKeySpec(key, algorithm));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha1() throws Exception {
|
||||
testMac("HMACSHA1", 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha224() throws Exception {
|
||||
testMac("HMACSHA224", 28);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha256() throws Exception {
|
||||
testMac("HMACSHA256", 32);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha384() throws Exception {
|
||||
testMac("HMACSHA384", 48);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha512() throws Exception {
|
||||
testMac("HMACSHA512", 64);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha3_224() throws Exception {
|
||||
testMac("HMACSHA3-224", 28);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha3_256() throws Exception {
|
||||
testMac("HMACSHA3-256", 32);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha3_384() throws Exception {
|
||||
testMac("HMACSHA3-384", 48);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHmacSha3_512() throws Exception {
|
||||
testMac("HMACSHA3-512", 64);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the mac of a message repeated multiple times.
|
||||
*
|
||||
* @param algorithm the message digest (e.g. "HMACSHA1")
|
||||
* @param message the bytes to mac
|
||||
* @param repetitions the number of repetitions of the message
|
||||
* @return the digest
|
||||
* @throws GeneralSecurityException if the computation of the mac fails (e.g. because the
|
||||
* algorithm is unknown).
|
||||
*/
|
||||
public byte[] macRepeatedMessage(String algorithm, Key key, byte[] message, long repetitions)
|
||||
throws Exception {
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
mac.init(key);
|
||||
// If the message is short then it is more efficient to collect multiple copies
|
||||
// of the message in one chunk and call update with the larger chunk.
|
||||
final int maxChunkSize = 1 << 16;
|
||||
if (message.length != 0 && 2 * message.length < maxChunkSize) {
|
||||
int repetitionsPerChunk = maxChunkSize / message.length;
|
||||
byte[] chunk = new byte[message.length * repetitionsPerChunk];
|
||||
for (int i = 0; i < repetitionsPerChunk; i++) {
|
||||
System.arraycopy(message, 0, chunk, i * message.length, message.length);
|
||||
}
|
||||
while (repetitions >= repetitionsPerChunk) {
|
||||
mac.update(chunk);
|
||||
repetitions -= repetitionsPerChunk;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < repetitions; i++) {
|
||||
mac.update(message);
|
||||
}
|
||||
return mac.doFinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* A test for hashing long messages.
|
||||
*
|
||||
* <p>Java does not allow strings or arrays of size 2^31 or longer. However, it is still possible
|
||||
* to compute a MAC of a long message by repeatedly calling Mac.update(). To compute correct MACs
|
||||
* the total message length must be known. This length can be bigger than 2^32 bytes.
|
||||
*
|
||||
* <p>Reference: http://www-01.ibm.com/support/docview.wss?uid=swg1PK62549 IBMJCE SHA-1
|
||||
* IMPLEMENTATION RETURNS INCORRECT HASH FOR LARGE SETS OF DATA
|
||||
*/
|
||||
private void testLongMac(
|
||||
String algorithm, String keyhex, String message, long repetitions, String expected)
|
||||
throws Exception {
|
||||
|
||||
Key key = new SecretKeySpec(TestUtil.hexToBytes(keyhex), algorithm);
|
||||
byte[] bytes = message.getBytes(UTF_8);
|
||||
byte[] mac = null;
|
||||
try {
|
||||
mac = macRepeatedMessage(algorithm, key, bytes, repetitions);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
System.out.println("Algorithm " + algorithm + " is not supported. Skipping test.");
|
||||
return;
|
||||
}
|
||||
String hexmac = TestUtil.bytesToHex(mac);
|
||||
assertEquals(expected, hexmac);
|
||||
}
|
||||
|
||||
@SlowTest(
|
||||
providers = {
|
||||
ProviderType.OPENJDK,
|
||||
ProviderType.BOUNCY_CASTLE,
|
||||
ProviderType.SPONGY_CASTLE,
|
||||
ProviderType.CONSCRYPT
|
||||
})
|
||||
@Test
|
||||
public void testLongMacSha1() throws Exception {
|
||||
testLongMac(
|
||||
"HMACSHA1",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
|
||||
"a",
|
||||
2147483647L,
|
||||
"703925f6dceb9c602969ad39bba9b1eb49472071");
|
||||
testLongMac(
|
||||
"HMACSHA1",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
|
||||
"a",
|
||||
5000000000L,
|
||||
"d7f4c387f2237ea119fcc27cd7520fc5132b6230");
|
||||
}
|
||||
|
||||
@SlowTest(
|
||||
providers = {
|
||||
ProviderType.OPENJDK,
|
||||
ProviderType.BOUNCY_CASTLE,
|
||||
ProviderType.SPONGY_CASTLE,
|
||||
ProviderType.CONSCRYPT
|
||||
})
|
||||
@Test
|
||||
public void testLongMacSha256() throws Exception {
|
||||
testLongMac(
|
||||
"HMACSHA256",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
|
||||
"a",
|
||||
2147483647L,
|
||||
"84f213c9bb5b329d547bc31dabed41939754b1af7482365ec74380c45f6ea0a7");
|
||||
testLongMac(
|
||||
"HMACSHA256",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
|
||||
"a",
|
||||
5000000000L,
|
||||
"59a75754df7093fa4339aa618b64b104f153a5b42cc85394fdb8735b13ea684a");
|
||||
}
|
||||
|
||||
@SlowTest(
|
||||
providers = {
|
||||
ProviderType.OPENJDK,
|
||||
ProviderType.BOUNCY_CASTLE,
|
||||
ProviderType.SPONGY_CASTLE,
|
||||
ProviderType.CONSCRYPT
|
||||
})
|
||||
@Test
|
||||
public void testLongMacSha384() throws Exception {
|
||||
testLongMac(
|
||||
"HMACSHA384",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
|
||||
+ "202122232425262728292a2b2c2d2e2f",
|
||||
"a",
|
||||
2147483647L,
|
||||
"aea987905f64791691b3fdea06f8e4125f396ebb73f37894e961b1a7522a55da"
|
||||
+ "ecd856a70c92c6646e6f8c3fcb935528");
|
||||
testLongMac(
|
||||
"HMACSHA384",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
|
||||
+ "202122232425262728292a2b2c2d2e2f",
|
||||
"a",
|
||||
5000000000L,
|
||||
"88485c9c5714d43a99dacbc861988c7ea39c02d82104bf93e55ec1b8a24fe15a"
|
||||
+ "a477e6a84d159d8b7a3daaa89c4f2372");
|
||||
}
|
||||
|
||||
@SlowTest(
|
||||
providers = {
|
||||
ProviderType.OPENJDK,
|
||||
ProviderType.BOUNCY_CASTLE,
|
||||
ProviderType.SPONGY_CASTLE,
|
||||
ProviderType.CONSCRYPT
|
||||
})
|
||||
@Test
|
||||
public void testLongMacSha512() throws Exception {
|
||||
testLongMac(
|
||||
"HMACSHA512",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
|
||||
+ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"a",
|
||||
2147483647L,
|
||||
"fc68fbc294951c691e5bc085c3af026099f39a57230b242aaf1fc5ca691e05da"
|
||||
+ "d1a5de7d4f30e1c958c6a2cee6159218dab683187e6d56bab824a3adefde9102");
|
||||
testLongMac(
|
||||
"HMACSHA512",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
|
||||
+ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||
"a",
|
||||
5000000000L,
|
||||
"31b1d721b958203bff7d7ddf50d48b17fc760a80a99a7f23ec966ce3bbefff29"
|
||||
+ "0d176eebbb6a440960024be0726c94960bbf75816548a7fd4552c7baba4585ee");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user