1 // Copyright 2022 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.internal; 18 19 import static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug; 20 21 import com.google.crypto.tink.Key; 22 import com.google.crypto.tink.Parameters; 23 import com.google.crypto.tink.SecretKeyAccess; 24 import java.security.GeneralSecurityException; 25 import java.util.concurrent.atomic.AtomicReference; 26 import javax.annotation.Nullable; 27 28 /** 29 * A Mutable version of the {@link SerializationRegistry}. 30 * 31 * <p>This class probably shouldn't exist; it would be better if we had only the 32 * SerializationRegistry. However, at the moment, we need this, since a call to e.g. 33 * 34 * <pre> AesCmacKeyManager.register() </pre> 35 * 36 * should register such an object into a global, mutable registry. 37 */ 38 public final class MutableSerializationRegistry { createGlobalInstance()39 private static MutableSerializationRegistry createGlobalInstance() 40 throws GeneralSecurityException { 41 MutableSerializationRegistry registry = new MutableSerializationRegistry(); 42 registry.registerKeySerializer( 43 KeySerializer.create( 44 LegacyProtoKey::getSerialization, LegacyProtoKey.class, ProtoKeySerialization.class)); 45 return registry; 46 } 47 48 private static final MutableSerializationRegistry GLOBAL_INSTANCE = 49 exceptionIsBug(MutableSerializationRegistry::createGlobalInstance); 50 globalInstance()51 public static MutableSerializationRegistry globalInstance() { 52 return GLOBAL_INSTANCE; 53 } 54 55 private final AtomicReference<SerializationRegistry> registry = 56 new AtomicReference<>(new SerializationRegistry.Builder().build()); 57 MutableSerializationRegistry()58 public MutableSerializationRegistry() {} 59 60 /** 61 * Registers a key serializer for later use in {@link #serializeKey}. 62 * 63 * <p>This registers a key serializer which can later be used to serialize a key by calling {@link 64 * #serializeKey}. If a serializer for the pair {@code (KeyT, SerializationT)} has already been 65 * registered, this checks if they are the same. If they are, the call is ignored, otherwise an 66 * exception is thrown, and the object is unchanged. 67 */ 68 public synchronized <KeyT extends Key, SerializationT extends Serialization> registerKeySerializer(KeySerializer<KeyT, SerializationT> serializer)69 void registerKeySerializer(KeySerializer<KeyT, SerializationT> serializer) 70 throws GeneralSecurityException { 71 SerializationRegistry newRegistry = 72 new SerializationRegistry.Builder(registry.get()).registerKeySerializer(serializer).build(); 73 registry.set(newRegistry); 74 } 75 76 /** 77 * Registers a key parser for later use in {@link #parseKey}. 78 * 79 * <p>This registers a key serializer which can later be used to serialize a key by calling {@link 80 * #parseKey}. If a parser for the pair {@code (SerializationT, parser.getObjectIdentifier())} has 81 * already been registered, this checks if they are the same. If they are, the call is ignored, 82 * otherwise an exception is thrown, and the object is unchanged. 83 */ registerKeyParser( KeyParser<SerializationT> parser)84 public synchronized <SerializationT extends Serialization> void registerKeyParser( 85 KeyParser<SerializationT> parser) throws GeneralSecurityException { 86 SerializationRegistry newRegistry = 87 new SerializationRegistry.Builder(registry.get()).registerKeyParser(parser).build(); 88 registry.set(newRegistry); 89 } 90 91 /** 92 * Registers a key serializer for later use in {@link #serializeKey}. 93 * 94 * <p>This registers a key serializer which can later be used to serialize a key by calling {@link 95 * #serializeKey}. If a serializer for the pair {@code (KeyT, SerializationT)} has already been 96 * registered, this checks if they are the same. If they are, the call is ignored, otherwise an 97 * exception is thrown, and the object is unchanged. 98 */ 99 public synchronized <ParametersT extends Parameters, SerializationT extends Serialization> registerParametersSerializer( ParametersSerializer<ParametersT, SerializationT> serializer)100 void registerParametersSerializer( 101 ParametersSerializer<ParametersT, SerializationT> serializer) 102 throws GeneralSecurityException { 103 SerializationRegistry newRegistry = 104 new SerializationRegistry.Builder(registry.get()) 105 .registerParametersSerializer(serializer) 106 .build(); 107 registry.set(newRegistry); 108 } 109 110 /** 111 * Registers a key parser for later use in {@link #parseKey}. 112 * 113 * <p>This registers a key serializer which can later be used to serialize a key by calling {@link 114 * #parseKey}. If a parser for the pair {@code (SerializationT, parser.getObjectIdentifier())} has 115 * already been registered, this checks if they are the same. If they are, the call is ignored, 116 * otherwise an exception is thrown, and the object is unchanged. 117 */ registerParametersParser( ParametersParser<SerializationT> parser)118 public synchronized <SerializationT extends Serialization> void registerParametersParser( 119 ParametersParser<SerializationT> parser) throws GeneralSecurityException { 120 SerializationRegistry newRegistry = 121 new SerializationRegistry.Builder(registry.get()).registerParametersParser(parser).build(); 122 registry.set(newRegistry); 123 } 124 125 /** Returns true if a parser for this {@code serializedKey} has been registered. */ hasParserForKey( SerializationT serializedKey)126 public <SerializationT extends Serialization> boolean hasParserForKey( 127 SerializationT serializedKey) { 128 return registry.get().hasParserForKey(serializedKey); 129 } 130 131 /** 132 * Parses the given serialization into a Key. 133 * 134 * <p>This will look up a previously registered parser for the passed in {@code SerializationT} 135 * class, and the used object identifier (as indicated by {@code 136 * serializedKey.getObjectIdentifier()}), and then parse the object with this parsers. 137 */ parseKey( SerializationT serializedKey, @Nullable SecretKeyAccess access)138 public <SerializationT extends Serialization> Key parseKey( 139 SerializationT serializedKey, @Nullable SecretKeyAccess access) 140 throws GeneralSecurityException { 141 return registry.get().parseKey(serializedKey, access); 142 } 143 144 /** 145 * Returns a Key object from a protoKeySerialization, even if no parser has been registered. 146 * 147 * <p>Falling back is useful because we want users to be able to call {@code #getAt} even for key 148 * types for which we did not yet register a parser; in this case we simply fall back to return a 149 * LegacyProtoKey. 150 * 151 * @throws GeneralSecurityException if a parser is registered but parsing fails or required 152 * SecretKeyAccess is missing 153 */ parseKeyWithLegacyFallback( ProtoKeySerialization protoKeySerialization, @Nullable SecretKeyAccess access)154 public Key parseKeyWithLegacyFallback( 155 ProtoKeySerialization protoKeySerialization, @Nullable SecretKeyAccess access) 156 throws GeneralSecurityException { 157 if (!hasParserForKey(protoKeySerialization)) { 158 return new LegacyProtoKey(protoKeySerialization, access); 159 } 160 return parseKey(protoKeySerialization, access); 161 } 162 163 164 /** Returns true if a parser for this {@code serializedKey} has been registered. */ hasSerializerForKey( KeyT key, Class<SerializationT> serializationClass)165 public <KeyT extends Key, SerializationT extends Serialization> boolean hasSerializerForKey( 166 KeyT key, Class<SerializationT> serializationClass) { 167 return registry.get().hasSerializerForKey(key, serializationClass); 168 } 169 170 /** 171 * Serializes a given Key into a "SerializationT" object. 172 * 173 * <p>This will look up a previously registered serializer for the requested {@code 174 * SerializationT} class and the passed in key type, and then call serializeKey on the result. 175 */ serializeKey( KeyT key, Class<SerializationT> serializationClass, @Nullable SecretKeyAccess access)176 public <KeyT extends Key, SerializationT extends Serialization> SerializationT serializeKey( 177 KeyT key, Class<SerializationT> serializationClass, @Nullable SecretKeyAccess access) 178 throws GeneralSecurityException { 179 return registry.get().serializeKey(key, serializationClass, access); 180 } 181 182 /** Returns true if a parser for this {@code serializedKey} has been registered. */ hasParserForParameters( SerializationT serializedParameters)183 public <SerializationT extends Serialization> boolean hasParserForParameters( 184 SerializationT serializedParameters) { 185 return registry.get().hasParserForParameters(serializedParameters); 186 } 187 188 /** 189 * Parses the given serialization into a Parameters object. 190 * 191 * <p>This will look up a previously registered parser for the passed in {@code SerializationT} 192 * class, and the used object identifier (as indicated by {@code 193 * serializedKey.getObjectIdentifier()}), and then parse the object with this parsers. 194 */ parseParameters( SerializationT serializedParameters)195 public <SerializationT extends Serialization> Parameters parseParameters( 196 SerializationT serializedParameters) throws GeneralSecurityException { 197 return registry.get().parseParameters(serializedParameters); 198 } 199 200 /** 201 * Returns a Parameters object from a protoKeySerialization, even if no parser has been 202 * registered. 203 * 204 * <p>Falling back is useful because we need to have a parameters object even if no parser is 205 * registered (e.g. for when we create a Key from a key template/parameters name object). 206 */ parseParametersWithLegacyFallback( ProtoParametersSerialization protoParametersSerialization)207 public Parameters parseParametersWithLegacyFallback( 208 ProtoParametersSerialization protoParametersSerialization) throws GeneralSecurityException { 209 if (!hasParserForParameters(protoParametersSerialization)) { 210 return new LegacyProtoParameters(protoParametersSerialization); 211 } else { 212 return parseParameters(protoParametersSerialization); 213 } 214 } 215 216 /** Returns true if a parser for this {@code serializedKey} has been registered. */ 217 public <ParametersT extends Parameters, SerializationT extends Serialization> hasSerializerForParameters( ParametersT parameters, Class<SerializationT> serializationClass)218 boolean hasSerializerForParameters( 219 ParametersT parameters, Class<SerializationT> serializationClass) { 220 return registry.get().hasSerializerForParameters(parameters, serializationClass); 221 } 222 223 /** 224 * Serializes a given Parameters object into a "SerializationT" object. 225 * 226 * <p>This will look up a previously registered serializer for the requested {@code 227 * SerializationT} class and the passed in key type, and then call serializeKey on the result. 228 */ 229 public <ParametersT extends Parameters, SerializationT extends Serialization> serializeParameters( ParametersT parameters, Class<SerializationT> serializationClass)230 SerializationT serializeParameters( 231 ParametersT parameters, Class<SerializationT> serializationClass) 232 throws GeneralSecurityException { 233 return registry.get().serializeParameters(parameters, serializationClass); 234 } 235 } 236