xref: /aosp_15_r20/external/tink/java_src/src/main/java/com/google/crypto/tink/internal/PrimitiveRegistry.java (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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