blob: 6709ebd74da1e7c4d3e32911ba3422d6065a8b80 [file] [log] [blame]
// Copyright 2022 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.internal;
import com.google.crypto.tink.Key;
import com.google.crypto.tink.Parameters;
import com.google.crypto.tink.SecretKeyAccess;
import java.security.GeneralSecurityException;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;
/**
* A Mutable version of the {@link SerializationRegistry}.
*
* <p>This class probably shouldn't exist; it would be better if we had only the
* SerializationRegistry. However, at the moment, we need this, since a call to e.g.
*
* <pre> AesCmacKeyManager.register() </pre>
*
* should register such an object into a global, mutable registry.
*/
public final class MutableSerializationRegistry {
private static final MutableSerializationRegistry GLOBAL_INSTANCE =
new MutableSerializationRegistry();
public static MutableSerializationRegistry globalInstance() {
return GLOBAL_INSTANCE;
}
private final AtomicReference<SerializationRegistry> registry =
new AtomicReference<>(new SerializationRegistry.Builder().build());
public MutableSerializationRegistry() {}
/**
* Registers a key serializer for later use in {@link #serializeKey}.
*
* <p>This registers a key serializer which can later be used to serialize a key by calling {@link
* #serializeKey}. If a serializer for the pair {@code (KeyT, SerializationT)} has already been
* registered, this checks if they are the same. If they are, the call is ignored, otherwise an
* exception is thrown, and the object is unchanged.
*/
public synchronized <KeyT extends Key, SerializationT extends Serialization>
void registerKeySerializer(KeySerializer<KeyT, SerializationT> serializer)
throws GeneralSecurityException {
SerializationRegistry newRegistry =
new SerializationRegistry.Builder(registry.get()).registerKeySerializer(serializer).build();
registry.set(newRegistry);
}
/**
* Registers a key parser for later use in {@link #parseKey}.
*
* <p>This registers a key serializer which can later be used to serialize a key by calling {@link
* #parseKey}. If a parser for the pair {@code (SerializationT, parser.getObjectIdentifier())} has
* already been registered, this checks if they are the same. If they are, the call is ignored,
* otherwise an exception is thrown, and the object is unchanged.
*/
public synchronized <SerializationT extends Serialization> void registerKeyParser(
KeyParser<SerializationT> parser) throws GeneralSecurityException {
SerializationRegistry newRegistry =
new SerializationRegistry.Builder(registry.get()).registerKeyParser(parser).build();
registry.set(newRegistry);
}
/**
* Registers a key serializer for later use in {@link #serializeKey}.
*
* <p>This registers a key serializer which can later be used to serialize a key by calling {@link
* #serializeKey}. If a serializer for the pair {@code (KeyT, SerializationT)} has already been
* registered, this checks if they are the same. If they are, the call is ignored, otherwise an
* exception is thrown, and the object is unchanged.
*/
public synchronized <ParametersT extends Parameters, SerializationT extends Serialization>
void registerParametersSerializer(
ParametersSerializer<ParametersT, SerializationT> serializer)
throws GeneralSecurityException {
SerializationRegistry newRegistry =
new SerializationRegistry.Builder(registry.get())
.registerParametersSerializer(serializer)
.build();
registry.set(newRegistry);
}
/**
* Registers a key parser for later use in {@link #parseKey}.
*
* <p>This registers a key serializer which can later be used to serialize a key by calling {@link
* #parseKey}. If a parser for the pair {@code (SerializationT, parser.getObjectIdentifier())} has
* already been registered, this checks if they are the same. If they are, the call is ignored,
* otherwise an exception is thrown, and the object is unchanged.
*/
public synchronized <SerializationT extends Serialization> void registerParametersParser(
ParametersParser<SerializationT> parser) throws GeneralSecurityException {
SerializationRegistry newRegistry =
new SerializationRegistry.Builder(registry.get()).registerParametersParser(parser).build();
registry.set(newRegistry);
}
/**
* Parses the given serialization into a Key.
*
* <p>This will look up a previously registered parser for the passed in {@code SerializationT}
* class, and the used object identifier (as indicated by {@code
* serializedKey.getObjectIdentifier()}), and then parse the object with this parsers.
*/
public <SerializationT extends Serialization> Key parseKey(
SerializationT serializedKey, @Nullable SecretKeyAccess access)
throws GeneralSecurityException {
return registry.get().parseKey(serializedKey, access);
}
/**
* Returns a Key object from a protoKeySerialization, even if no parser has been registered.
*
* <p>Falling back is useful because we want users to be able to call {@code #getAt} even for key
* types for which we did not yet register a parser; in this case we simply fall back to return a
* LegacyProtoKey.
*
* <p>This always requires SecretKeyAccess. This guarantees that it cannot fail (every
* ProtoKeySerialization can be parsed into a LegacyProtoKey).
*/
public Key parseKeyWithLegacyFallback(
ProtoKeySerialization protoKeySerialization, SecretKeyAccess access) {
if (access == null) {
throw new NullPointerException("access cannot be null");
}
try {
return parseKey(protoKeySerialization, access);
} catch (GeneralSecurityException e) {
try {
return new LegacyProtoKey(protoKeySerialization, access);
} catch (GeneralSecurityException e2) {
// Cannot happen -- this only throws if we have no access.
throw new TinkBugException("Creating a LegacyProtoKey failed", e2);
}
}
}
/**
* Serializes a given Key into a "SerializationT" object.
*
* <p>This will look up a previously registered serializer for the requested {@code
* SerializationT} class and the passed in key type, and then call serializeKey on the result.
*/
public <KeyT extends Key, SerializationT extends Serialization> SerializationT serializeKey(
KeyT key, Class<SerializationT> serializationClass, @Nullable SecretKeyAccess access)
throws GeneralSecurityException {
return registry.get().serializeKey(key, serializationClass, access);
}
/**
* Parses the given serialization into a Parameters object.
*
* <p>This will look up a previously registered parser for the passed in {@code SerializationT}
* class, and the used object identifier (as indicated by {@code
* serializedKey.getObjectIdentifier()}), and then parse the object with this parsers.
*/
public <SerializationT extends Serialization> Parameters parseParameters(
SerializationT serializedParameters) throws GeneralSecurityException {
return registry.get().parseParameters(serializedParameters);
}
/**
* Returns a Parameters object from a protoKeySerialization, even if no parser has been
* registered.
*
* <p>Falling back is useful because we need to have a parameters object even if no parser is
* registered (e.g. for when we create a Key from a key template/parameters name object).
*/
public Parameters parseParametersWithLegacyFallback(
ProtoParametersSerialization protoParametersSerialization) {
try {
return parseParameters(protoParametersSerialization);
} catch (GeneralSecurityException e) {
return new LegacyProtoParameters(protoParametersSerialization);
}
}
/**
* Serializes a given Parameters object into a "SerializationT" object.
*
* <p>This will look up a previously registered serializer for the requested {@code
* SerializationT} class and the passed in key type, and then call serializeKey on the result.
*/
public <ParametersT extends Parameters, SerializationT extends Serialization>
SerializationT serializeParameters(
ParametersT parameters, Class<SerializationT> serializationClass)
throws GeneralSecurityException {
return registry.get().serializeParameters(parameters, serializationClass);
}
}