// Copyright 2019 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;

import com.google.crypto.tink.annotations.Alpha;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

An object which collects all the operations which one can do on for a single key type, identified by a single KeyProto.

A KeyTypeManager manages all the operations one can do on a given KeyProto. This includes generating primitives, generating keys (if applicable), parsing and validating keys and key formats. This object is meant to be implemented, i.e., one should use it via the Registry, and not directly.

In order to implement a new key manager, one should subclass this class, setting the type parameter to the proto of the corresponding key (e.g., subclass KeyTypeManager<AesGcmKey>).

For each primitive the key manager should implement, one needs to add an argument to the constructor. The type of it should be a PrimitiveFactory<PrimitiveT, KeyT>, an object which knows how to produce primitives.

If the key manager can create new keys, one also needs to implement the method #keyFactory. In this case it needs to return an object of type KeyFactory<KeyFormatProto, KeyProtoT>, where one has to specify a proto for the key format as well.

/** * An object which collects all the operations which one can do on for a single key type, identified * by a single KeyProto. * * <p>A KeyTypeManager manages all the operations one can do on a given KeyProto. This includes * generating primitives, generating keys (if applicable), parsing and validating keys and key * formats. This object is meant to be implemented, i.e., one should use it via the {@link * Registry}, and not directly. * * <p>In order to implement a new key manager, one should subclass this class, setting the type * parameter to the proto of the corresponding key (e.g., subclass {@code * KeyTypeManager<AesGcmKey>}). * * <p>For each primitive the key manager should implement, one needs to add an argument to the * constructor. The type of it should be a {@code PrimitiveFactory<PrimitiveT, KeyT>}, an object * which knows how to produce primitives. * * <p>If the key manager can create new keys, one also needs to implement the method {@code * #keyFactory}. In this case it needs to return an object of type {@code KeyFactory<KeyFormatProto, * KeyProtoT>}, where one has to specify a proto for the key format as well. */
@Alpha public abstract class KeyTypeManager<KeyProtoT extends MessageLite> {
A PrimitiveFactory knows how to create primitives from a given key.
/** A PrimitiveFactory knows how to create primitives from a given key. */
protected abstract static class PrimitiveFactory<PrimitiveT, KeyT> { private final Class<PrimitiveT> clazz; public PrimitiveFactory(Class<PrimitiveT> clazz) { this.clazz = clazz; }
Returns the class object corresponding to the generic parameter PrimitiveT.
/** * Returns the class object corresponding to the generic parameter {@code PrimitiveT}. */
final Class<PrimitiveT> getPrimitiveClass() { return clazz; }
Creates a new instance of PrimitiveT.
/** Creates a new instance of {@code PrimitiveT}. */
public abstract PrimitiveT getPrimitive(KeyT key) throws GeneralSecurityException; } private final Class<KeyProtoT> clazz; private final Map<Class<?>, PrimitiveFactory<?, KeyProtoT>> factories; private final Class<?> firstPrimitiveClass;
Constructs a new KeyTypeManager.

Takes an arbitrary number of PrimitiveFactory objects as input. These will be used and provided via getPrimitive to the user.

Throws:
/** * Constructs a new KeyTypeManager. * * <p>Takes an arbitrary number of {@link PrimitiveFactory} objects as input. These will be used * and provided via {@link #getPrimitive} to the user. * * @throws IllegalArgumentException if two of the passed in factories produce primitives of the * same class. */
@SafeVarargs // Safe because we do not reference the array (see Effective Java ed. 3, Item 32). protected KeyTypeManager(Class<KeyProtoT> clazz, PrimitiveFactory<?, KeyProtoT>... factories) { this.clazz = clazz; Map<Class<?>, PrimitiveFactory<?, KeyProtoT>> factoriesMap = new HashMap<>(); for (PrimitiveFactory<?, KeyProtoT> factory : factories) { if (factoriesMap.containsKey(factory.getPrimitiveClass())) { throw new IllegalArgumentException( "KeyTypeManager constructed with duplicate factories for primitive " + factory.getPrimitiveClass().getCanonicalName()); } factoriesMap.put(factory.getPrimitiveClass(), factory); } if (factories.length > 0) { this.firstPrimitiveClass = factories[0].getPrimitiveClass(); } else { this.firstPrimitiveClass = Void.class; } this.factories = Collections.unmodifiableMap(factoriesMap); }
Returns the class corresponding to the key protobuffer.
/** Returns the class corresponding to the key protobuffer. */
public final Class<KeyProtoT> getKeyClass() { return clazz; }
Returns the type URL that identifies the key type of keys managed by this KeyManager.
/** Returns the type URL that identifies the key type of keys managed by this KeyManager. */
public abstract String getKeyType();
Returns the version number of this KeyManager.
/** Returns the version number of this KeyManager. */
public abstract int getVersion();
Returns the KeyMaterialType for this proto.
/** Returns the {@link KeyMaterialType} for this proto. */
public abstract KeyMaterialType keyMaterialType();
Parses a serialized key proto.

Implement as return KeyProtoT.parseFrom(byteString);.

/** * Parses a serialized key proto. * * <p>Implement as {@code return KeyProtoT.parseFrom(byteString);}. */
public abstract KeyProtoT parseKey(ByteString byteString) throws InvalidProtocolBufferException;
Checks if the given keyProto is a valid key.
Throws:
/** * Checks if the given {@code keyProto} is a valid key. * * @throws GeneralSecurityException if the passed {@code keyProto} is not valid in any way. */
public abstract void validateKey(KeyProtoT keyProto) throws GeneralSecurityException;
Creates the requested primitive.
Throws:
  • IllegalArgumentException – if the given primitiveClass is not supported (i.e., not returned by supportedPrimitives.
  • GeneralSecurityException – if the underlying factory throws a GeneralSecurityException creating the primitive.
/** * Creates the requested primitive. * * @throws java.lang.IllegalArgumentException if the given {@code primitiveClass} is not supported * (i.e., not returned by {@link #supportedPrimitives}. * @throws GeneralSecurityException if the underlying factory throws a GeneralSecurityException * creating the primitive. */
public final <P> P getPrimitive(KeyProtoT key, Class<P> primitiveClass) throws GeneralSecurityException { @SuppressWarnings("unchecked") // factories maps Class<P> to PrimitiveFactory<P, KeyProtoT>. PrimitiveFactory<P, KeyProtoT> factory = (PrimitiveFactory<P, KeyProtoT>) factories.get(primitiveClass); if (factory == null) { throw new IllegalArgumentException( "Requested primitive class " + primitiveClass.getCanonicalName() + " not supported."); } return factory.getPrimitive(key); }
Returns a set containing the supported primitives.
/** * Returns a set containing the supported primitives. */
public final Set<Class<?>> supportedPrimitives() { return factories.keySet(); }
Returns the first class object of the first supported primitive, or Class<Void> if the key manager supports no primitive at all.
/** * Returns the first class object of the first supported primitive, or {@code Class<Void>} if the * key manager supports no primitive at all. */
final Class<?> firstSupportedPrimitiveClass() { return firstPrimitiveClass; }
A KeyFactory creates new keys from a given KeyFormat.

A KeyFactory implements all the methods which are required if a KeyTypeManager should also be able to generate keys. In particular, in this case it needs to have some KeyFormat protocol buffer which can be validated, parsed, and from which a key can be generated.

/** * A {@code KeyFactory} creates new keys from a given KeyFormat. * * <p>A KeyFactory implements all the methods which are required if a KeyTypeManager should also * be able to generate keys. In particular, in this case it needs to have some KeyFormat protocol * buffer which can be validated, parsed, and from which a key can be generated. */
public abstract static class KeyFactory<KeyFormatProtoT extends MessageLite, KeyT> { private final Class<KeyFormatProtoT> clazz; public KeyFactory(Class<KeyFormatProtoT> clazz) { this.clazz = clazz; }
Returns the class corresponding to the key format protobuffer.
/** * Returns the class corresponding to the key format protobuffer. */
public final Class<KeyFormatProtoT> getKeyFormatClass() { return clazz; }
Checks if the given keyFormatProto is a valid key.
Throws:
/** * Checks if the given {@code keyFormatProto} is a valid key. * * @throws GeneralSecurityException if the passed {@code keyFormatProto} is not valid in any * way. */
public abstract void validateKeyFormat(KeyFormatProtoT keyFormatProto) throws GeneralSecurityException;
Parses a serialized key proto.

Implement as return KeyFormatProtoT.parseFrom(byteString);.

/** * Parses a serialized key proto. * * <p>Implement as {@code return KeyFormatProtoT.parseFrom(byteString);}. */
public abstract KeyFormatProtoT parseKeyFormat(ByteString byteString) throws InvalidProtocolBufferException;
Creates a new key from a given format.
/** Creates a new key from a given format. */
public abstract KeyT createKey(KeyFormatProtoT keyFormat) throws GeneralSecurityException;
Derives a new key from a given format, using the given {@param pseudoRandomness}.

Implementations need to note that the given paramter {@param pseudoRandomness} may only produce a finite amount of randomness. Hence, proper implementations will first obtain all the pseudorandom bytes needed; and only after produce the key.

While validateKeyFormat is called before this method will be called, implementations must check the version of the given {@param keyFormat}, as validateKeyFormat is also called from createKey.

Not every KeyTypeManager needs to implement this; if not implemented a GeneralSecurityException will be thrown.

/** * Derives a new key from a given format, using the given {@param pseudoRandomness}. * * <p>Implementations need to note that the given paramter {@param pseudoRandomness} may only * produce a finite amount of randomness. Hence, proper implementations will first obtain all * the pseudorandom bytes needed; and only after produce the key. * * <p>While {@link validateKeyFormat} is called before this method will be called, * implementations must check the version of the given {@param keyFormat}, as {@link * validateKeyFormat} is also called from {@link createKey}. * * <p>Not every KeyTypeManager needs to implement this; if not implemented a {@link * GeneralSecurityException} will be thrown. */
public KeyT deriveKey(KeyFormatProtoT keyFormat, InputStream pseudoRandomness) throws GeneralSecurityException { throw new GeneralSecurityException( "deriveKey not implemented for key of type " + clazz.toString()); } }
Returns the KeyFactory for this key type.

By default, this throws an UnsupportedOperationException. Hence, if an implementation does not support creating primitives, no implementation is required.

Throws:
/** * Returns the {@link KeyFactory} for this key type. * * <p>By default, this throws an UnsupportedOperationException. Hence, if an implementation does * not support creating primitives, no implementation is required. * * @throws UnsupportedOperationException if the manager does not support creating primitives. */
public KeyFactory<?, KeyProtoT> keyFactory() { throw new UnsupportedOperationException("Creating keys is not supported."); } }