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