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