Add integration tests for key managers and signatures.
These ensure that if users register Signature KeyManagers it works as expected.
PiperOrigin-RevId: 602623367
Change-Id: I5afd48263ff0917fe2c6d788eb783db3f76f9aa9
diff --git a/src/test/java/com/google/crypto/tink/signature/BUILD.bazel b/src/test/java/com/google/crypto/tink/signature/BUILD.bazel
index b28409e..9c964ec 100644
--- a/src/test/java/com/google/crypto/tink/signature/BUILD.bazel
+++ b/src/test/java/com/google/crypto/tink/signature/BUILD.bazel
@@ -686,3 +686,34 @@
"@maven//:junit_junit",
],
)
+
+java_test(
+ name = "KeyManagerIntegrationTest",
+ size = "small",
+ srcs = ["KeyManagerIntegrationTest.java"],
+ deps = [
+ "//proto:ed25519_java_proto",
+ "//proto:tink_java_proto",
+ "//src/main/java/com/google/crypto/tink:insecure_secret_key_access",
+ "//src/main/java/com/google/crypto/tink:public_key_sign",
+ "//src/main/java/com/google/crypto/tink:public_key_verify",
+ "//src/main/java/com/google/crypto/tink:registry",
+ "//src/main/java/com/google/crypto/tink:registry_cluster",
+ "//src/main/java/com/google/crypto/tink:tink_proto_keyset_format",
+ "//src/main/java/com/google/crypto/tink/signature:ed25519_parameters",
+ "//src/main/java/com/google/crypto/tink/signature:ed25519_private_key",
+ "//src/main/java/com/google/crypto/tink/signature:ed25519_public_key",
+ "//src/main/java/com/google/crypto/tink/signature:signature_config",
+ "//src/main/java/com/google/crypto/tink/signature/internal/testing:legacy_public_key_sign_key_manager",
+ "//src/main/java/com/google/crypto/tink/signature/internal/testing:legacy_public_key_verify_key_manager",
+ "//src/main/java/com/google/crypto/tink/subtle:ed25519_sign",
+ "//src/main/java/com/google/crypto/tink/subtle:ed25519_verify",
+ "//src/main/java/com/google/crypto/tink/subtle:hex",
+ "//src/main/java/com/google/crypto/tink/util:bytes",
+ "//src/main/java/com/google/crypto/tink/util:secret_bytes",
+ "@maven//:com_google_code_findbugs_jsr305",
+ "@maven//:com_google_protobuf_protobuf_java",
+ "@maven//:com_google_truth_truth",
+ "@maven//:junit_junit",
+ ],
+)
diff --git a/src/test/java/com/google/crypto/tink/signature/KeyManagerIntegrationTest.java b/src/test/java/com/google/crypto/tink/signature/KeyManagerIntegrationTest.java
new file mode 100644
index 0000000..25337d7
--- /dev/null
+++ b/src/test/java/com/google/crypto/tink/signature/KeyManagerIntegrationTest.java
@@ -0,0 +1,262 @@
+// Copyright 2024 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.signature;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.crypto.tink.InsecureSecretKeyAccess;
+import com.google.crypto.tink.KeysetHandle;
+import com.google.crypto.tink.PublicKeySign;
+import com.google.crypto.tink.PublicKeyVerify;
+import com.google.crypto.tink.Registry;
+import com.google.crypto.tink.TinkProtoKeysetFormat;
+import com.google.crypto.tink.proto.Ed25519PrivateKey;
+import com.google.crypto.tink.proto.Ed25519PublicKey;
+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.internal.testing.LegacyPublicKeySignKeyManager;
+import com.google.crypto.tink.signature.internal.testing.LegacyPublicKeyVerifyKeyManager;
+import com.google.crypto.tink.subtle.Ed25519Sign;
+import com.google.crypto.tink.subtle.Ed25519Verify;
+import com.google.crypto.tink.subtle.Hex;
+import com.google.crypto.tink.util.Bytes;
+import com.google.crypto.tink.util.SecretBytes;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.ExtensionRegistryLite;
+import java.security.GeneralSecurityException;
+import javax.annotation.Nullable;
+import org.junit.BeforeClass;
+import org.junit.Test;
+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;
+
+/**
+ * This test attempts to test the case where a user registers their own key type with
+ * Registry.registerKeyManager() and then uses it.
+ */
+@RunWith(Theories.class)
+public final class KeyManagerIntegrationTest {
+ private static final String PRIVATE_TYPE_URL = "type.googleapis.com/custom.Ed25519PrivateKey";
+ private static final String PUBLIC_TYPE_URL = "type.googleapis.com/custom.Ed25519PublicKey";
+
+ private static byte[] publicKeyByteArray;
+ private static byte[] privateKeyByteArray;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ // We register Tink and key manger, as a user would typically do if they add their own key type.
+ SignatureConfig.register();
+ // Register the key managers the user would register. These have type URLs PRIVATE_TYPE_URL and
+ // PUBLIC_TYPE_URL, and interpret the keys as Ed25519PrivateKey and Ed25519PublicKey exactly
+ // as Tink would.
+ Registry.registerKeyManager(new LegacyPublicKeySignKeyManager(), true);
+ Registry.registerKeyManager(new LegacyPublicKeyVerifyKeyManager(), false);
+
+ publicKeyByteArray =
+ Hex.decode("ea42941a6dc801484390b2955bc7376d172eeb72640a54e5b50c95efa2fc6ad8");
+ privateKeyByteArray =
+ Hex.decode("9cac7d19aeecc563a3dff7bcae0fbbbc28087b986c49a3463077dd5281437e81");
+ }
+
+ @Test
+ public void testGetPublicKeyset_works() throws Exception {
+ Ed25519PublicKey protoPublicKey =
+ Ed25519PublicKey.newBuilder()
+ .setVersion(0)
+ .setKeyValue(ByteString.copyFrom(publicKeyByteArray))
+ .build();
+ Ed25519PrivateKey protoPrivateKey =
+ Ed25519PrivateKey.newBuilder()
+ .setVersion(0)
+ .setPublicKey(protoPublicKey)
+ .setKeyValue(ByteString.copyFrom(privateKeyByteArray))
+ .build();
+ KeyData keyData =
+ KeyData.newBuilder()
+ .setTypeUrl(PRIVATE_TYPE_URL)
+ .setValue(protoPrivateKey.toByteString())
+ .setKeyMaterialType(KeyMaterialType.ASYMMETRIC_PRIVATE)
+ .build();
+ Keyset keyset =
+ Keyset.newBuilder()
+ .addKey(
+ Keyset.Key.newBuilder()
+ .setKeyData(keyData)
+ .setStatus(KeyStatusType.ENABLED)
+ .setOutputPrefixType(OutputPrefixType.TINK)
+ .setKeyId(0x23456789)
+ .build())
+ .setPrimaryKeyId(0x23456789)
+ .build();
+
+ KeysetHandle handle =
+ TinkProtoKeysetFormat.parseKeyset(keyset.toByteArray(), InsecureSecretKeyAccess.get());
+ KeysetHandle publicHandle = handle.getPublicKeysetHandle();
+
+ Keyset publicKeyset =
+ Keyset.parseFrom(
+ TinkProtoKeysetFormat.serializeKeysetWithoutSecret(publicHandle),
+ ExtensionRegistryLite.getEmptyRegistry());
+
+ assertThat(publicKeyset.getPrimaryKeyId()).isEqualTo(0x23456789);
+ assertThat(publicKeyset.getKeyCount()).isEqualTo(1);
+ assertThat(publicKeyset.getKey(0).getKeyId()).isEqualTo(0x23456789);
+ assertThat(publicKeyset.getKey(0).getStatus()).isEqualTo(KeyStatusType.ENABLED);
+ assertThat(publicKeyset.getKey(0).getOutputPrefixType()).isEqualTo(OutputPrefixType.TINK);
+ assertThat(publicKeyset.getKey(0).getKeyData().getTypeUrl()).isEqualTo(PUBLIC_TYPE_URL);
+ assertThat(publicKeyset.getKey(0).getKeyData().getKeyMaterialType())
+ .isEqualTo(KeyMaterialType.ASYMMETRIC_PUBLIC);
+ assertThat(
+ Ed25519PublicKey.parseFrom(
+ publicKeyset.getKey(0).getKeyData().getValue(),
+ ExtensionRegistryLite.getEmptyRegistry()))
+ .isEqualTo(protoPublicKey);
+ }
+
+ @DataPoints("allOutputPrefixTypes")
+ public static final OutputPrefixType[] OUTPUT_PREFIX_TYPES =
+ new OutputPrefixType[] {
+ OutputPrefixType.LEGACY,
+ OutputPrefixType.CRUNCHY,
+ OutputPrefixType.TINK,
+ OutputPrefixType.RAW
+ };
+
+ private static Ed25519Parameters.Variant variantForOutputPrefix(OutputPrefixType outputPrefixType)
+ throws GeneralSecurityException {
+ switch (outputPrefixType) {
+ case LEGACY:
+ return Ed25519Parameters.Variant.LEGACY;
+ case CRUNCHY:
+ return Ed25519Parameters.Variant.CRUNCHY;
+ case TINK:
+ return Ed25519Parameters.Variant.TINK;
+ case RAW:
+ return Ed25519Parameters.Variant.NO_PREFIX;
+ default:
+ throw new GeneralSecurityException("Unknown output prefix type: " + outputPrefixType);
+ }
+ }
+
+ /**
+ * This test computes the signature using a keyset with one key, with the custom key manager. It
+ * then verifies the Signature using normal Tink subtle Ed25519Verify.
+ */
+ @Theory
+ public void testComputeCustom_verifyBuiltIn_works(
+ @FromDataPoints("allOutputPrefixTypes") OutputPrefixType outputPrefixType) throws Exception {
+ Ed25519PublicKey protoPublicKey =
+ Ed25519PublicKey.newBuilder()
+ .setVersion(0)
+ .setKeyValue(ByteString.copyFrom(publicKeyByteArray))
+ .build();
+ Ed25519PrivateKey protoPrivateKey =
+ Ed25519PrivateKey.newBuilder()
+ .setVersion(0)
+ .setPublicKey(protoPublicKey)
+ .setKeyValue(ByteString.copyFrom(privateKeyByteArray))
+ .build();
+ KeyData keyData =
+ KeyData.newBuilder()
+ .setTypeUrl(PRIVATE_TYPE_URL)
+ .setValue(protoPrivateKey.toByteString())
+ .setKeyMaterialType(KeyMaterialType.ASYMMETRIC_PRIVATE)
+ .build();
+ Keyset keyset =
+ Keyset.newBuilder()
+ .addKey(
+ Keyset.Key.newBuilder()
+ .setKeyData(keyData)
+ .setStatus(KeyStatusType.ENABLED)
+ .setOutputPrefixType(outputPrefixType)
+ .setKeyId(0x23456789)
+ .build())
+ .setPrimaryKeyId(0x23456789)
+ .build();
+
+ KeysetHandle handle =
+ TinkProtoKeysetFormat.parseKeyset(keyset.toByteArray(), InsecureSecretKeyAccess.get());
+ PublicKeySign customSigner = handle.getPrimitive(PublicKeySign.class);
+
+ byte[] message = new byte[] {1, 2, 3};
+ byte[] signature = customSigner.sign(message);
+
+ @Nullable Integer idRequirement = outputPrefixType == OutputPrefixType.RAW ? null : 0x23456789;
+ PublicKeyVerify tinkVerifier =
+ Ed25519Verify.create(
+ com.google.crypto.tink.signature.Ed25519PublicKey.create(
+ variantForOutputPrefix(outputPrefixType),
+ Bytes.copyFrom(publicKeyByteArray),
+ idRequirement));
+
+ tinkVerifier.verify(signature, message);
+ }
+
+ /**
+ * This test computes the signature using a Tink subtle Ed25519Sign. It then verifies the
+ * Signature with a PublicKeyVerify from the custom keyset.
+ */
+ @Theory
+ public void testComputeBuiltIn_verifyCustom_works(
+ @FromDataPoints("allOutputPrefixTypes") OutputPrefixType outputPrefixType) throws Exception {
+ Ed25519PublicKey protoPublicKey =
+ Ed25519PublicKey.newBuilder()
+ .setVersion(0)
+ .setKeyValue(ByteString.copyFrom(publicKeyByteArray))
+ .build();
+ KeyData keyData =
+ KeyData.newBuilder()
+ .setTypeUrl(PUBLIC_TYPE_URL)
+ .setValue(protoPublicKey.toByteString())
+ .setKeyMaterialType(KeyMaterialType.ASYMMETRIC_PUBLIC)
+ .build();
+ Keyset keyset =
+ Keyset.newBuilder()
+ .addKey(
+ Keyset.Key.newBuilder()
+ .setKeyData(keyData)
+ .setStatus(KeyStatusType.ENABLED)
+ .setOutputPrefixType(outputPrefixType)
+ .setKeyId(0x23456789)
+ .build())
+ .setPrimaryKeyId(0x23456789)
+ .build();
+
+ KeysetHandle handle = TinkProtoKeysetFormat.parseKeysetWithoutSecret(keyset.toByteArray());
+ PublicKeyVerify customVerifier = handle.getPrimitive(PublicKeyVerify.class);
+ @Nullable Integer idRequirement = outputPrefixType == OutputPrefixType.RAW ? null : 0x23456789;
+
+ PublicKeySign tinkSigner =
+ Ed25519Sign.create(
+ com.google.crypto.tink.signature.Ed25519PrivateKey.create(
+ com.google.crypto.tink.signature.Ed25519PublicKey.create(
+ variantForOutputPrefix(outputPrefixType),
+ Bytes.copyFrom(publicKeyByteArray),
+ idRequirement),
+ SecretBytes.copyFrom(privateKeyByteArray, InsecureSecretKeyAccess.get())));
+
+ byte[] message = new byte[] {1, 2, 3};
+ byte[] signature = tinkSigner.sign(message);
+ customVerifier.verify(signature, message);
+ }
+}