blob: 6264c179445478fde5f64aec040ca7ad2dd7b134 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// 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.mac;
import static com.google.common.truth.Truth.assertThat;
import static com.google.crypto.tink.internal.testing.Asserts.assertEqualWhenValueParsed;
import static org.junit.Assert.assertThrows;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.Key;
import com.google.crypto.tink.Parameters;
import com.google.crypto.tink.internal.MutableSerializationRegistry;
import com.google.crypto.tink.internal.ProtoKeySerialization;
import com.google.crypto.tink.internal.ProtoParametersSerialization;
import com.google.crypto.tink.internal.testing.KeyWithSerialization;
import com.google.crypto.tink.internal.testing.ParametersWithSerialization;
import com.google.crypto.tink.proto.AesCmacParams;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.proto.KeyTemplate;
import com.google.crypto.tink.proto.OutputPrefixType;
import com.google.crypto.tink.util.SecretBytes;
import com.google.protobuf.ByteString;
import java.security.GeneralSecurityException;
import javax.annotation.Nullable;
import org.junit.BeforeClass;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.FromDataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
/** Test for AesCmacProtoSerialization. */
@RunWith(Theories.class)
@SuppressWarnings("UnnecessarilyFullyQualified") // Fully specifying proto types is more readable
public final class AesCmacProtoSerializationTest {
private static final String TYPE_URL = "type.googleapis.com/google.crypto.tink.AesCmacKey";
private static final SecretBytes AES_KEY = SecretBytes.randomBytes(32);
private static final ByteString AES_KEY_AS_BYTE_STRING =
ByteString.copyFrom(AES_KEY.toByteArray(InsecureSecretKeyAccess.get()));
private static final MutableSerializationRegistry registry = new MutableSerializationRegistry();
@BeforeClass
public static void setUp() throws Exception {
AesCmacProtoSerialization.register(registry);
}
static AesCmacParameters createAesCmacParameters(int tagSize, AesCmacParameters.Variant variant) {
try {
return AesCmacParameters.createForKeysetWithCryptographicTagSize(tagSize, variant);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
static AesCmacKey createKey(
int tagSize,
AesCmacParameters.Variant variant,
SecretBytes aesKey,
@Nullable Integer idRequirement)
throws GeneralSecurityException {
return AesCmacKey.createForKeyset(
createAesCmacParameters(tagSize, variant), aesKey, idRequirement);
}
static com.google.crypto.tink.proto.AesCmacKeyFormat createProtoFormat(int tagSize) {
return com.google.crypto.tink.proto.AesCmacKeyFormat.newBuilder()
.setKeySize(32)
.setParams(AesCmacParams.newBuilder().setTagSize(tagSize))
.build();
}
static com.google.crypto.tink.proto.AesCmacKey createProtoKey(int tagSize, ByteString aesKey) {
return com.google.crypto.tink.proto.AesCmacKey.newBuilder()
.setVersion(0)
.setKeyValue(aesKey)
.setParams(AesCmacParams.newBuilder().setTagSize(tagSize))
.build();
}
@DataPoints("validParameters")
public static final ParametersWithSerialization[] VALID_PARAMETERS =
new ParametersWithSerialization[] {
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 16, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(16))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 16, AesCmacParameters.Variant.CRUNCHY),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.CRUNCHY, createProtoFormat(16))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 16, AesCmacParameters.Variant.LEGACY),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.LEGACY, createProtoFormat(16))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 16, AesCmacParameters.Variant.NO_PREFIX),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.RAW, createProtoFormat(16))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 10, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(10))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 11, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(11))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 12, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(12))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 13, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(13))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 14, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(14))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 15, AesCmacParameters.Variant.TINK),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.TINK, createProtoFormat(15))),
new ParametersWithSerialization(
createAesCmacParameters(/*tagSize=*/ 11, AesCmacParameters.Variant.NO_PREFIX),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.RAW, createProtoFormat(11))),
};
@DataPoints("invalidParameters")
public static final ProtoParametersSerialization[] INVALID_PARAMETERS =
new ProtoParametersSerialization[] {
ProtoParametersSerialization.create(TYPE_URL, OutputPrefixType.RAW, createProtoFormat(9)),
ProtoParametersSerialization.create(TYPE_URL, OutputPrefixType.RAW, createProtoFormat(7)),
ProtoParametersSerialization.create(TYPE_URL, OutputPrefixType.RAW, createProtoFormat(17)),
ProtoParametersSerialization.create(TYPE_URL, OutputPrefixType.RAW, createProtoFormat(19)),
ProtoParametersSerialization.create(TYPE_URL, OutputPrefixType.RAW, createProtoFormat(32)),
ProtoParametersSerialization.create(
TYPE_URL, OutputPrefixType.UNKNOWN_PREFIX, createProtoFormat(16)),
// Proto messages start with a VarInt, which always ends with a byte with most
// significant bit unset. 0x80 is hence invalid.
ProtoParametersSerialization.create(
KeyTemplate.newBuilder()
.setTypeUrl(TYPE_URL)
.setOutputPrefixType(OutputPrefixType.RAW)
.setValue(ByteString.copyFrom(new byte[] {(byte) 0x80}))
.build()),
};
@Theory
public void testSerializeParameters(
@FromDataPoints("validParameters") ParametersWithSerialization pair) throws Exception {
ProtoParametersSerialization serializedParameters =
registry.serializeParameters(pair.getParameters(), ProtoParametersSerialization.class);
assertEqualWhenValueParsed(
com.google.crypto.tink.proto.AesCmacKeyFormat.parser(),
serializedParameters,
pair.getSerializedParameters());
}
@Theory
public void testParseValidParameters(
@FromDataPoints("validParameters") ParametersWithSerialization pair) throws Exception {
Parameters parsed = registry.parseParameters(pair.getSerializedParameters());
assertThat(parsed).isEqualTo(pair.getParameters());
}
@Theory
public void testParseInvalidParameters_fails(
@FromDataPoints("invalidParameters") ProtoParametersSerialization serializedParameters)
throws Exception {
assertThrows(
GeneralSecurityException.class, () -> registry.parseParameters(serializedParameters));
}
private static KeyWithSerialization[] createValidKeys() {
try {
return new KeyWithSerialization[] {
new KeyWithSerialization(
createKey(
/*tagSize=*/ 16, AesCmacParameters.Variant.TINK, AES_KEY, /*idRequirement=*/ 1479),
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(/*tagSize=*/ 16, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
/*idRequirement=*/ 1479)),
new KeyWithSerialization(
createKey(16, AesCmacParameters.Variant.CRUNCHY, AES_KEY, 1479),
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(/*tagSize=*/ 16, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.CRUNCHY,
/*idRequirement=*/ 1479)),
new KeyWithSerialization(
createKey(16, AesCmacParameters.Variant.LEGACY, AES_KEY, 1479),
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(/*tagSize=*/ 16, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.LEGACY,
1479)),
new KeyWithSerialization(
createKey(16, AesCmacParameters.Variant.NO_PREFIX, AES_KEY, null),
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(/*tagSize=*/ 16, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.RAW,
/*idRequirement=*/ null)),
};
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private static ProtoKeySerialization[] createInvalidKeys() {
try {
return new ProtoKeySerialization[] {
// Bad Version Number (1)
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(16, AES_KEY_AS_BYTE_STRING).toBuilder()
.setVersion(1)
.build()
.toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Unknown prefix
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(16, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.UNKNOWN_PREFIX,
1479),
// Bad Tag Length (9)
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(9, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Bad Tag Length (17)
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(17, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Bad Key Length (16)
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(16, ByteString.copyFrom(new byte[16])).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Bad Key Length (31)
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(16, ByteString.copyFrom(new byte[31])).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Bad Key Length (64)
ProtoKeySerialization.create(
TYPE_URL,
createProtoKey(16, ByteString.copyFrom(new byte[64])).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Invalid proto encoding
ProtoKeySerialization.create(
TYPE_URL,
// Proto messages start with a VarInt, which always ends with a byte with most
// significant bit unset. 0x80 is hence invalid.
ByteString.copyFrom(new byte[] {(byte) 0x80}),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
// Wrong Type URL -- not sure if this should be tested; this won't even get to the code
// under test.
ProtoKeySerialization.create(
"WrongTypeUrl",
createProtoKey(16, AES_KEY_AS_BYTE_STRING).toByteString(),
KeyMaterialType.SYMMETRIC,
OutputPrefixType.TINK,
1479),
};
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@DataPoints("validKeys")
public static final KeyWithSerialization[] VALID_KEYS = createValidKeys();
@DataPoints("invalidKeys")
public static final ProtoKeySerialization[] INVALID_KEYS = createInvalidKeys();
@Theory
public void testSerializeKeys(@FromDataPoints("validKeys") KeyWithSerialization pair)
throws Exception {
ProtoKeySerialization tinkFormatSerialized =
registry.serializeKey(
pair.getKey(), ProtoKeySerialization.class, InsecureSecretKeyAccess.get());
assertEqualWhenValueParsed(
com.google.crypto.tink.proto.AesCmacKey.parser(),
tinkFormatSerialized,
pair.getSerialization());
}
@Theory
public void testParseKeys(@FromDataPoints("validKeys") KeyWithSerialization pair)
throws Exception {
Key parsed = registry.parseKey(pair.getSerialization(), InsecureSecretKeyAccess.get());
assertThat(parsed.equalsKey(pair.getKey())).isTrue();
}
@Theory
public void testSerializeKeys_noAccess_throws(
@FromDataPoints("validKeys") KeyWithSerialization pair) throws Exception {
assertThrows(
GeneralSecurityException.class,
() -> registry.serializeKey(pair.getKey(), ProtoKeySerialization.class, null));
}
@Theory
public void testParseKeys_noAccess_throws(@FromDataPoints("validKeys") KeyWithSerialization pair)
throws Exception {
assertThrows(
GeneralSecurityException.class, () -> registry.parseKey(pair.getSerialization(), null));
}
@Theory
public void testParseInvalidKeys_throws(
@FromDataPoints("invalidKeys") ProtoKeySerialization serialization) throws Exception {
assertThrows(
GeneralSecurityException.class,
() -> registry.parseKey(serialization, InsecureSecretKeyAccess.get()));
}
}