• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.signature;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.crypto.tink.InsecureSecretKeyAccess;
22 import com.google.crypto.tink.KeysetHandle;
23 import com.google.crypto.tink.Parameters;
24 import com.google.crypto.tink.PublicKeySign;
25 import com.google.crypto.tink.PublicKeyVerify;
26 import com.google.crypto.tink.Registry;
27 import com.google.crypto.tink.RegistryConfiguration;
28 import com.google.crypto.tink.TinkProtoKeysetFormat;
29 import com.google.crypto.tink.TinkProtoParametersFormat;
30 import com.google.crypto.tink.proto.Ed25519KeyFormat;
31 import com.google.crypto.tink.proto.Ed25519PrivateKey;
32 import com.google.crypto.tink.proto.Ed25519PublicKey;
33 import com.google.crypto.tink.proto.KeyData;
34 import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
35 import com.google.crypto.tink.proto.KeyStatusType;
36 import com.google.crypto.tink.proto.KeyTemplate;
37 import com.google.crypto.tink.proto.Keyset;
38 import com.google.crypto.tink.proto.OutputPrefixType;
39 import com.google.crypto.tink.signature.internal.testing.LegacyPublicKeySignKeyManager;
40 import com.google.crypto.tink.signature.internal.testing.LegacyPublicKeyVerifyKeyManager;
41 import com.google.crypto.tink.subtle.Ed25519Sign;
42 import com.google.crypto.tink.subtle.Ed25519Verify;
43 import com.google.crypto.tink.subtle.Hex;
44 import com.google.crypto.tink.util.Bytes;
45 import com.google.crypto.tink.util.SecretBytes;
46 import com.google.protobuf.ByteString;
47 import com.google.protobuf.ExtensionRegistryLite;
48 import java.security.GeneralSecurityException;
49 import java.util.Set;
50 import java.util.TreeSet;
51 import javax.annotation.Nullable;
52 import org.junit.BeforeClass;
53 import org.junit.Test;
54 import org.junit.experimental.theories.DataPoints;
55 import org.junit.experimental.theories.FromDataPoints;
56 import org.junit.experimental.theories.Theories;
57 import org.junit.experimental.theories.Theory;
58 import org.junit.runner.RunWith;
59 
60 /**
61  * This test attempts to test the case where a user registers their own key type with
62  * Registry.registerKeyManager() and then uses it.
63  */
64 @RunWith(Theories.class)
65 public final class KeyManagerIntegrationTest {
66   private static final String PRIVATE_TYPE_URL = "type.googleapis.com/custom.Ed25519PrivateKey";
67   private static final String PUBLIC_TYPE_URL = "type.googleapis.com/custom.Ed25519PublicKey";
68 
69   private static byte[] publicKeyByteArray;
70   private static byte[] privateKeyByteArray;
71 
72   @BeforeClass
setUpClass()73   public static void setUpClass() throws Exception {
74     // We register Tink and key manger, as a user would typically do if they add their own key type.
75     SignatureConfig.register();
76     // Register the key managers the user would register. These have type URLs PRIVATE_TYPE_URL and
77     // PUBLIC_TYPE_URL, and interpret the keys as Ed25519PrivateKey and Ed25519PublicKey exactly
78     // as Tink would.
79     Registry.registerKeyManager(new LegacyPublicKeySignKeyManager(), true);
80     Registry.registerKeyManager(new LegacyPublicKeyVerifyKeyManager(), false);
81 
82     publicKeyByteArray =
83         Hex.decode("ea42941a6dc801484390b2955bc7376d172eeb72640a54e5b50c95efa2fc6ad8");
84     privateKeyByteArray =
85         Hex.decode("9cac7d19aeecc563a3dff7bcae0fbbbc28087b986c49a3463077dd5281437e81");
86   }
87 
88   @Test
testGetPublicKeyset_works()89   public void testGetPublicKeyset_works() throws Exception {
90     Ed25519PublicKey protoPublicKey =
91         Ed25519PublicKey.newBuilder()
92             .setVersion(0)
93             .setKeyValue(ByteString.copyFrom(publicKeyByteArray))
94             .build();
95     Ed25519PrivateKey protoPrivateKey =
96         Ed25519PrivateKey.newBuilder()
97             .setVersion(0)
98             .setPublicKey(protoPublicKey)
99             .setKeyValue(ByteString.copyFrom(privateKeyByteArray))
100             .build();
101     KeyData keyData =
102         KeyData.newBuilder()
103             .setTypeUrl(PRIVATE_TYPE_URL)
104             .setValue(protoPrivateKey.toByteString())
105             .setKeyMaterialType(KeyMaterialType.ASYMMETRIC_PRIVATE)
106             .build();
107     Keyset keyset =
108         Keyset.newBuilder()
109             .addKey(
110                 Keyset.Key.newBuilder()
111                     .setKeyData(keyData)
112                     .setStatus(KeyStatusType.ENABLED)
113                     .setOutputPrefixType(OutputPrefixType.TINK)
114                     .setKeyId(0x23456789)
115                     .build())
116             .setPrimaryKeyId(0x23456789)
117             .build();
118 
119     KeysetHandle handle =
120         TinkProtoKeysetFormat.parseKeyset(keyset.toByteArray(), InsecureSecretKeyAccess.get());
121     KeysetHandle publicHandle = handle.getPublicKeysetHandle();
122 
123     Keyset publicKeyset =
124         Keyset.parseFrom(
125             TinkProtoKeysetFormat.serializeKeysetWithoutSecret(publicHandle),
126             ExtensionRegistryLite.getEmptyRegistry());
127 
128     assertThat(publicKeyset.getPrimaryKeyId()).isEqualTo(0x23456789);
129     assertThat(publicKeyset.getKeyCount()).isEqualTo(1);
130     assertThat(publicKeyset.getKey(0).getKeyId()).isEqualTo(0x23456789);
131     assertThat(publicKeyset.getKey(0).getStatus()).isEqualTo(KeyStatusType.ENABLED);
132     assertThat(publicKeyset.getKey(0).getOutputPrefixType()).isEqualTo(OutputPrefixType.TINK);
133     assertThat(publicKeyset.getKey(0).getKeyData().getTypeUrl()).isEqualTo(PUBLIC_TYPE_URL);
134     assertThat(publicKeyset.getKey(0).getKeyData().getKeyMaterialType())
135         .isEqualTo(KeyMaterialType.ASYMMETRIC_PUBLIC);
136     assertThat(
137             Ed25519PublicKey.parseFrom(
138                 publicKeyset.getKey(0).getKeyData().getValue(),
139                 ExtensionRegistryLite.getEmptyRegistry()))
140         .isEqualTo(protoPublicKey);
141   }
142 
143   @DataPoints("allOutputPrefixTypes")
144   public static final OutputPrefixType[] OUTPUT_PREFIX_TYPES =
145       new OutputPrefixType[] {
146         OutputPrefixType.LEGACY,
147         OutputPrefixType.CRUNCHY,
148         OutputPrefixType.TINK,
149         OutputPrefixType.RAW
150       };
151 
variantForOutputPrefix(OutputPrefixType outputPrefixType)152   private static Ed25519Parameters.Variant variantForOutputPrefix(OutputPrefixType outputPrefixType)
153       throws GeneralSecurityException {
154     switch (outputPrefixType) {
155       case LEGACY:
156         return Ed25519Parameters.Variant.LEGACY;
157       case CRUNCHY:
158         return Ed25519Parameters.Variant.CRUNCHY;
159       case TINK:
160         return Ed25519Parameters.Variant.TINK;
161       case RAW:
162         return Ed25519Parameters.Variant.NO_PREFIX;
163       default:
164         throw new GeneralSecurityException("Unknown output prefix type: " + outputPrefixType);
165     }
166   }
167 
168   /**
169    * This test computes the signature using a keyset with one key, with the custom key manager. It
170    * then verifies the Signature using normal Tink subtle Ed25519Verify.
171    */
172   @Theory
testComputeCustom_verifyBuiltIn_works( @romDataPoints"allOutputPrefixTypes") OutputPrefixType outputPrefixType)173   public void testComputeCustom_verifyBuiltIn_works(
174       @FromDataPoints("allOutputPrefixTypes") OutputPrefixType outputPrefixType) throws Exception {
175     Ed25519PublicKey protoPublicKey =
176         Ed25519PublicKey.newBuilder()
177             .setVersion(0)
178             .setKeyValue(ByteString.copyFrom(publicKeyByteArray))
179             .build();
180     Ed25519PrivateKey protoPrivateKey =
181         Ed25519PrivateKey.newBuilder()
182             .setVersion(0)
183             .setPublicKey(protoPublicKey)
184             .setKeyValue(ByteString.copyFrom(privateKeyByteArray))
185             .build();
186     KeyData keyData =
187         KeyData.newBuilder()
188             .setTypeUrl(PRIVATE_TYPE_URL)
189             .setValue(protoPrivateKey.toByteString())
190             .setKeyMaterialType(KeyMaterialType.ASYMMETRIC_PRIVATE)
191             .build();
192     Keyset keyset =
193         Keyset.newBuilder()
194             .addKey(
195                 Keyset.Key.newBuilder()
196                     .setKeyData(keyData)
197                     .setStatus(KeyStatusType.ENABLED)
198                     .setOutputPrefixType(outputPrefixType)
199                     .setKeyId(0x23456789)
200                     .build())
201             .setPrimaryKeyId(0x23456789)
202             .build();
203 
204     KeysetHandle handle =
205         TinkProtoKeysetFormat.parseKeyset(keyset.toByteArray(), InsecureSecretKeyAccess.get());
206     PublicKeySign customSigner =
207         handle.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class);
208 
209     byte[] message = new byte[] {1, 2, 3};
210     byte[] signature = customSigner.sign(message);
211 
212     @Nullable Integer idRequirement = outputPrefixType == OutputPrefixType.RAW ? null : 0x23456789;
213     PublicKeyVerify tinkVerifier =
214         Ed25519Verify.create(
215             com.google.crypto.tink.signature.Ed25519PublicKey.create(
216                 variantForOutputPrefix(outputPrefixType),
217                 Bytes.copyFrom(publicKeyByteArray),
218                 idRequirement));
219 
220     tinkVerifier.verify(signature, message);
221   }
222 
223   /**
224    * This test computes the signature using a Tink subtle Ed25519Sign. It then verifies the
225    * Signature with a PublicKeyVerify from the custom keyset.
226    */
227   @Theory
testComputeBuiltIn_verifyCustom_works( @romDataPoints"allOutputPrefixTypes") OutputPrefixType outputPrefixType)228   public void testComputeBuiltIn_verifyCustom_works(
229       @FromDataPoints("allOutputPrefixTypes") OutputPrefixType outputPrefixType) throws Exception {
230     Ed25519PublicKey protoPublicKey =
231         Ed25519PublicKey.newBuilder()
232             .setVersion(0)
233             .setKeyValue(ByteString.copyFrom(publicKeyByteArray))
234             .build();
235     KeyData keyData =
236         KeyData.newBuilder()
237             .setTypeUrl(PUBLIC_TYPE_URL)
238             .setValue(protoPublicKey.toByteString())
239             .setKeyMaterialType(KeyMaterialType.ASYMMETRIC_PUBLIC)
240             .build();
241     Keyset keyset =
242         Keyset.newBuilder()
243             .addKey(
244                 Keyset.Key.newBuilder()
245                     .setKeyData(keyData)
246                     .setStatus(KeyStatusType.ENABLED)
247                     .setOutputPrefixType(outputPrefixType)
248                     .setKeyId(0x23456789)
249                     .build())
250             .setPrimaryKeyId(0x23456789)
251             .build();
252 
253     KeysetHandle handle = TinkProtoKeysetFormat.parseKeysetWithoutSecret(keyset.toByteArray());
254     PublicKeyVerify customVerifier =
255         handle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class);
256     @Nullable Integer idRequirement = outputPrefixType == OutputPrefixType.RAW ? null : 0x23456789;
257 
258     PublicKeySign tinkSigner =
259         Ed25519Sign.create(
260             com.google.crypto.tink.signature.Ed25519PrivateKey.create(
261                 com.google.crypto.tink.signature.Ed25519PublicKey.create(
262                     variantForOutputPrefix(outputPrefixType),
263                     Bytes.copyFrom(publicKeyByteArray),
264                     idRequirement),
265                 SecretBytes.copyFrom(privateKeyByteArray, InsecureSecretKeyAccess.get())));
266 
267     byte[] message = new byte[] {1, 2, 3};
268     byte[] signature = tinkSigner.sign(message);
269     customVerifier.verify(signature, message);
270   }
271 
272   @Theory
testKeyGeneration_givesNewKeys_works( @romDataPoints"allOutputPrefixTypes") OutputPrefixType outputPrefixType)273   public void testKeyGeneration_givesNewKeys_works(
274       @FromDataPoints("allOutputPrefixTypes") OutputPrefixType outputPrefixType) throws Exception {
275 
276     KeyTemplate protoKeyTemplate =
277         KeyTemplate.newBuilder()
278             .setOutputPrefixType(outputPrefixType)
279             .setTypeUrl(PRIVATE_TYPE_URL)
280             .setValue(Ed25519KeyFormat.getDefaultInstance().toByteString())
281             .build();
282     Parameters parameters = TinkProtoParametersFormat.parse(protoKeyTemplate.toByteArray());
283 
284     Set<String> keys = new TreeSet<>();
285     int numKeys = 20;
286 
287     for (int i = 0; i < numKeys; i++) {
288       KeysetHandle handle =
289           KeysetHandle.newBuilder()
290               .addEntry(
291                   KeysetHandle.generateEntryFromParameters(parameters)
292                       .withFixedId(0x88117722)
293                       .makePrimary())
294               .build();
295 
296       Keyset keyset =
297           Keyset.parseFrom(
298               TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()),
299               ExtensionRegistryLite.getEmptyRegistry());
300       assertThat(keyset.getPrimaryKeyId()).isEqualTo(0x88117722);
301       assertThat(keyset.getKeyCount()).isEqualTo(1);
302       assertThat(keyset.getKey(0).getKeyId()).isEqualTo(0x88117722);
303       assertThat(keyset.getKey(0).getStatus()).isEqualTo(KeyStatusType.ENABLED);
304       assertThat(keyset.getKey(0).getOutputPrefixType()).isEqualTo(outputPrefixType);
305       assertThat(keyset.getKey(0).getKeyData().getTypeUrl()).isEqualTo(PRIVATE_TYPE_URL);
306       assertThat(keyset.getKey(0).getKeyData().getKeyMaterialType())
307           .isEqualTo(KeyMaterialType.ASYMMETRIC_PRIVATE);
308       Ed25519PrivateKey privateKey =
309           Ed25519PrivateKey.parseFrom(
310               keyset.getKey(0).getKeyData().getValue(), ExtensionRegistryLite.getEmptyRegistry());
311       keys.add(Hex.encode(privateKey.getKeyValue().toByteArray()));
312     }
313     assertThat(keys).hasSize(numKeys);
314   }
315 }
316