blob: 3b65a3d5f52479786db4c59d33b26c6bd6aca28d [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;
import static com.google.common.truth.Truth.assertThat;
import static com.google.crypto.tink.testing.TestUtil.assertExceptionContains;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.crypto.tink.aead.AeadConfig;
import com.google.crypto.tink.aead.AesEaxKeyManager;
import com.google.crypto.tink.aead.AesGcmKeyManager;
import com.google.crypto.tink.config.TinkConfig;
import com.google.crypto.tink.config.TinkFips;
import com.google.crypto.tink.config.internal.TinkFipsUtil;
import com.google.crypto.tink.internal.KeyTypeManager;
import com.google.crypto.tink.internal.PrimitiveFactory;
import com.google.crypto.tink.internal.PrivateKeyTypeManager;
import com.google.crypto.tink.mac.MacConfig;
import com.google.crypto.tink.mac.MacKeyTemplates;
import com.google.crypto.tink.proto.AesEaxKey;
import com.google.crypto.tink.proto.AesGcmKey;
import com.google.crypto.tink.proto.AesGcmKeyFormat;
import com.google.crypto.tink.proto.Ed25519KeyFormat;
import com.google.crypto.tink.proto.Ed25519PrivateKey;
import com.google.crypto.tink.proto.Ed25519PublicKey;
import com.google.crypto.tink.proto.HashType;
import com.google.crypto.tink.proto.HmacKey;
import com.google.crypto.tink.proto.KeyData;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.proto.KeyStatusType;
import com.google.crypto.tink.proto.Keyset;
import com.google.crypto.tink.proto.OutputPrefixType;
import com.google.crypto.tink.signature.EcdsaSignKeyManager;
import com.google.crypto.tink.signature.SignatureKeyTemplates;
import com.google.crypto.tink.subtle.AesEaxJce;
import com.google.crypto.tink.subtle.AesGcmJce;
import com.google.crypto.tink.subtle.PrfMac;
import com.google.crypto.tink.subtle.Random;
import com.google.crypto.tink.testing.TestUtil;
import com.google.crypto.tink.testing.TestUtil.DummyAead;
import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for Registry. */
@RunWith(JUnit4.class)
public class RegistryTest {
private static class CustomAeadKeyManager implements KeyManager<Aead> {
public CustomAeadKeyManager(String typeUrl) {
this.typeUrl = typeUrl;
}
private final String typeUrl;
@Override
public Aead getPrimitive(ByteString proto) throws GeneralSecurityException {
return new DummyAead();
}
@Override
public Aead getPrimitive(MessageLite proto) throws GeneralSecurityException {
return new DummyAead();
}
@Override
public MessageLite newKey(ByteString template) throws GeneralSecurityException {
throw new GeneralSecurityException("Not Implemented");
}
@Override
public MessageLite newKey(MessageLite template) throws GeneralSecurityException {
throw new GeneralSecurityException("Not Implemented");
}
@Override
public KeyData newKeyData(ByteString serializedKeyFormat) throws GeneralSecurityException {
return KeyData.newBuilder()
.setTypeUrl(typeUrl)
.setValue(serializedKeyFormat)
.build();
}
@Override
public boolean doesSupport(String typeUrl) {
return typeUrl.equals(this.typeUrl);
}
@Override
public String getKeyType() {
return this.typeUrl;
}
@Override
public int getVersion() {
return 0;
}
@Override
public Class<Aead> getPrimitiveClass() {
return Aead.class;
}
}
private static interface EncryptOnly {
byte[] encrypt(final byte[] plaintext) throws GeneralSecurityException;
}
private static class AeadToEncryptOnlyWrapper implements PrimitiveWrapper<Aead, EncryptOnly> {
@Override
public EncryptOnly wrap(PrimitiveSet<Aead> set) throws GeneralSecurityException {
return new EncryptOnly() {
@Override
public byte[] encrypt(final byte[] plaintext)
throws GeneralSecurityException {
return set.getPrimary().getPrimitive().encrypt(plaintext, new byte[0]);
}
};
}
@Override
public Class<EncryptOnly> getPrimitiveClass() {
return EncryptOnly.class;
}
@Override
public Class<Aead> getInputPrimitiveClass() {
return Aead.class;
}
}
@Before
public void setUp() throws GeneralSecurityException {
// All tests for the registry assume that if the tests are run in FIPS, that BoringSSL is
// built in FIPS mode. If BoringSSL is not built in FIPS mode, there aren't any key managers
// available which could be registered, therefore the tests would just fail.
Assume.assumeFalse(TinkFips.useOnlyFips() && !TinkFipsUtil.fipsModuleAvailable());
TinkFipsUtil.unsetFipsRestricted();
Registry.reset();
TinkConfig.register();
Registry.registerPrimitiveWrapper(new AeadToEncryptOnlyWrapper());
}
private void testGetKeyManagerShouldWork(String typeUrl, String className) throws Exception {
assertThat(Registry.getKeyManager(typeUrl).getClass().toString()).contains(className);
}
@Test
public void testGetKeyManager_legacy_shouldWork() throws Exception {
// Skip test if in FIPS mode, as EAX is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
testGetKeyManagerShouldWork(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL, "KeyManagerImpl");
testGetKeyManagerShouldWork(AeadConfig.AES_EAX_TYPE_URL, "KeyManagerImpl");
testGetKeyManagerShouldWork(MacConfig.HMAC_TYPE_URL, "KeyManagerImpl");
}
@Test
public void testGetKeyManager_shouldWorkAesEax() throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
assertThat(
Registry.getKeyManager(AeadConfig.AES_EAX_TYPE_URL, Aead.class).getClass().toString())
.contains("KeyManagerImpl");
}
@Test
public void testGetKeyManager_shouldWorkHmac() throws Exception {
assertThat(Registry.getKeyManager(MacConfig.HMAC_TYPE_URL, Mac.class).getClass().toString())
.contains("KeyManagerImpl");
}
@Test
public void testGetKeyManager_legacy_wrongType_shouldThrowException() throws Exception {
// Skip test if in FIPS mode, as no provider available to instantiate.
Assume.assumeFalse(TinkFips.useOnlyFips());
KeyManager<Aead> wrongType = Registry.getKeyManager(MacConfig.HMAC_TYPE_URL);
HmacKey hmacKey = (HmacKey) Registry.newKey(MacKeyTemplates.HMAC_SHA256_128BITTAG);
ClassCastException e =
assertThrows(
ClassCastException.class,
() -> {
Aead unused = wrongType.getPrimitive(hmacKey);
});
assertExceptionContains(e, "com.google.crypto.tink.Aead");
assertExceptionContains(e, "com.google.crypto.tink.subtle.PrfMac");
}
@Test
public void testGetKeyManager_wrongType_shouldThrowException() throws Exception {
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() -> Registry.getKeyManager(MacConfig.HMAC_TYPE_URL, Aead.class));
assertExceptionContains(e, "com.google.crypto.tink.Mac");
assertExceptionContains(e, "com.google.crypto.tink.Aead not supported");
}
@Test
public void testGetKeyManager_legacy_badTypeUrl_shouldThrowException() throws Exception {
String badTypeUrl = "bad type URL";
GeneralSecurityException e =
assertThrows(GeneralSecurityException.class, () -> Registry.getKeyManager(badTypeUrl));
assertExceptionContains(e, "No key manager found");
assertExceptionContains(e, badTypeUrl);
}
@Test
public void testGetKeyManager_badTypeUrl_shouldThrowException() throws Exception {
String badTypeUrl = "bad type URL";
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class, () -> Registry.getKeyManager(badTypeUrl, Aead.class));
assertExceptionContains(e, "No key manager found");
assertExceptionContains(e, badTypeUrl);
}
@Test
public void testGetUntypedKeyManager_shouldWorkHmac() throws Exception {
assertThat(Registry.getUntypedKeyManager(MacConfig.HMAC_TYPE_URL).getClass().toString())
.contains("KeyManagerImpl");
}
@Test
public void testRegisterKeyManager_keyManagerIsNull_shouldThrowException() throws Exception {
IllegalArgumentException e =
assertThrows(IllegalArgumentException.class, () -> Registry.registerKeyManager(null));
assertThat(e.toString()).contains("must be non-null");
}
@Test
public void testRegisterKeyManager_moreRestrictedNewKeyAllowed_shouldWork() throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "someTypeUrl";
Registry.registerKeyManager(new CustomAeadKeyManager(typeUrl));
Registry.registerKeyManager(new CustomAeadKeyManager(typeUrl), false);
}
@Test
public void testRegisterKeyManager_sameNewKeyAllowed_shouldWork() throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "someOtherTypeUrl";
Registry.registerKeyManager(new CustomAeadKeyManager(typeUrl));
Registry.registerKeyManager(new CustomAeadKeyManager(typeUrl), true);
}
@Test
public void testRegisterKeyManager_lessRestrictedNewKeyAllowed_shouldThrowException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "yetAnotherTypeUrl";
Registry.registerKeyManager(new CustomAeadKeyManager(typeUrl), false);
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(new CustomAeadKeyManager(typeUrl), true));
}
@Test
public void testRegisterKeyManager_keyManagerFromAnotherClass_shouldThrowException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
// This should not overwrite the existing manager.
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerKeyManager(
new CustomAeadKeyManager(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL)));
assertThat(e.toString()).contains("already registered");
KeyManager<Aead> manager = Registry.getKeyManager(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL);
assertThat(manager.getClass().toString()).contains("KeyManagerImpl");
}
@Test
public void testRegisterKeyManager_deprecated_keyManagerIsNull_shouldThrowException()
throws Exception {
IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class,
() -> Registry.registerKeyManager(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL, null));
assertThat(e.toString()).contains("must be non-null");
}
@Test
public void testRegisterKeyManager_deprecated_withKeyTypeNotSupported_shouldThrowException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "yetSomeOtherTypeUrl";
String differentTypeUrl = "differentTypeUrl";
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(differentTypeUrl, new CustomAeadKeyManager(typeUrl)));
assertExceptionContains(e, "Manager does not support key type " + differentTypeUrl);
}
@Test
public void testRegisterKeyManager_deprecated_moreRestrictedNewKeyAllowed_shouldWork()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "typeUrl";
Registry.registerKeyManager(typeUrl, new CustomAeadKeyManager(typeUrl));
try {
Registry.registerKeyManager(typeUrl, new CustomAeadKeyManager(typeUrl), false);
} catch (GeneralSecurityException e) {
fail("repeated registrations of the same key manager should work");
}
}
@Test
public void testRegisterKeyManager_deprecated_lessRestrictedNewKeyAllowed_shouldThrowException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "totallyDifferentTypeUrl";
Registry.registerKeyManager(typeUrl, new CustomAeadKeyManager(typeUrl), false);
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(typeUrl, new CustomAeadKeyManager(typeUrl), true));
}
@Test
public void testRegisterKeyManager_deprecated_keyManagerFromAnotherClass_shouldThrowException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
// This should not overwrite the existing manager.
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerKeyManager(
AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL,
new CustomAeadKeyManager(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL)));
assertThat(e.toString()).contains("already registered");
KeyManager<Aead> manager = Registry.getKeyManager(AeadConfig.AES_CTR_HMAC_AEAD_TYPE_URL);
assertThat(manager.getClass().toString()).contains("KeyManagerImpl");
}
@Test
public void testGetPublicKeyData_shouldWork() throws Exception {
// Skip test if in FIPS mode, as no provider available to instantiate.
Assume.assumeFalse(TinkFips.useOnlyFips());
KeyData privateKeyData = Registry.newKeyData(SignatureKeyTemplates.ECDSA_P256);
KeyData publicKeyData = Registry.getPublicKeyData(privateKeyData.getTypeUrl(),
privateKeyData.getValue());
PublicKeyVerify verifier = Registry.<PublicKeyVerify>getPrimitive(publicKeyData);
PublicKeySign signer = Registry.<PublicKeySign>getPrimitive(privateKeyData);
byte[] message = "Nice test message".getBytes(UTF_8);
verifier.verify(signer.sign(message), message);
}
@Test
public void testGetPublicKeyData_shouldThrow() throws Exception {
KeyData keyData = Registry.newKeyData(MacKeyTemplates.HMAC_SHA256_128BITTAG);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() -> Registry.getPublicKeyData(keyData.getTypeUrl(), keyData.getValue()));
assertThat(e.toString()).contains("not a PrivateKeyManager");
}
@Test
public void testGetInputPrimitive_encryptOnly() throws Exception {
assertThat(Registry.getInputPrimitive(EncryptOnly.class)).isEqualTo(Aead.class);
assertThat(Registry.getInputPrimitive(Aead.class)).isEqualTo(Aead.class);
}
@Test
public void testGetPrimitive_legacy_aesGcm_shouldWork() throws Exception {
// Skip test if in FIPS mode, as EAX is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
AesEaxKey aesEaxKey =
(AesEaxKey) Registry.newKey(AesEaxKeyManager.aes128EaxTemplate().getProto());
KeyData aesEaxKeyData = Registry.newKeyData(AesEaxKeyManager.aes128EaxTemplate().getProto());
Aead aead = Registry.getPrimitive(aesEaxKeyData);
assertThat(aesEaxKey.getKeyValue().size()).isEqualTo(16);
assertThat(aesEaxKeyData.getTypeUrl()).isEqualTo(AeadConfig.AES_EAX_TYPE_URL);
// This might break when we add native implementations.
assertThat(aead.getClass()).isEqualTo(AesEaxJce.class);
}
@Test
public void testGetPrimitive_aesGcm_shouldWork() throws Exception {
// Skip test if in FIPS mode, as EAX is not supported in FIPS mode.
Assume.assumeFalse(TinkFips.useOnlyFips());
AesEaxKey aesEaxKey =
(AesEaxKey) Registry.newKey(AesEaxKeyManager.aes128EaxTemplate().getProto());
KeyData aesEaxKeyData = Registry.newKeyData(AesEaxKeyManager.aes128EaxTemplate().getProto());
Aead aead = Registry.getPrimitive(aesEaxKeyData, Aead.class);
assertThat(aesEaxKey.getKeyValue().size()).isEqualTo(16);
assertThat(aesEaxKeyData.getTypeUrl()).isEqualTo(AeadConfig.AES_EAX_TYPE_URL);
// This might break when we add native implementations.
assertThat(aead.getClass()).isEqualTo(AesEaxJce.class);
}
@Test
public void testGetPrimitive_legacy_hmac_shouldWork() throws Exception {
// Skip test if in FIPS mode, as no provider available to instantiate.
Assume.assumeFalse(TinkFips.useOnlyFips());
com.google.crypto.tink.proto.KeyTemplate template = MacKeyTemplates.HMAC_SHA256_128BITTAG;
HmacKey hmacKey = (HmacKey) Registry.newKey(template);
KeyData hmacKeyData = Registry.newKeyData(template);
Mac mac = Registry.getPrimitive(hmacKeyData);
assertThat(hmacKey.getKeyValue().size()).isEqualTo(32);
assertThat(hmacKey.getParams().getTagSize()).isEqualTo(16);
assertThat(hmacKey.getParams().getHash()).isEqualTo(HashType.SHA256);
assertThat(hmacKeyData.getTypeUrl()).isEqualTo(MacConfig.HMAC_TYPE_URL);
// This might break when we add native implementations.
assertThat(mac.getClass()).isEqualTo(PrfMac.class);
}
@Test
public void testGetPrimitive_hmac_shouldWork() throws Exception {
// Skip test if in FIPS mode, as no provider available to instantiate.
Assume.assumeFalse(TinkFips.useOnlyFips());
com.google.crypto.tink.proto.KeyTemplate template = MacKeyTemplates.HMAC_SHA256_128BITTAG;
HmacKey hmacKey = (HmacKey) Registry.newKey(template);
KeyData hmacKeyData = Registry.newKeyData(template);
Mac mac = Registry.getPrimitive(hmacKeyData, Mac.class);
assertThat(hmacKey.getKeyValue().size()).isEqualTo(32);
assertThat(hmacKey.getParams().getTagSize()).isEqualTo(16);
assertThat(hmacKey.getParams().getHash()).isEqualTo(HashType.SHA256);
assertThat(hmacKeyData.getTypeUrl()).isEqualTo(MacConfig.HMAC_TYPE_URL);
// This might break when we add native implementations.
assertThat(mac.getClass()).isEqualTo(PrfMac.class);
}
@Test
public void
testNewKeyData_keyTemplateProto_registeredTypeUrl_returnsCustomAeadKeyManagerNewKeyData()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "testNewKeyDataTypeUrl";
CustomAeadKeyManager km = new CustomAeadKeyManager(typeUrl);
ByteString keyformat = ByteString.copyFromUtf8("testNewKeyDataKeyFormat");
Registry.registerKeyManager(km);
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(keyformat)
.setTypeUrl(typeUrl)
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
assertThat(Registry.newKeyData(template)).isEqualTo(km.newKeyData(keyformat));
}
@Test
public void testNewKeyData_keyTemplateProto_registeredTypeUrlNewKeyAllowedFalse_throwsException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "testNewKeyDataTypeUrl";
CustomAeadKeyManager km = new CustomAeadKeyManager(typeUrl);
ByteString keyformat = ByteString.copyFromUtf8("testNewKeyDataKeyFormat");
Registry.registerKeyManager(km, false);
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(keyformat)
.setTypeUrl(typeUrl)
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
assertThrows(GeneralSecurityException.class, () -> Registry.newKeyData(template));
}
@Test
public void testNewKeyData_keyTemplateProto_unregisteredTypeUrl_throwsException()
throws Exception {
String typeUrl = "testNewKeyDataTypeUrl";
ByteString keyformat = ByteString.copyFromUtf8("testNewKeyDataKeyFormat");
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(keyformat)
.setTypeUrl(typeUrl)
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
assertThrows(GeneralSecurityException.class, () -> Registry.newKeyData(template));
}
@Test
public void
testNewKeyData_keyTemplateClass_registeredTypeUrl_returnsCustomAeadKeyManagerNewKeyData()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "testNewKeyDataTypeUrl";
CustomAeadKeyManager km = new CustomAeadKeyManager(typeUrl);
ByteString keyformat = ByteString.copyFromUtf8("testNewKeyDataKeyFormat");
Registry.registerKeyManager(km);
com.google.crypto.tink.KeyTemplate template =
com.google.crypto.tink.KeyTemplate.create(
typeUrl, keyformat.toByteArray(),
com.google.crypto.tink.KeyTemplate.OutputPrefixType.TINK);
assertThat(Registry.newKeyData(template)).isEqualTo(km.newKeyData(keyformat));
}
@Test
public void testNewKeyData_keyTemplateClass_registeredTypeUrlNewKeyAllowedFalse_throwsException()
throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
String typeUrl = "testNewKeyDataTypeUrl";
CustomAeadKeyManager km = new CustomAeadKeyManager(typeUrl);
ByteString keyformat = ByteString.copyFromUtf8("testNewKeyDataKeyFormat");
Registry.registerKeyManager(km, false);
com.google.crypto.tink.KeyTemplate template =
com.google.crypto.tink.KeyTemplate.create(
typeUrl, keyformat.toByteArray(),
com.google.crypto.tink.KeyTemplate.OutputPrefixType.TINK);
assertThrows(GeneralSecurityException.class, () -> Registry.newKeyData(template));
}
@Test
public void testNewKeyData_keyTemplateClass_unregisteredTypeUrl_throwsException()
throws Exception {
String typeUrl = "testNewKeyDataTypeUrl";
ByteString keyformat = ByteString.copyFromUtf8("testNewKeyDataKeyFormat");
com.google.crypto.tink.KeyTemplate template =
com.google.crypto.tink.KeyTemplate.create(
typeUrl, keyformat.toByteArray(),
com.google.crypto.tink.KeyTemplate.OutputPrefixType.TINK);
assertThrows(GeneralSecurityException.class, () -> Registry.newKeyData(template));
}
private static Map<String, KeyTypeManager.KeyFactory.KeyFormat<AesGcmKeyFormat>>
createTestAesGcmKeyFormats() {
Map<String, KeyTypeManager.KeyFactory.KeyFormat<AesGcmKeyFormat>> formats = new HashMap<>();
formats.put(
"TINK",
new KeyTypeManager.KeyFactory.KeyFormat<>(
AesGcmKeyFormat.newBuilder().setKeySize(16).build(),
KeyTemplate.OutputPrefixType.TINK));
formats.put(
"RAW",
new KeyTypeManager.KeyFactory.KeyFormat<>(
AesGcmKeyFormat.newBuilder().setKeySize(32).build(), KeyTemplate.OutputPrefixType.RAW));
return Collections.unmodifiableMap(formats);
}
/** Implementation of a KeyTypeManager for testing. */
private static class TestKeyTypeManager extends KeyTypeManager<AesGcmKey> {
private Map<String, KeyFactory.KeyFormat<AesGcmKeyFormat>> keyFormats =
createTestAesGcmKeyFormats();
public TestKeyTypeManager() {
super(
AesGcmKey.class,
new PrimitiveFactory<Aead, AesGcmKey>(Aead.class) {
@Override
public Aead getPrimitive(AesGcmKey key) throws GeneralSecurityException {
return new AesGcmJce(key.getKeyValue().toByteArray());
}
},
new PrimitiveFactory<FakeAead, AesGcmKey>(FakeAead.class) {
@Override
public FakeAead getPrimitive(AesGcmKey key) {
return new FakeAead();
}
});
}
public TestKeyTypeManager(Map<String, KeyFactory.KeyFormat<AesGcmKeyFormat>> keyFormats) {
super(
AesGcmKey.class,
new PrimitiveFactory<Aead, AesGcmKey>(Aead.class) {
@Override
public Aead getPrimitive(AesGcmKey key) throws GeneralSecurityException {
return new AesGcmJce(key.getKeyValue().toByteArray());
}
},
new PrimitiveFactory<FakeAead, AesGcmKey>(FakeAead.class) {
@Override
public FakeAead getPrimitive(AesGcmKey key) {
return new FakeAead();
}
});
this.keyFormats = keyFormats;
}
@Override
public String getKeyType() {
return "type.googleapis.com/google.crypto.tink.AesGcmKey";
}
@Override
public int getVersion() {
return 1;
}
@Override
public KeyMaterialType keyMaterialType() {
return KeyMaterialType.SYMMETRIC;
}
@Override
public void validateKey(AesGcmKey keyProto) throws GeneralSecurityException {
// Throw by hand so we can verify the exception comes from here.
if (keyProto.getKeyValue().size() != 16) {
throw new GeneralSecurityException("validateKey(AesGcmKey) failed");
}
}
@Override
public AesGcmKey parseKey(ByteString byteString) throws InvalidProtocolBufferException {
return AesGcmKey.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry());
}
@Override
public KeyFactory<AesGcmKeyFormat, AesGcmKey> keyFactory() {
return new KeyFactory<AesGcmKeyFormat, AesGcmKey>(AesGcmKeyFormat.class) {
@Override
public void validateKeyFormat(AesGcmKeyFormat format) throws GeneralSecurityException {
// Throw by hand so we can verify the exception comes from here.
if (format.getKeySize() != 16) {
throw new GeneralSecurityException("validateKeyFormat(AesGcmKeyFormat) failed");
}
}
@Override
public AesGcmKeyFormat parseKeyFormat(ByteString byteString)
throws InvalidProtocolBufferException {
return AesGcmKeyFormat.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry());
}
@Override
public AesGcmKey createKey(AesGcmKeyFormat format) throws GeneralSecurityException {
return AesGcmKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(format.getKeySize())))
.setVersion(getVersion())
.build();
}
@Override
public AesGcmKey deriveKey(AesGcmKeyFormat format, InputStream stream)
throws GeneralSecurityException {
byte[] pseudorandomness = new byte[format.getKeySize()];
try {
stream.read(pseudorandomness);
} catch (IOException e) {
throw new AssertionError("Unexpected IOException", e);
}
return AesGcmKey.newBuilder()
.setKeyValue(ByteString.copyFrom(pseudorandomness))
.setVersion(getVersion())
.build();
}
@Override
public Map<String, KeyFactory.KeyFormat<AesGcmKeyFormat>> keyFormats() {
return keyFormats;
}
};
}
@Override
public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
return TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;
}
}
// Same as TestKeyTypeManager, but not compatible with FIPS.
private static class TestKeyTypeManagerNonFips extends TestKeyTypeManager {
@Override
public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
return TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS;
}
}
@Test
public void testRegisterKeyTypeManager() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
}
@Test
public void testRegisterKeyTypeManager_keyTemplates_works() throws Exception {
Registry.reset();
assertThat(Registry.keyTemplates()).isEmpty();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
assertThat(Registry.keyTemplates()).hasSize(2);
assertThat(Registry.keyTemplates()).contains("TINK");
assertThat(Registry.keyTemplates()).contains("RAW");
}
@Test
public void testRegisterKeyTypeManager_disallowedNewKey_keyTemplates_works() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), false);
assertThat(Registry.keyTemplates()).isEmpty();
}
@Test
public void testRegisterKeyTypeManager_existingKeyManager_noNewKeyTemplate_works()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
Registry.registerKeyManager(new TestKeyTypeManager(), true);
}
@Test
public void testRegisterKeyTypeManager_existingKeyManager_newKeyTemplate_fails()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
Map<String, KeyTypeManager.KeyFactory.KeyFormat<AesGcmKeyFormat>> formats = new HashMap<>();
formats.put(
"NEW_KEY_TEMPLATE_NAME",
new KeyTypeManager.KeyFactory.KeyFormat<>(
AesGcmKeyFormat.newBuilder().setKeySize(16).build(),
KeyTemplate.OutputPrefixType.TINK));
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(new TestKeyTypeManager(formats), true));
}
@Test
public void testRegisterKeyTypeManager_newKeyManager_existingKeyTemplate_fails()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
TestKeyTypeManager manager =
new TestKeyTypeManager() {
@Override
public String getKeyType() {
return "blah";
}
};
assertThrows(GeneralSecurityException.class, () -> Registry.registerKeyManager(manager, true));
}
@Test
public void testRegisterKeyTypeManager_getKeyManagerAead_works() throws Exception {
Registry.reset();
TestKeyTypeManager testKeyTypeManager = new TestKeyTypeManager();
Registry.registerKeyManager(testKeyTypeManager, true);
KeyManager<Aead> km = Registry.getKeyManager(testKeyTypeManager.getKeyType(), Aead.class);
assertThat(km.getKeyType()).isEqualTo(testKeyTypeManager.getKeyType());
}
@Test
public void testRegisterKeyTypeManager_getKeyManagerFakeAead_works() throws Exception {
Registry.reset();
TestKeyTypeManager testKeyTypeManager = new TestKeyTypeManager();
Registry.registerKeyManager(testKeyTypeManager, true);
KeyManager<FakeAead> km =
Registry.getKeyManager(testKeyTypeManager.getKeyType(), FakeAead.class);
assertThat(km.getKeyType()).isEqualTo(testKeyTypeManager.getKeyType());
}
@Test
public void testRegisterKeyTypeManager_getKeyManagerMac_throws() throws Exception {
Registry.reset();
TestKeyTypeManager testKeyTypeManager = new TestKeyTypeManager();
Registry.registerKeyManager(testKeyTypeManager, true);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() -> Registry.getKeyManager(testKeyTypeManager.getKeyType(), Mac.class));
assertExceptionContains(e, "com.google.crypto.tink.Mac");
assertExceptionContains(e, "com.google.crypto.tink.Aead");
assertExceptionContains(e, "com.google.crypto.tink.RegistryTest.FakeAead");
}
// Checks that calling getUntypedKeyManager will return the keymanager for the *first* implemented
// class in the constructor.
@Test
public void testRegisterKeyTypeManager_getUntypedKeyManager_returnsAead() throws Exception {
Registry.reset();
TestKeyTypeManager testKeyTypeManager = new TestKeyTypeManager();
Registry.registerKeyManager(testKeyTypeManager, true);
KeyManager<?> km = Registry.getUntypedKeyManager(testKeyTypeManager.getKeyType());
assertThat(km.getPrimitiveClass()).isEqualTo(Aead.class);
}
@Test
public void testRegisterKeyTypeManager_moreRestrictedNewKeyAllowed_shouldWork() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
Registry.registerKeyManager(new TestKeyTypeManager(), false);
}
@Test
public void testRegisterKeyTypeManager_sameNewKeyAllowed_shouldWork() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
Registry.registerKeyManager(new TestKeyTypeManager(), true);
Registry.registerKeyManager(new TestKeyTypeManager(), false);
Registry.registerKeyManager(new TestKeyTypeManager(), false);
}
@Test
public void testRegisterKeyTypeManager_lessRestrictedNewKeyAllowed_throws() throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), false);
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(new TestKeyTypeManager(), true));
}
@Test
public void testRegisterKeyTypeManager_differentClass_throws() throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(new TestKeyTypeManager() {}, true));
}
@Test
public void testRegisterKeyTypeManager_afterKeyManager_throws() throws Exception {
// Skip test if in FIPS mode, as registerKeyManager() is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
Registry.reset();
Registry.registerKeyManager(new CustomAeadKeyManager(new TestKeyTypeManager().getKeyType()));
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(new TestKeyTypeManager(), true));
}
@Test
public void testRegisterKeyTypeManager_beforeKeyManager_throws() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerKeyManager(
new CustomAeadKeyManager(new TestKeyTypeManager().getKeyType())));
}
@Test
public void testParseKeyData_succeeds() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
AesGcmKey key =
AesGcmKey.newBuilder()
.setKeyValue(ByteString.copyFrom("0123456789abcdef".getBytes(UTF_8)))
.build();
KeyData keyData =
KeyData.newBuilder()
.setTypeUrl(new TestKeyTypeManager().getKeyType())
.setValue(key.toByteString())
.build();
assertThat(Registry.parseKeyData(keyData)).isEqualTo(key);
}
@Test
public void testDeriveKey_succeeds() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
AesGcmKeyFormat format = AesGcmKeyFormat.newBuilder().setKeySize(16).build();
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(format.toByteString())
.setTypeUrl(new TestKeyTypeManager().getKeyType())
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
byte[] keyMaterial = Random.randBytes(100);
KeyData keyData = Registry.deriveKey(template, new ByteArrayInputStream(keyMaterial));
assertThat(keyData.getKeyMaterialType()).isEqualTo(new TestKeyTypeManager().keyMaterialType());
assertThat(keyData.getTypeUrl()).isEqualTo(new TestKeyTypeManager().getKeyType());
AesGcmKey key =
AesGcmKey.parseFrom(keyData.getValue(), ExtensionRegistryLite.getEmptyRegistry());
for (int i = 0; i < 16; ++i) {
assertThat(key.getKeyValue().byteAt(i)).isEqualTo(keyMaterial[i]);
}
}
// Tests that validate is called.
@Test
public void testDeriveKey_wrongKeySize_validateThrows() throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestKeyTypeManager(), true);
AesGcmKeyFormat format = AesGcmKeyFormat.newBuilder().setKeySize(32).build();
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(format.toByteString())
.setTypeUrl(new TestKeyTypeManager().getKeyType())
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
ByteArrayInputStream emptyInput = new ByteArrayInputStream(new byte[0]);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class, () -> Registry.deriveKey(template, emptyInput));
assertExceptionContains(e, "validateKeyFormat");
}
@Test
public void testDeriveKey_inexistantKeyMananger_throws() throws Exception {
Registry.reset();
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(AesGcmKeyFormat.getDefaultInstance().toByteString())
.setTypeUrl(new TestKeyTypeManager().getKeyType())
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
ByteArrayInputStream emptyInput = new ByteArrayInputStream(new byte[0]);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class, () -> Registry.deriveKey(template, emptyInput));
assertExceptionContains(e, "No keymanager registered");
}
private static class PublicPrimitiveA {}
private static class PublicPrimitiveB {}
private static class TestPublicKeyTypeManager extends KeyTypeManager<Ed25519PublicKey> {
public TestPublicKeyTypeManager() {
super(
Ed25519PublicKey.class,
new PrimitiveFactory<PublicPrimitiveA, Ed25519PublicKey>(PublicPrimitiveA.class) {
@Override
public PublicPrimitiveA getPrimitive(Ed25519PublicKey key) {
return new PublicPrimitiveA();
}
},
new PrimitiveFactory<PublicPrimitiveB, Ed25519PublicKey>(PublicPrimitiveB.class) {
@Override
public PublicPrimitiveB getPrimitive(Ed25519PublicKey key) {
return new PublicPrimitiveB();
}
});
}
@Override
public String getKeyType() {
return "type.googleapis.com/google.crypto.tink.Ed25519PublicKey";
}
@Override
public int getVersion() {
return 1;
}
@Override
public KeyMaterialType keyMaterialType() {
return KeyMaterialType.ASYMMETRIC_PUBLIC;
}
@Override
public void validateKey(Ed25519PublicKey keyProto) throws GeneralSecurityException {
if (keyProto.getKeyValue().size() != 32) {
throw new GeneralSecurityException("validateKey(Ed25519PublicKey) failed");
}
}
@Override
public Ed25519PublicKey parseKey(ByteString byteString) throws InvalidProtocolBufferException {
return Ed25519PublicKey.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry());
}
@Override
public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
return TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;
}
}
// Same as TestPublicKeyTypeManager, but not compatible with FIPS.
private static class TestPublicKeyTypeManagerNonFips extends TestPublicKeyTypeManager {
@Override
public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
return TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS;
}
}
private static class PrivatePrimitiveA {}
private static class PrivatePrimitiveB {}
private static Map<String, KeyTypeManager.KeyFactory.KeyFormat<Ed25519KeyFormat>>
createTestEd25519KeyFormats() {
Map<String, KeyTypeManager.KeyFactory.KeyFormat<Ed25519KeyFormat>> formats = new HashMap<>();
formats.put(
"TINK",
new KeyTypeManager.KeyFactory.KeyFormat<>(
Ed25519KeyFormat.getDefaultInstance(), KeyTemplate.OutputPrefixType.TINK));
formats.put(
"RAW",
new KeyTypeManager.KeyFactory.KeyFormat<>(
Ed25519KeyFormat.getDefaultInstance(), KeyTemplate.OutputPrefixType.RAW));
return Collections.unmodifiableMap(formats);
}
private static class TestPrivateKeyTypeManager
extends PrivateKeyTypeManager<Ed25519PrivateKey, Ed25519PublicKey> {
public TestPrivateKeyTypeManager() {
super(
Ed25519PrivateKey.class,
Ed25519PublicKey.class,
new PrimitiveFactory<PrivatePrimitiveA, Ed25519PrivateKey>(PrivatePrimitiveA.class) {
@Override
public PrivatePrimitiveA getPrimitive(Ed25519PrivateKey key) {
return new PrivatePrimitiveA();
}
},
new PrimitiveFactory<PrivatePrimitiveB, Ed25519PrivateKey>(PrivatePrimitiveB.class) {
@Override
public PrivatePrimitiveB getPrimitive(Ed25519PrivateKey key) {
return new PrivatePrimitiveB();
}
});
}
@Override
public String getKeyType() {
return "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey";
}
@Override
public int getVersion() {
return 1;
}
@Override
public KeyMaterialType keyMaterialType() {
return KeyMaterialType.ASYMMETRIC_PRIVATE;
}
@Override
public void validateKey(Ed25519PrivateKey keyProto) throws GeneralSecurityException {
// Throw by hand so we can verify the exception comes from here.
if (keyProto.getKeyValue().size() != 32) {
throw new GeneralSecurityException("validateKey(Ed25519PrivateKey) failed");
}
}
@Override
public Ed25519PrivateKey parseKey(ByteString byteString) throws InvalidProtocolBufferException {
return Ed25519PrivateKey.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry());
}
@Override
public Ed25519PublicKey getPublicKey(Ed25519PrivateKey privateKey) {
return privateKey.getPublicKey();
}
@Override
public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
return TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;
}
}
// Same as TestPrivateKeyTypeManager, but not compatible with FIPS.
private static class TestPrivateKeyTypeManagerNonFips extends TestPrivateKeyTypeManager {
@Override
public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
return TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS;
}
}
private static class TestPrivateKeyTypeManagerWithKeyFactory extends TestPrivateKeyTypeManager {
private Map<String, KeyTypeManager.KeyFactory.KeyFormat<Ed25519KeyFormat>> keyFormats =
createTestEd25519KeyFormats();
public TestPrivateKeyTypeManagerWithKeyFactory() {
super();
}
public TestPrivateKeyTypeManagerWithKeyFactory(
Map<String, KeyTypeManager.KeyFactory.KeyFormat<Ed25519KeyFormat>> keyFormats) {
super();
this.keyFormats = keyFormats;
}
@Override
public KeyTypeManager.KeyFactory<Ed25519KeyFormat, Ed25519PrivateKey> keyFactory() {
return new KeyTypeManager.KeyFactory<Ed25519KeyFormat, Ed25519PrivateKey>(
Ed25519KeyFormat.class) {
@Override
public void validateKeyFormat(Ed25519KeyFormat format) throws GeneralSecurityException {}
@Override
public Ed25519KeyFormat parseKeyFormat(ByteString byteString)
throws InvalidProtocolBufferException {
return Ed25519KeyFormat.parseFrom(byteString, ExtensionRegistryLite.getEmptyRegistry());
}
@Override
public Ed25519PrivateKey createKey(Ed25519KeyFormat format)
throws GeneralSecurityException {
return Ed25519PrivateKey.newBuilder()
.setKeyValue(ByteString.copyFrom("created", UTF_8))
.build();
}
@Override
public Ed25519PrivateKey deriveKey(Ed25519KeyFormat format, InputStream inputStream)
throws GeneralSecurityException {
return Ed25519PrivateKey.newBuilder()
.setKeyValue(ByteString.copyFrom("derived", UTF_8))
.build();
}
@Override
public Map<String, KeyTypeManager.KeyFactory.KeyFormat<Ed25519KeyFormat>> keyFormats() {
return keyFormats;
}
};
}
}
@Test
public void testRegisterAssymmetricKeyManagers() throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
}
@Test
public void testRegisterAssymmetricKeyManagers_keyTemplates_works() throws Exception {
Registry.reset();
assertThat(Registry.keyTemplates()).isEmpty();
Registry.registerKeyManager(new TestPrivateKeyTypeManagerWithKeyFactory(), true);
assertThat(Registry.keyTemplates()).hasSize(2);
assertThat(Registry.keyTemplates()).contains("TINK");
assertThat(Registry.keyTemplates()).contains("RAW");
}
@Test
public void testRegisterAssymmetricKeyManagers_disallowedNewKey_keyTemplates_works()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestPrivateKeyTypeManagerWithKeyFactory(), false);
assertThat(Registry.keyTemplates()).isEmpty();
}
@Test
public void testRegisterAssymmetricKeyManagers_existingKeyManager_noNewKeyTemplate_works()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestPrivateKeyTypeManagerWithKeyFactory(), true);
Registry.registerKeyManager(new TestPrivateKeyTypeManagerWithKeyFactory(), true);
}
@Test
public void testRegisterAssymmetricKeyManagers_existingKeyManager_newKeyTemplate_fails()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestPrivateKeyTypeManagerWithKeyFactory(), true);
Map<String, KeyTypeManager.KeyFactory.KeyFormat<Ed25519KeyFormat>> formats = new HashMap<>();
formats.put(
"NEW_KEY_TEMPLATE_NAME",
new KeyTypeManager.KeyFactory.KeyFormat<>(
Ed25519KeyFormat.getDefaultInstance(), KeyTemplate.OutputPrefixType.TINK));
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerKeyManager(
new TestPrivateKeyTypeManagerWithKeyFactory(formats), true));
}
@Test
public void testRegisterAssymmetricKeyManagers_newKeyManager_existingKeyTemplate_fails()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestPrivateKeyTypeManagerWithKeyFactory(), true);
TestPrivateKeyTypeManagerWithKeyFactory manager =
new TestPrivateKeyTypeManagerWithKeyFactory() {
@Override
public String getKeyType() {
return "blah";
}
};
assertThrows(GeneralSecurityException.class, () -> Registry.registerKeyManager(manager, true));
}
@Test
public void testRegisterAssymmetricKeyManagers_getPrivateKeyManagerPrimitiveA_works()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
KeyManager<PrivatePrimitiveA> km =
Registry.getKeyManager(
new TestPrivateKeyTypeManager().getKeyType(), PrivatePrimitiveA.class);
assertThat(km.getKeyType()).isEqualTo(new TestPrivateKeyTypeManager().getKeyType());
}
@Test
public void testRegisterAssymmetricKeyManagers_getPrivateKeyManagerPrimitiveB_works()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
KeyManager<PrivatePrimitiveB> km =
Registry.getKeyManager(
new TestPrivateKeyTypeManager().getKeyType(), PrivatePrimitiveB.class);
assertThat(km.getKeyType()).isEqualTo(new TestPrivateKeyTypeManager().getKeyType());
}
@Test
public void testRegisterAssymmetricKeyManagers_getPrivateKeyManagerPublicA_works()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
KeyManager<PublicPrimitiveA> km =
Registry.getKeyManager(new TestPublicKeyTypeManager().getKeyType(), PublicPrimitiveA.class);
assertThat(km.getKeyType()).isEqualTo(new TestPublicKeyTypeManager().getKeyType());
}
@Test
public void testRegisterAssymmetricKeyManagers_getPrivateKeyManagerPublicB_works()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
KeyManager<PublicPrimitiveB> km =
Registry.getKeyManager(new TestPublicKeyTypeManager().getKeyType(), PublicPrimitiveB.class);
assertThat(km.getKeyType()).isEqualTo(new TestPublicKeyTypeManager().getKeyType());
}
@Test
public void testRegisterAssymmetricKeyManagers_getPrivateKeyManagerWrongPrimitive_throws()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() -> Registry.getKeyManager(new TestPrivateKeyTypeManager().getKeyType(), Mac.class));
assertExceptionContains(e, "com.google.crypto.tink.Mac");
assertExceptionContains(e, "PrivatePrimitiveA");
assertExceptionContains(e, "PrivatePrimitiveB");
}
@Test
public void testRegisterAssymmetricKeyManagers_getPublicKeyManagerWrongPrimitive_throws()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() -> Registry.getKeyManager(new TestPublicKeyTypeManager().getKeyType(), Mac.class));
assertExceptionContains(e, "com.google.crypto.tink.Mac");
assertExceptionContains(e, "PublicPrimitiveA");
assertExceptionContains(e, "PublicPrimitiveB");
}
// Checks that calling getUntypedKeyManager will return the keymanager for the *first* implemented
// class in the constructor.
@Test
public void testRegisterAssymmetricKeyManagers_getUntypedPrivateKeyManager_returnsPrimitiveA()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
KeyManager<?> km = Registry.getUntypedKeyManager(new TestPrivateKeyTypeManager().getKeyType());
assertThat(km.getPrimitiveClass()).isEqualTo(PrivatePrimitiveA.class);
}
// Checks that calling getUntypedKeyManager will return the keymanager for the *first* implemented
// class in the constructor.
@Test
public void testRegisterAssymmetricKeyManagers_getUntypedPublicKeyManager_returnsPrimitiveA()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
KeyManager<?> km = Registry.getUntypedKeyManager(new TestPublicKeyTypeManager().getKeyType());
assertThat(km.getPrimitiveClass()).isEqualTo(PublicPrimitiveA.class);
}
@Test
public void testRegisterAssymmetricKeyManagers_newKeyAllowed_withoutKeyFactory_fails()
throws Exception {
Registry.reset();
assertThrows(
UnsupportedOperationException.class,
() ->
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), true));
}
@Test
public void testRegisterAssymmetricKeyManagers_moreRestrictedNewKeyAllowed_shouldWork()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), true);
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), false);
}
@Test
public void testRegisterAssymmetricKeyManagers_sameNewKeyAllowed_shouldWork() throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), true);
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), true);
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), false);
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), false);
}
@Test
public void testRegisterAssymmetricKeyManagers_lessRestrictedNewKeyAllowed_throws()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), false);
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(),
new TestPublicKeyTypeManager(),
true));
}
@Test
public void testRegisterAssymmetricKeyManagers_publicKeyManagerCanBeRegisteredAlone()
throws Exception {
Registry.reset();
Registry.registerKeyManager(new TestPublicKeyTypeManager(), false);
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), true);
Registry.registerKeyManager(new TestPublicKeyTypeManager(), false);
}
@Test
public void testRegisterAssymmetricKeyManagers_getPublicKeyData_shouldWork()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
Ed25519PrivateKey privateKey =
Ed25519PrivateKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(32)))
.setPublicKey(
Ed25519PublicKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(32))))
.build();
KeyData publicKeyData =
Registry.getPublicKeyData(
new TestPrivateKeyTypeManager().getKeyType(), privateKey.toByteString());
assertThat(publicKeyData.getTypeUrl()).isEqualTo(new TestPublicKeyTypeManager().getKeyType());
Ed25519PublicKey publicKey =
Ed25519PublicKey.parseFrom(
publicKeyData.getValue(), ExtensionRegistryLite.getEmptyRegistry());
assertThat(publicKey.getKeyValue()).isEqualTo(privateKey.getPublicKey().getKeyValue());
}
@Test
public void testRegisterAsymmetricKeyManagersSeparately_getPublicKeyData_throws()
throws Exception {
Registry.reset();
// Register asymmetric key managers only with registerKeyManager.
Registry.registerKeyManager(new TestPrivateKeyTypeManager(), false);
Registry.registerKeyManager(new TestPublicKeyTypeManager(), false);
Ed25519PrivateKey privateKey =
Ed25519PrivateKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(32)))
.setPublicKey(
Ed25519PublicKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(32))))
.build();
assertThrows(
GeneralSecurityException.class,
() ->
Registry.getPublicKeyData(
new TestPrivateKeyTypeManager().getKeyType(), privateKey.toByteString()));
}
@Test
public void testRegisterAssymmetricKeyManagersOnce_getPublicKeyData_shouldWork()
throws Exception {
Registry.reset();
// Register asymmetric key managers with registerAsymmetricKeyManagers and registerKeyManager.
Registry.registerKeyManager(new TestPrivateKeyTypeManager(), false);
Registry.registerKeyManager(new TestPublicKeyTypeManager(), false);
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
Registry.registerKeyManager(new TestPrivateKeyTypeManager(), false);
Registry.registerKeyManager(new TestPublicKeyTypeManager(), false);
Ed25519PrivateKey privateKey =
Ed25519PrivateKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(32)))
.setPublicKey(
Ed25519PublicKey.newBuilder()
.setKeyValue(ByteString.copyFrom(Random.randBytes(32))))
.build();
KeyData publicKeyData =
Registry.getPublicKeyData(
new TestPrivateKeyTypeManager().getKeyType(), privateKey.toByteString());
assertThat(publicKeyData.getTypeUrl()).isEqualTo(new TestPublicKeyTypeManager().getKeyType());
Ed25519PublicKey publicKey =
Ed25519PublicKey.parseFrom(
publicKeyData.getValue(), ExtensionRegistryLite.getEmptyRegistry());
assertThat(publicKey.getKeyValue()).isEqualTo(privateKey.getPublicKey().getKeyValue());
}
@Test
public void testRegisterAssymmetricKeyManagers_differentClassPrivateKey_throws()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerAsymmetricKeyManagers(
// Note: due to the {} this is a subclass of TestPrivateKeyTypeManager.
new TestPrivateKeyTypeManager() {}, new TestPublicKeyTypeManager(), false));
}
@Test
public void testRegisterAssymmetricKeyManagers_differentClassPublicKey_throws() throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerAsymmetricKeyManagers(
// Note: due to the {} this is a subclass of TestPublicKeyTypeManager.
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager() {}, false));
}
@Test
public void testRegisterAssymmetricKeyManagers_thenNormalRegister_throws()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
assertThrows(
GeneralSecurityException.class,
// Note: due to the {} this is a subclass of TestPublicKeyTypeManager.
() -> Registry.registerKeyManager(new TestPrivateKeyTypeManager() {}, false));
}
@Test
public void testRegisterAssymmetricKeyManagers_thenNormalRegisterForPublic_throws()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
assertThrows(
GeneralSecurityException.class,
// Note: due to the {} this is a subclass of TestPublicKeyTypeManager.
() -> Registry.registerKeyManager(new TestPublicKeyTypeManager() {}, false));
}
@Test
public void testRegisterAssymmetricKeyManagers_throwsWithDifferentPublicKeyManager()
throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
GeneralSecurityException e =
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(),
new TestPublicKeyTypeManager() {
@Override
public String getKeyType() {
return "bla";
}
},
false));
assertExceptionContains(e, "public key manager corresponding to");
}
@Test
public void testAsymmetricKeyManagers_deriveKey_withoutKeyFactory() throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManager(), new TestPublicKeyTypeManager(), false);
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(Ed25519KeyFormat.getDefaultInstance().toByteString())
.setTypeUrl(new TestPrivateKeyTypeManager().getKeyType())
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
assertThrows(
UnsupportedOperationException.class,
() -> Registry.deriveKey(template, new ByteArrayInputStream(new byte[0])));
}
@Test
public void testAsymmetricKeyManagers_deriveKey() throws Exception {
Registry.reset();
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerWithKeyFactory(), new TestPublicKeyTypeManager(), true);
com.google.crypto.tink.proto.KeyTemplate template =
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setValue(Ed25519KeyFormat.getDefaultInstance().toByteString())
.setTypeUrl(new TestPrivateKeyTypeManagerWithKeyFactory().getKeyType())
.setOutputPrefixType(OutputPrefixType.TINK)
.build();
KeyData keyData = Registry.deriveKey(template, new ByteArrayInputStream(new byte[0]));
Ed25519PrivateKey key =
Ed25519PrivateKey.parseFrom(keyData.getValue(), ExtensionRegistryLite.getEmptyRegistry());
assertThat(key.getKeyValue()).isEqualTo(ByteString.copyFrom("derived", UTF_8));
}
private static class Catalogue1 implements Catalogue<Aead> {
@Override
public KeyManager<Aead> getKeyManager(String typeUrl, String primitiveName, int minVersion) {
return null;
}
@Override
public PrimitiveWrapper<Aead, Aead> getPrimitiveWrapper() {
return null;
}
}
private static class Catalogue2 implements Catalogue<Aead> {
@Override
public KeyManager<Aead> getKeyManager(String typeUrl, String primitiveName, int minVersion) {
return null;
}
@Override
public PrimitiveWrapper<Aead, Aead> getPrimitiveWrapper() {
return null;
}
}
private static class Catalogue3 implements Catalogue<Aead> {
@Override
public KeyManager<Aead> getKeyManager(String typeUrl, String primitiveName, int minVersion) {
return null;
}
@Override
public PrimitiveWrapper<Aead, Aead> getPrimitiveWrapper() {
return null;
}
}
@Test
public void testAddCatalogue_multiThreads_shouldWork() throws Exception {
final boolean[] threwException = new boolean[3];
Thread thread1 =
new Thread(
new Runnable() {
@Override
public void run() {
try {
Registry.addCatalogue("catalogue", new Catalogue1());
threwException[0] = false;
} catch (GeneralSecurityException e) {
threwException[0] = true;
}
}
});
Thread thread2 =
new Thread(
new Runnable() {
@Override
public void run() {
try {
Registry.addCatalogue("catalogue", new Catalogue2());
threwException[1] = false;
} catch (GeneralSecurityException e) {
threwException[1] = true;
}
}
});
Thread thread3 =
new Thread(
new Runnable() {
@Override
public void run() {
try {
Registry.addCatalogue("catalogue", new Catalogue3());
threwException[2] = false;
} catch (GeneralSecurityException e) {
threwException[2] = true;
}
}
});
// Start the threads.
thread1.start();
thread2.start();
thread3.start();
// Wait until all threads finished.
thread1.join();
thread2.join();
thread3.join();
// Count threads that threw exception.
int count = 0;
for (int i = 0; i < 3; i++) {
if (threwException[i]) {
count++;
}
}
assertThat(count).isEqualTo(2);
}
// TODO(przydatek): Add more tests for creation of PrimitiveSets.
private static PrimitiveSet<Aead> createAeadPrimitiveSet() throws Exception {
return TestUtil.createPrimitiveSet(
TestUtil.createKeyset(
Keyset.Key.newBuilder()
.setKeyData(Registry.newKeyData(AesEaxKeyManager.aes128EaxTemplate()))
.setKeyId(1)
.setStatus(KeyStatusType.ENABLED)
.setOutputPrefixType(OutputPrefixType.TINK)
.build()),
Aead.class);
}
@Test
public void testWrap_wrapperRegistered() throws Exception {
// Skip test if in FIPS mode, as EAX is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
Registry.wrap(createAeadPrimitiveSet());
}
@Test
public void testWrap_noWrapperRegistered_throws() throws Exception {
// Skip test if in FIPS mode, as EAX is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
PrimitiveSet<Aead> aeadSet = createAeadPrimitiveSet();
Registry.reset();
GeneralSecurityException e =
assertThrows(GeneralSecurityException.class, () -> Registry.wrap(aeadSet));
assertExceptionContains(e, "No wrapper found");
assertExceptionContains(e, "Aead");
}
@Test
public void testWrap_wrapAsEncryptOnly() throws Exception {
// Skip test if in FIPS mode, as EAX is not allowed in FipsMode.
Assume.assumeFalse(TinkFips.useOnlyFips());
// Check that Registry.wrap can be assigned to an EncryptOnly (as there's a suppress warning).
EncryptOnly encrypt = Registry.wrap(createAeadPrimitiveSet(), EncryptOnly.class);
assertThat(encrypt).isNotNull();
}
@Test
public void testWrap_registerSecondWrapperForEncryptOnly_throws() throws Exception {
assertThrows(
GeneralSecurityException.class,
() -> {
Registry.registerPrimitiveWrapper(
new PrimitiveWrapper<Mac, EncryptOnly>() {
@Override
public EncryptOnly wrap(PrimitiveSet<Mac> primitiveSet) {
return null;
}
@Override
public Class<EncryptOnly> getPrimitiveClass() {
return EncryptOnly.class;
}
@Override
public Class<Mac> getInputPrimitiveClass() {
return Mac.class;
}
});
});
}
@Test
public void testFips_succeedsOnEmptyRegistry() throws Exception {
Registry.reset();
Registry.restrictToFipsIfEmpty();
assertTrue(TinkFipsUtil.useOnlyFips());
}
@Test
public void testFips_failsOnNonEmptyRegistry() throws Exception {
assertThrows(GeneralSecurityException.class, Registry::restrictToFipsIfEmpty);
}
@Test
public void testFips_registerNonFipsKeyTypeManagerFails() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
assertThrows(
GeneralSecurityException.class,
() -> Registry.registerKeyManager(new TestKeyTypeManagerNonFips(), true));
}
@Test
public void testFips_registerFipsKeyTypeManagerSucceeds() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
AesGcmKeyManager.register(true);
}
@Test
public void testFips_registerNonFipsKeyTypeManagerAsymmetricFails() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
assertThrows(
GeneralSecurityException.class,
() ->
Registry.registerAsymmetricKeyManagers(
new TestPrivateKeyTypeManagerNonFips(),
new TestPublicKeyTypeManagerNonFips(),
false));
}
@Test
public void testFips_registerFipsKeyTypeManagerAsymmetricSucceeds() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
EcdsaSignKeyManager.registerPair(true);
}
@Test
public void testFips_registerFipsKeyManager_fails() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
String typeUrl = "testNewKeyDataTypeUrl";
CustomAeadKeyManager km = new CustomAeadKeyManager(typeUrl);
assertThrows(
GeneralSecurityException.class, () -> Registry.registerKeyManager(km));
}
private static class FakeAead {}
}