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 com.google.crypto.tink.Key; 20 import com.google.crypto.tink.PrimitiveSet; 21 import com.google.crypto.tink.PrimitiveWrapper; 22 import com.google.errorprone.annotations.CanIgnoreReturnValue; 23 import java.security.GeneralSecurityException; 24 import java.util.HashMap; 25 import java.util.Map; 26 import java.util.Objects; 27 28 /** 29 * Allows registering {@code PrimitiveConstructor} objects, and creating primitives with those 30 * objects. 31 */ 32 public class PrimitiveRegistry { 33 private final Map<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>> primitiveConstructorMap; 34 private final Map<Class<?>, PrimitiveWrapper<?, ?>> primitiveWrapperMap; 35 36 /** Allows building PrimitiveRegistry objects. */ 37 public static final class Builder { 38 private final Map<PrimitiveConstructorIndex, PrimitiveConstructor<?, ?>> 39 primitiveConstructorMap; 40 private final Map<Class<?>, PrimitiveWrapper<?, ?>> primitiveWrapperMap; 41 Builder()42 private Builder() { 43 primitiveConstructorMap = new HashMap<>(); 44 primitiveWrapperMap = new HashMap<>(); 45 } 46 Builder(PrimitiveRegistry registry)47 private Builder(PrimitiveRegistry registry) { 48 primitiveConstructorMap = new HashMap<>(registry.primitiveConstructorMap); 49 primitiveWrapperMap = new HashMap<>(registry.primitiveWrapperMap); 50 } 51 52 /** 53 * Registers a primitive constructor for later use in {@link #getPrimitive}. 54 * 55 * <p>This registers a primitive constructor which can later be used to create a primitive 56 * by calling {@link #getPrimitive}. If a constructor for the pair {@code (KeyT, PrimitiveT)} 57 * has already been registered, this checks if they are the same. If they are, the call is 58 * ignored, otherwise an exception is thrown. 59 */ 60 @CanIgnoreReturnValue registerPrimitiveConstructor( PrimitiveConstructor<KeyT, PrimitiveT> primitiveConstructor)61 public <KeyT extends Key, PrimitiveT> Builder registerPrimitiveConstructor( 62 PrimitiveConstructor<KeyT, PrimitiveT> primitiveConstructor) 63 throws GeneralSecurityException { 64 if (primitiveConstructor == null) { 65 throw new NullPointerException("primitive constructor must be non-null"); 66 } 67 PrimitiveConstructorIndex index = 68 new PrimitiveConstructorIndex( 69 primitiveConstructor.getKeyClass(), primitiveConstructor.getPrimitiveClass()); 70 if (primitiveConstructorMap.containsKey(index)) { 71 PrimitiveConstructor<?, ?> existingConstructor = primitiveConstructorMap.get(index); 72 if (!existingConstructor.equals(primitiveConstructor) 73 || !primitiveConstructor.equals(existingConstructor)) { 74 throw new GeneralSecurityException( 75 "Attempt to register non-equal PrimitiveConstructor object for already existing" 76 + " object of type: " 77 + index); 78 } 79 } else { 80 primitiveConstructorMap.put(index, primitiveConstructor); 81 } 82 return this; 83 } 84 85 @CanIgnoreReturnValue registerPrimitiveWrapper( PrimitiveWrapper<InputPrimitiveT, WrapperPrimitiveT> wrapper)86 public <InputPrimitiveT, WrapperPrimitiveT> Builder registerPrimitiveWrapper( 87 PrimitiveWrapper<InputPrimitiveT, WrapperPrimitiveT> wrapper) 88 throws GeneralSecurityException { 89 if (wrapper == null) { 90 throw new NullPointerException("wrapper must be non-null"); 91 } 92 Class<WrapperPrimitiveT> wrapperClassObject = wrapper.getPrimitiveClass(); 93 if (primitiveWrapperMap.containsKey(wrapperClassObject)) { 94 PrimitiveWrapper<?, ?> existingPrimitiveWrapper = 95 primitiveWrapperMap.get(wrapperClassObject); 96 if (!existingPrimitiveWrapper.equals(wrapper) 97 || !wrapper.equals(existingPrimitiveWrapper)) { 98 throw new GeneralSecurityException( 99 "Attempt to register non-equal PrimitiveWrapper object or input class object for" 100 + " already existing object of type" 101 + wrapperClassObject); 102 } 103 } else { 104 primitiveWrapperMap.put(wrapperClassObject, wrapper); 105 } 106 return this; 107 } 108 build()109 public PrimitiveRegistry build() { 110 return new PrimitiveRegistry(this); 111 } 112 } 113 builder()114 public static Builder builder() { 115 return new Builder(); 116 } 117 builder(PrimitiveRegistry registry)118 public static Builder builder(PrimitiveRegistry registry) { 119 return new Builder(registry); 120 } 121 PrimitiveRegistry(Builder builder)122 private PrimitiveRegistry(Builder builder) { 123 primitiveConstructorMap = new HashMap<>(builder.primitiveConstructorMap); 124 primitiveWrapperMap = new HashMap<>(builder.primitiveWrapperMap); 125 } 126 127 /** 128 * Creates a primitive from a given key. 129 * 130 * <p>This will look up a previously registered constructor for the given pair of {@code (KeyT, 131 * PrimitiveT)}, and, if successful, use the registered PrimitiveConstructor object to create the 132 * requested primitive. Throws on a failed lookup, or if the primitive construction threw. 133 */ getPrimitive( KeyT key, Class<PrimitiveT> primitiveClass)134 public <KeyT extends Key, PrimitiveT> PrimitiveT getPrimitive( 135 KeyT key, Class<PrimitiveT> primitiveClass) throws GeneralSecurityException { 136 PrimitiveConstructorIndex index = new PrimitiveConstructorIndex(key.getClass(), primitiveClass); 137 if (!primitiveConstructorMap.containsKey(index)) { 138 throw new GeneralSecurityException("No PrimitiveConstructor for " + index + " available"); 139 } 140 @SuppressWarnings("unchecked") // We know we only insert like this. 141 PrimitiveConstructor<KeyT, PrimitiveT> primitiveConstructor = 142 (PrimitiveConstructor<KeyT, PrimitiveT>) primitiveConstructorMap.get(index); 143 return primitiveConstructor.constructPrimitive(key); 144 } 145 getInputPrimitiveClass(Class<?> wrapperClassObject)146 public Class<?> getInputPrimitiveClass(Class<?> wrapperClassObject) 147 throws GeneralSecurityException { 148 if (!primitiveWrapperMap.containsKey(wrapperClassObject)) { 149 throw new GeneralSecurityException( 150 "No input primitive class for " + wrapperClassObject + " available"); 151 } 152 return primitiveWrapperMap.get(wrapperClassObject).getInputPrimitiveClass(); 153 } 154 wrap( PrimitiveSet<InputPrimitiveT> primitives, Class<WrapperPrimitiveT> wrapperClassObject)155 public <InputPrimitiveT, WrapperPrimitiveT> WrapperPrimitiveT wrap( 156 PrimitiveSet<InputPrimitiveT> primitives, Class<WrapperPrimitiveT> wrapperClassObject) 157 throws GeneralSecurityException { 158 if (!primitiveWrapperMap.containsKey(wrapperClassObject)) { 159 throw new GeneralSecurityException( 160 "No wrapper found for " + wrapperClassObject); 161 } 162 @SuppressWarnings("unchecked") // We know this is how this map is organized. 163 PrimitiveWrapper<?, WrapperPrimitiveT> wrapper = 164 (PrimitiveWrapper<?, WrapperPrimitiveT>) 165 primitiveWrapperMap.get(wrapperClassObject); 166 if (!primitives.getPrimitiveClass().equals(wrapper.getInputPrimitiveClass()) 167 || !wrapper.getInputPrimitiveClass().equals(primitives.getPrimitiveClass())) { 168 throw new GeneralSecurityException( 169 "Input primitive type of the wrapper doesn't match the type of primitives in the provided" 170 + " PrimitiveSet"); 171 } 172 @SuppressWarnings("unchecked") // The check above ensured this. 173 PrimitiveWrapper<InputPrimitiveT, WrapperPrimitiveT> typedWrapper = 174 (PrimitiveWrapper<InputPrimitiveT, WrapperPrimitiveT>) wrapper; 175 return typedWrapper.wrap(primitives); 176 } 177 178 private static final class PrimitiveConstructorIndex { 179 private final Class<?> keyClass; 180 private final Class<?> primitiveClass; 181 PrimitiveConstructorIndex(Class<?> keyClass, Class<?> primitiveClass)182 private PrimitiveConstructorIndex(Class<?> keyClass, Class<?> primitiveClass) { 183 this.keyClass = keyClass; 184 this.primitiveClass = primitiveClass; 185 } 186 187 @Override equals(Object o)188 public boolean equals(Object o) { 189 if (!(o instanceof PrimitiveConstructorIndex)) { 190 return false; 191 } 192 PrimitiveConstructorIndex other = (PrimitiveConstructorIndex) o; 193 return other.keyClass.equals(keyClass) && other.primitiveClass.equals(primitiveClass); 194 } 195 196 @Override hashCode()197 public int hashCode() { 198 return Objects.hash(keyClass, primitiveClass); 199 } 200 201 @Override toString()202 public String toString() { 203 return keyClass.getSimpleName() + " with primitive type: " + primitiveClass.getSimpleName(); 204 } 205 } 206 } 207