blob: ec66ffd0426c42a2b00ed236d47b1d412914ca55 [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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.crypto.tink.subtle;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
import com.google.crypto.tink.Mac;
import com.google.crypto.tink.config.TinkFips;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.util.Arrays;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link AesCmac}. */
@RunWith(JUnit4.class)
public class PrfAesCmacTest {
private static class MacTestVector {
String algName;
public byte[] key;
public byte[] message;
public byte[] tag;
public MacTestVector(String algName, String key, String message, String tag) {
this.algName = algName;
this.key = Hex.decode(key);
this.message = Hex.decode(message);
this.tag = Hex.decode(tag);
}
}
// Test data from https://tools.ietf.org/html/rfc4493#section-4.
private static final MacTestVector[] CMAC_TEST_VECTORS = {
new MacTestVector(
"AESCMAC", "2b7e151628aed2a6abf7158809cf4f3c", "", "bb1d6929e95937287fa37d129b756746"),
new MacTestVector(
"AESCMAC",
"2b7e151628aed2a6abf7158809cf4f3c",
"6bc1bee22e409f96e93d7e117393172a"
+ "ae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411",
"dfa66747de9ae63030ca32611497c827"),
new MacTestVector(
"AESCMAC",
"2b7e151628aed2a6abf7158809cf4f3c",
"6bc1bee22e409f96e93d7e117393172a"
+ "ae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52ef"
+ "f69f2445df4f9b17ad2b417be66c3710",
"51f0bebf7e3b9d92fc49741779363cfe"),
};
@Test
public void testFipsCompatibility() throws Exception {
Assume.assumeTrue(TinkFips.useOnlyFips());
// In FIPS-mode we expect that creating a PrfAesCmac fails.
assertThrows(
GeneralSecurityException.class,
() -> new PrfMac(new PrfAesCmac(CMAC_TEST_VECTORS[0].key), 16));
}
@Test
public void testMacTestVectors() throws Exception {
Assume.assumeFalse(TinkFips.useOnlyFips());
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(t.key), t.tag.length);
assertArrayEquals(t.tag, mac.computeMac(t.message));
try {
mac.verifyMac(t.tag, t.message);
} catch (GeneralSecurityException e) {
throw new AssertionError("Valid MAC, should not throw exception", e);
}
}
}
@Test
public void testTagTruncation() throws Exception {
Assume.assumeFalse(TinkFips.useOnlyFips());
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(t.key), t.tag.length);
for (int j = 1; j < t.tag.length; j++) {
byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length - j);
assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
}
}
// Test with random keys.
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(Random.randBytes(t.key.length)), t.tag.length);
for (int j = 1; j < t.tag.length; j++) {
byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length - j);
assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
}
}
}
@Test
public void testBitFlipMessage() throws Exception {
Assume.assumeFalse(TinkFips.useOnlyFips());
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(t.key), t.tag.length);
for (int b = 0; b < t.message.length; b++) {
for (int bit = 0; bit < 8; bit++) {
byte[] modifiedMessage = Arrays.copyOf(t.message, t.message.length);
modifiedMessage[b] = (byte) (modifiedMessage[b] ^ (1 << bit));
assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(t.tag, modifiedMessage));
}
}
}
// Test with random keys.
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(Random.randBytes(t.key.length)), t.tag.length);
for (int b = 0; b < t.message.length; b++) {
for (int bit = 0; bit < 8; bit++) {
byte[] modifiedMessage = Arrays.copyOf(t.message, t.message.length);
modifiedMessage[b] = (byte) (modifiedMessage[b] ^ (1 << bit));
assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(t.tag, modifiedMessage));
}
}
}
}
@Test
public void testBitFlipTag() throws Exception {
Assume.assumeFalse(TinkFips.useOnlyFips());
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(t.key), t.tag.length);
for (int b = 0; b < t.tag.length; b++) {
for (int bit = 0; bit < 8; bit++) {
byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length);
modifiedTag[b] = (byte) (modifiedTag[b] ^ (1 << bit));
assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
}
}
}
// Test with random keys.
for (MacTestVector t : CMAC_TEST_VECTORS) {
Mac mac = new PrfMac(new PrfAesCmac(Random.randBytes(t.key.length)), t.tag.length);
for (int b = 0; b < t.tag.length; b++) {
for (int bit = 0; bit < 8; bit++) {
byte[] modifiedTag = Arrays.copyOf(t.tag, t.tag.length);
modifiedTag[b] = (byte) (modifiedTag[b] ^ (1 << bit));
assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(modifiedTag, t.message));
}
}
}
}
@Test
public void testThrowExceptionIfTagSizeIsTooSmall() throws Exception {
Assume.assumeFalse(TinkFips.useOnlyFips());
for (int i = 0; i < PrfMac.MIN_TAG_SIZE_IN_BYTES; i++) {
final int j = i;
assertThrows(
InvalidAlgorithmParameterException.class,
() -> new PrfMac(new PrfAesCmac(Random.randBytes(16)), j));
}
}
@Test
public void testThrowExceptionIfTagSizeIsTooLarge() throws Exception {
Assume.assumeFalse(TinkFips.useOnlyFips());
assertThrows(
InvalidAlgorithmParameterException.class,
() -> new PrfMac(new PrfAesCmac(Random.randBytes(16)), 17));
}
}