xref: /aosp_15_r20/external/tink/java_src/src/main/java/com/google/crypto/tink/KeysetHandle.java (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2017 Google Inc.
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;
18 
19 import com.google.crypto.tink.annotations.Alpha;
20 import com.google.crypto.tink.internal.InternalConfiguration;
21 import com.google.crypto.tink.internal.LegacyProtoParameters;
22 import com.google.crypto.tink.internal.MutableSerializationRegistry;
23 import com.google.crypto.tink.internal.ProtoKeySerialization;
24 import com.google.crypto.tink.internal.ProtoParametersSerialization;
25 import com.google.crypto.tink.internal.RegistryConfiguration;
26 import com.google.crypto.tink.internal.TinkBugException;
27 import com.google.crypto.tink.monitoring.MonitoringAnnotations;
28 import com.google.crypto.tink.proto.EncryptedKeyset;
29 import com.google.crypto.tink.proto.KeyData;
30 import com.google.crypto.tink.proto.KeyStatusType;
31 import com.google.crypto.tink.proto.Keyset;
32 import com.google.crypto.tink.proto.KeysetInfo;
33 import com.google.crypto.tink.proto.OutputPrefixType;
34 import com.google.crypto.tink.tinkkey.KeyAccess;
35 import com.google.crypto.tink.tinkkey.KeyHandle;
36 import com.google.crypto.tink.tinkkey.internal.InternalKeyHandle;
37 import com.google.crypto.tink.tinkkey.internal.ProtoKey;
38 import com.google.errorprone.annotations.CanIgnoreReturnValue;
39 import com.google.errorprone.annotations.Immutable;
40 import com.google.protobuf.ByteString;
41 import com.google.protobuf.ExtensionRegistryLite;
42 import com.google.protobuf.InvalidProtocolBufferException;
43 import java.io.IOException;
44 import java.security.GeneralSecurityException;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Set;
50 import javax.annotation.Nullable;
51 
52 /**
53  * A KeysetHandle provides abstracted access to {@link Keyset}, to limit the exposure of actual
54  * protocol buffers that hold sensitive key material.
55  *
56  * <p>This class allows reading and writing encrypted keysets. Users that want to read or write can
57  * use the restricted API {@link CleartextKeysetHandle}. Users can also load keysets that don't
58  * contain any secret key material with {@link NoSecretKeysetHandle}.
59  *
60  * @since 1.0.0
61  */
62 public final class KeysetHandle {
63   /**
64    * Used to create new {@code KeysetHandle} objects.
65    *
66    * <p>A builder can be used to create a new {@code KeysetHandle} object. To create a builder with
67    * an empty keyset, one calls {@code KeysetHandle.newBuilder();}. To create a builder from an
68    * existing keyset, one calls {@code KeysetHandle.newBuilder(keyset);}.
69    *
70    * <p>To add a new key to a {@code Builder}, one calls {@link #addEntry} with a KeysetEntry
71    * object. Such objects can be created
72    *
73    * <ul>
74    *   <li>From a named {@link Parameters} with {@link
75    *       KeysetHandle#generateEntryFromParametersName},
76    *   <li>From a {@link Parameters} object, with {@link KeysetHandle#generateEntryFromParameters},
77    *   <li>By importing an existing key, with {@link KeysetHandle#importKey}
78    * </ul>
79    *
80    * <p>All these functions return a {@code KeysetBuilder.Entry}. It is necessary to assign an ID to
81    * a new entry by calling one of {@link Entry#withFixedId} or {@link Entry#withRandomId}. The
82    * exception is when an existing key which has an id requirement is imported (in which case the
83    * required ID is used).
84    *
85    * <p>It is possible to set the status of an entry by calling {@link Entry#setStatus}. The Status
86    * defaults to {@code ENABLED}.
87    *
88    * <p>It is possible to set whether an entry is the primary by calling {@link Entry#makePrimary}.
89    * The user must ensure that once {@link #build} is called, a primary has been set.
90    */
91   public static final class Builder {
92     private static class KeyIdStrategy {
93       private static final KeyIdStrategy RANDOM_ID = new KeyIdStrategy();
94       private final int fixedId;
95 
KeyIdStrategy()96       private KeyIdStrategy() {
97         this.fixedId = 0;
98       }
99 
KeyIdStrategy(int id)100       private KeyIdStrategy(int id) {
101         this.fixedId = id;
102       }
103 
randomId()104       private static KeyIdStrategy randomId() {
105         return RANDOM_ID;
106       }
107 
fixedId(int id)108       private static KeyIdStrategy fixedId(int id) {
109         return new KeyIdStrategy(id);
110       }
111 
getFixedId()112       private int getFixedId() {
113         return fixedId;
114       }
115     }
116 
117     /**
118      * One entry, representing a single key, in a Keyset.Builder.
119      *
120      * <p>This is the analogue of {@link Keyset.Entry} for a builder.
121      *
122      * <p>Users will have to ensure that each entry has an ID, and one entry is a primary. See
123      * {@link KeysetHandle.Builder#build} for details).
124      */
125     public static final class Entry {
126       // When "build" is called, for exactly one entry "isPrimary" needs to be set, and it should
127       // be enabled.
128       private boolean isPrimary;
129       // Set to ENABLED by default.
130       private KeyStatus keyStatus = KeyStatus.ENABLED;
131 
132       // Exactly one of key and parameters will be non-null (set in the constructor).
133       @Nullable private final Key key;
134       @Nullable private final Parameters parameters;
135       // strategy must be non-null when the keyset is built.
136       private KeyIdStrategy strategy = null;
137 
138       // The Builder which this Entry is part of. Each entry can be part of only one builder.
139       // When constructing a new entry, it is not part of any builder.
140       @Nullable private KeysetHandle.Builder builder = null;
141 
Entry(Key key)142       private Entry(Key key) {
143         this.key = key;
144         this.parameters = null;
145       }
146 
Entry(Parameters parameters)147       private Entry(Parameters parameters) {
148         this.key = null;
149         this.parameters = parameters;
150       }
151 
152       /**
153        * Marks that this entry is the primary key.
154        *
155        * <p>Other entries in the same keyset will be marked as non-primary if this Entry has already
156        * been added to a builder, otherwise they will marked as non-primary once this entry is added
157        * to a builder.
158        */
159       @CanIgnoreReturnValue
makePrimary()160       public Entry makePrimary() {
161         if (builder != null) {
162           builder.clearPrimary();
163         }
164         isPrimary = true;
165         return this;
166       }
167 
168       /** Returns whether this entry has been marked as a primary. */
isPrimary()169       public boolean isPrimary() {
170         return isPrimary;
171       }
172 
173       /** Sets the status of this entry. */
174       @CanIgnoreReturnValue
setStatus(KeyStatus status)175       public Entry setStatus(KeyStatus status) {
176         keyStatus = status;
177         return this;
178       }
179 
180       /** Returns the status of this entry. */
getStatus()181       public KeyStatus getStatus() {
182         return keyStatus;
183       }
184 
185       /** Tells Tink to assign a fixed id when this keyset is built. */
186       @CanIgnoreReturnValue
withFixedId(int id)187       public Entry withFixedId(int id) {
188         this.strategy = KeyIdStrategy.fixedId(id);
189         return this;
190       }
191 
192       /**
193        * Tells Tink to assign an unused uniform random id when this keyset is built.
194        *
195        * <p>Using {@code withRandomId} is invalid for an entry with an imported or preexisting key,
196        * which has an ID requirement.
197        *
198        * <p>If an entry is marked as {@code withRandomId}, all subsequent entries also need to be
199        * marked with {@code withRandomId}, or else calling {@code build()} will fail.
200        */
201       @CanIgnoreReturnValue
withRandomId()202       public Entry withRandomId() {
203         this.strategy = KeyIdStrategy.randomId();
204         return this;
205       }
206     }
207 
208     private final List<KeysetHandle.Builder.Entry> entries = new ArrayList<>();
209     // If set, throw this error on BUILD instead of actually building.
210     @Nullable private GeneralSecurityException errorToThrow = null;
211     private MonitoringAnnotations annotations = MonitoringAnnotations.EMPTY;
212     private boolean buildCalled = false;
213 
clearPrimary()214     private void clearPrimary() {
215       for (Builder.Entry entry : entries) {
216         entry.isPrimary = false;
217       }
218     }
219 
220     /** Adds an entry to a keyset */
221     @CanIgnoreReturnValue
addEntry(KeysetHandle.Builder.Entry entry)222     public KeysetHandle.Builder addEntry(KeysetHandle.Builder.Entry entry) {
223       if (entry.builder != null) {
224         throw new IllegalStateException("Entry has already been added to a KeysetHandle.Builder");
225       }
226       if (entry.isPrimary) {
227         clearPrimary();
228       }
229       entry.builder = this;
230       entries.add(entry);
231       return this;
232     }
233 
234     /**
235      * Sets MonitoringAnnotations. If not called, then the default value of {@link
236      * MonitoringAnnotations.EMPTY} is used.
237      *
238      * <p>When called twice, the last submitted annotations are used to create the keyset. This
239      * method is not thread-safe, and in case of multithreaded access it cannot be guaranteed which
240      * annotations get set.
241      */
242     @CanIgnoreReturnValue
243     @Alpha
setMonitoringAnnotations(MonitoringAnnotations annotations)244     public KeysetHandle.Builder setMonitoringAnnotations(MonitoringAnnotations annotations) {
245       this.annotations = annotations;
246       return this;
247     }
248 
249     /** Returns the number of entries in this builder. */
size()250     public int size() {
251       return entries.size();
252     }
253 
254     /**
255      * Returns the entry at index i, 0 <= i < size().
256      *
257      * @throws IndexOutOfBoundsException if i < 0 or i >= size();
258      */
getAt(int i)259     public Builder.Entry getAt(int i) {
260       return entries.get(i);
261     }
262 
263     /**
264      * Removes the entry at index {@code i} and returns that entry. Shifts any subsequent entries to
265      * the left (subtracts one from their indices).
266      *
267      * @deprecated Use {@link #deleteAt} or {@link #getAt} instead.
268      */
269     @CanIgnoreReturnValue
270     @Deprecated
removeAt(int i)271     public Builder.Entry removeAt(int i) {
272       return entries.remove(i);
273     }
274 
275     /**
276      * Deletes the entry at index {@code i}. Shifts any subsequent entries to the left (subtracts
277      * one from their indices).
278      */
279     @CanIgnoreReturnValue
deleteAt(int i)280     public KeysetHandle.Builder deleteAt(int i) {
281       entries.remove(i);
282       return this;
283     }
284 
checkIdAssignments(List<KeysetHandle.Builder.Entry> entries)285     private static void checkIdAssignments(List<KeysetHandle.Builder.Entry> entries)
286         throws GeneralSecurityException {
287       // We want "withRandomId"-entries after fixed id, as otherwise it might be that we randomly
288       // pick a number which is later specified as "withFixedId". Looking forward is deemed too
289       // complicated, especially if in the future we want different strategies (such as
290       // "withNextId").
291       for (int i = 0; i < entries.size() - 1; ++i) {
292         if (entries.get(i).strategy == KeyIdStrategy.RANDOM_ID
293             && entries.get(i + 1).strategy != KeyIdStrategy.RANDOM_ID) {
294           throw new GeneralSecurityException(
295               "Entries with 'withRandomId()' may only be followed by other entries with"
296                   + " 'withRandomId()'.");
297         }
298       }
299     }
300 
setErrorToThrow(GeneralSecurityException errorToThrow)301     private void setErrorToThrow(GeneralSecurityException errorToThrow) {
302       this.errorToThrow = errorToThrow;
303     }
304 
randomIdNotInSet(Set<Integer> ids)305     private static int randomIdNotInSet(Set<Integer> ids) {
306       int id = 0;
307       while (id == 0 || ids.contains(id)) {
308         id = com.google.crypto.tink.internal.Util.randKeyId();
309       }
310       return id;
311     }
312 
createKeyFromParameters( Parameters parameters, int id, KeyStatusType keyStatusType)313     private static Keyset.Key createKeyFromParameters(
314         Parameters parameters, int id, KeyStatusType keyStatusType)
315         throws GeneralSecurityException {
316       ProtoParametersSerialization serializedParameters;
317       if (parameters instanceof LegacyProtoParameters) {
318         serializedParameters = ((LegacyProtoParameters) parameters).getSerialization();
319       } else {
320         serializedParameters =
321             MutableSerializationRegistry.globalInstance()
322                 .serializeParameters(parameters, ProtoParametersSerialization.class);
323       }
324       KeyData keyData = Registry.newKeyData(serializedParameters.getKeyTemplate());
325       return Keyset.Key.newBuilder()
326           .setKeyId(id)
327           .setStatus(keyStatusType)
328           .setKeyData(keyData)
329           .setOutputPrefixType(serializedParameters.getKeyTemplate().getOutputPrefixType())
330           .build();
331     }
332 
getNextIdFromBuilderEntry( KeysetHandle.Builder.Entry builderEntry, Set<Integer> idsSoFar)333     private static int getNextIdFromBuilderEntry(
334         KeysetHandle.Builder.Entry builderEntry, Set<Integer> idsSoFar)
335         throws GeneralSecurityException {
336       int id = 0;
337       if (builderEntry.strategy == null) {
338         throw new GeneralSecurityException("No ID was set (with withFixedId or withRandomId)");
339       }
340       if (builderEntry.strategy == KeyIdStrategy.RANDOM_ID) {
341         id = randomIdNotInSet(idsSoFar);
342       } else {
343         id = builderEntry.strategy.getFixedId();
344       }
345       return id;
346     }
347 
createKeysetKeyFromBuilderEntry( KeysetHandle.Builder.Entry builderEntry, int id)348     private static Keyset.Key createKeysetKeyFromBuilderEntry(
349         KeysetHandle.Builder.Entry builderEntry, int id) throws GeneralSecurityException {
350       if (builderEntry.key == null) {
351         return createKeyFromParameters(
352             builderEntry.parameters, id, serializeStatus(builderEntry.getStatus()));
353       } else {
354         ProtoKeySerialization serializedKey =
355             MutableSerializationRegistry.globalInstance()
356                 .serializeKey(
357                     builderEntry.key, ProtoKeySerialization.class, InsecureSecretKeyAccess.get());
358         @Nullable Integer idRequirement = serializedKey.getIdRequirementOrNull();
359         if (idRequirement != null && idRequirement != id) {
360           throw new GeneralSecurityException("Wrong ID set for key with ID requirement");
361         }
362         return toKeysetKey(id, serializeStatus(builderEntry.getStatus()), serializedKey);
363       }
364     }
365 
366     /**
367      * Creates a new {@code KeysetHandle}.
368      *
369      * <p>Throws a {@code GeneralSecurityException} if one of the following holds
370      *
371      * <ul>
372      *   <li>No entry was marked as primary
373      *   <li>There is an entry in which the ID has not been set and which did not have a predefined
374      *       ID (see {@link Builder.Entry}).
375      *   <li>There is a {@code withRandomId}-entry which is followed by a non {@code
376      *       withRandomId}-entry
377      *   <li>There are two entries with the same {@code withFixedId} (including pre-existing keys
378      *       and imported keys which have an id requirement).
379      *   <li>{@code build()} was previously called for {@code withRandomId} entries,
380      *       and hence calling {@code build()} twice would result in a keyset with different
381      *       key IDs.
382      * </ul>
383      */
build()384     public KeysetHandle build() throws GeneralSecurityException {
385       if (errorToThrow != null) {
386         throw new GeneralSecurityException(
387             "Cannot build keyset due to error in original", errorToThrow);
388       }
389       if (buildCalled) {
390         throw new GeneralSecurityException("KeysetHandle.Builder#build must only be called once");
391       }
392       buildCalled = true;
393       Keyset.Builder keysetBuilder = Keyset.newBuilder();
394       Integer primaryId = null;
395 
396       checkIdAssignments(entries);
397       Set<Integer> idsSoFar = new HashSet<>();
398       for (KeysetHandle.Builder.Entry builderEntry : entries) {
399         if (builderEntry.keyStatus == null) {
400           throw new GeneralSecurityException("Key Status not set.");
401         }
402         int id = getNextIdFromBuilderEntry(builderEntry, idsSoFar);
403         if (idsSoFar.contains(id)) {
404           throw new GeneralSecurityException("Id " + id + " is used twice in the keyset");
405         }
406         idsSoFar.add(id);
407 
408         Keyset.Key keysetKey = createKeysetKeyFromBuilderEntry(builderEntry, id);
409         keysetBuilder.addKey(keysetKey);
410         if (builderEntry.isPrimary) {
411           if (primaryId != null) {
412             throw new GeneralSecurityException("Two primaries were set");
413           }
414           primaryId = id;
415         }
416       }
417       if (primaryId == null) {
418         throw new GeneralSecurityException("No primary was set");
419       }
420       keysetBuilder.setPrimaryKeyId(primaryId);
421       return KeysetHandle.fromKeysetAndAnnotations(keysetBuilder.build(), annotations);
422     }
423   }
424 
425   /**
426    * Represents a single entry in a keyset.
427    *
428    * <p>An entry in a keyset consists of a key, its ID, and the {@link KeyStatus}. In addition,
429    * there is one key marked as a primary.
430    *
431    * <p>The ID should be considered unique (though currently Tink still accepts keysets with
432    * repeated IDs). The {@code KeyStatus} tells Tink whether the key should still be used or not.
433    * There should always be exactly one key which is marked as a primary, however, at the moment
434    * Tink still accepts keysets which have none. This will be changed in the future.
435    */
436   @Alpha
437   @Immutable
438   public static final class Entry {
Entry(Key key, KeyStatus keyStatus, int id, boolean isPrimary)439     private Entry(Key key, KeyStatus keyStatus, int id, boolean isPrimary) {
440       this.key = key;
441       this.keyStatus = keyStatus;
442       this.id = id;
443       this.isPrimary = isPrimary;
444     }
445 
446     private final Key key;
447     private final KeyStatus keyStatus;
448     private final int id;
449     private final boolean isPrimary;
450     /**
451      * May return an internal class {@link com.google.crypto.tink.internal.LegacyProtoKey} in case
452      * there is no implementation of the corresponding key class yet.
453      */
getKey()454     public Key getKey() {
455       return key;
456     }
457 
getStatus()458     public KeyStatus getStatus() {
459       return keyStatus;
460     }
461 
getId()462     public int getId() {
463       return id;
464     }
465     /**
466      * Guaranteed to be true in exactly one entry.
467      *
468      * <p>Note: currently this may be false for all entries, since it is possible that keysets are
469      * parsed without a primary. In the future, such keysets will be rejected when the keyset is
470      * parsed.
471      */
isPrimary()472     public boolean isPrimary() {
473       return isPrimary;
474     }
475 
equalsEntry(Entry other)476     private boolean equalsEntry(Entry other) {
477       if (other.isPrimary != isPrimary) {
478         return false;
479       }
480       if (!other.keyStatus.equals(keyStatus)) {
481         return false;
482       }
483       if (other.id != id) {
484         return false;
485       }
486       if (!other.key.equalsKey(key)) {
487         return false;
488       }
489       return true;
490     }
491   }
492 
parseStatus(KeyStatusType in)493   private static KeyStatus parseStatus(KeyStatusType in) throws GeneralSecurityException {
494     switch (in) {
495       case ENABLED:
496         return KeyStatus.ENABLED;
497       case DISABLED:
498         return KeyStatus.DISABLED;
499       case DESTROYED:
500         return KeyStatus.DESTROYED;
501       default:
502         throw new GeneralSecurityException("Unknown key status");
503     }
504   }
505 
serializeStatus(KeyStatus in)506   private static KeyStatusType serializeStatus(KeyStatus in) {
507     if (KeyStatus.ENABLED.equals(in)) {
508       return KeyStatusType.ENABLED;
509     }
510     if (KeyStatus.DISABLED.equals(in)) {
511       return KeyStatusType.DISABLED;
512     }
513     if (KeyStatus.DESTROYED.equals(in)) {
514       return KeyStatusType.DESTROYED;
515     }
516     throw new IllegalStateException("Unknown key status");
517   }
518 
toKeysetKey( int id, KeyStatusType status, ProtoKeySerialization protoKeySerialization)519   private static Keyset.Key toKeysetKey(
520       int id, KeyStatusType status, ProtoKeySerialization protoKeySerialization) {
521     return Keyset.Key.newBuilder()
522         .setKeyData(
523             KeyData.newBuilder()
524                 .setTypeUrl(protoKeySerialization.getTypeUrl())
525                 .setValue(protoKeySerialization.getValue())
526                 .setKeyMaterialType(protoKeySerialization.getKeyMaterialType()))
527         .setStatus(status)
528         .setKeyId(id)
529         .setOutputPrefixType(protoKeySerialization.getOutputPrefixType())
530         .build();
531   }
532 
533   /**
534    * Returns an immutable list of key objects for this keyset.
535    *
536    * <p>If a status is unparseable or parsing of a key fails, there will be "null" in the
537    * corresponding entry.
538    */
getEntriesFromKeyset(Keyset keyset)539   private static List<Entry> getEntriesFromKeyset(Keyset keyset) {
540     List<Entry> result = new ArrayList<>(keyset.getKeyCount());
541     for (Keyset.Key protoKey : keyset.getKeyList()) {
542       int id = protoKey.getKeyId();
543       ProtoKeySerialization protoKeySerialization = toProtoKeySerialization(protoKey);
544       try {
545         Key key =
546             MutableSerializationRegistry.globalInstance()
547                 .parseKeyWithLegacyFallback(protoKeySerialization, InsecureSecretKeyAccess.get());
548         result.add(
549             new KeysetHandle.Entry(
550                 key, parseStatus(protoKey.getStatus()), id, id == keyset.getPrimaryKeyId()));
551       } catch (GeneralSecurityException e) {
552         result.add(null);
553       }
554     }
555     return Collections.unmodifiableList(result);
556   }
557 
toProtoKeySerialization(Keyset.Key protoKey)558   private static ProtoKeySerialization toProtoKeySerialization(Keyset.Key protoKey) {
559     int id = protoKey.getKeyId();
560     @Nullable
561     Integer idRequirement = protoKey.getOutputPrefixType() == OutputPrefixType.RAW ? null : id;
562     try {
563       return ProtoKeySerialization.create(
564           protoKey.getKeyData().getTypeUrl(),
565           protoKey.getKeyData().getValue(),
566           protoKey.getKeyData().getKeyMaterialType(),
567           protoKey.getOutputPrefixType(),
568           idRequirement);
569     } catch (GeneralSecurityException e) {
570       // Cannot happen -- this only happens if the idRequirement doesn't match OutputPrefixType
571       throw new TinkBugException("Creating a protokey serialization failed", e);
572     }
573   }
574 
entryByIndex(int i)575   private KeysetHandle.Entry entryByIndex(int i) {
576     if (entries.get(i) == null) {
577       // This may happen if a keyset without status makes it here; or if a key has a parser
578       // registered but parsing fails. We should reject such keysets earlier instead.
579       throw new IllegalStateException(
580           "Keyset-Entry at position " + i + " has wrong status or key parsing failed");
581     }
582     return entries.get(i);
583   }
584 
585   /**
586    * Creates a new entry with a fixed key.
587    *
588    * <p>If the Key has an IdRequirement, the default will be fixed to this ID. Otherwise, the user
589    * has to specify the ID to be used and call one of {@code withFixedId(i)} or {@code
590    * withRandomId()} on the returned entry.
591    */
importKey(Key key)592   public static KeysetHandle.Builder.Entry importKey(Key key) {
593     KeysetHandle.Builder.Entry importedEntry = new KeysetHandle.Builder.Entry(key);
594     @Nullable Integer requirement = key.getIdRequirementOrNull();
595     if (requirement != null) {
596       importedEntry.withFixedId(requirement);
597     }
598     return importedEntry;
599   }
600 
601   /**
602    * Creates a new entry with Status "ENABLED" and a new key created from the named parameters. No
603    * ID is set.
604    *
605    * <p>{@code namedParameters} is the key template name that fully specifies the parameters, e.g.
606    * "DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_128_GCM".
607    */
generateEntryFromParametersName(String namedParameters)608   public static KeysetHandle.Builder.Entry generateEntryFromParametersName(String namedParameters)
609       throws GeneralSecurityException {
610     if (!Registry.keyTemplateMap().containsKey(namedParameters)) {
611       throw new GeneralSecurityException("cannot find key template: " + namedParameters);
612     }
613     KeyTemplate template = Registry.keyTemplateMap().get(namedParameters);
614     ProtoParametersSerialization serialization =
615         ProtoParametersSerialization.create(template.getProto());
616     Parameters parameters =
617         MutableSerializationRegistry.globalInstance()
618             .parseParametersWithLegacyFallback(serialization);
619     return new KeysetHandle.Builder.Entry(parameters);
620   }
621 
622   /**
623    * Creates a new entry with Status "ENABLED" and a new key created from the parameters. No ID is
624    * set.
625    */
generateEntryFromParameters(Parameters parameters)626   public static KeysetHandle.Builder.Entry generateEntryFromParameters(Parameters parameters) {
627     return new KeysetHandle.Builder.Entry(parameters);
628   }
629 
630   private final Keyset keyset;
631   /* Note: this should be List<@Nullable Entry>; but since we use the Nullable annotation from
632    * javax.annotation it is not possible to do this.
633    *
634    * Contains all entries; but if either parsing the status or the key failed, contains null.
635    */
636   private final List<Entry> entries;
637   private final MonitoringAnnotations annotations;
638 
KeysetHandle(Keyset keyset, List<Entry> entries)639   private KeysetHandle(Keyset keyset, List<Entry> entries) {
640     this.keyset = keyset;
641     this.entries = entries;
642     this.annotations = MonitoringAnnotations.EMPTY;
643   }
644 
KeysetHandle( Keyset keyset, List<Entry> entries, MonitoringAnnotations annotations)645   private KeysetHandle(
646       Keyset keyset, List<Entry> entries, MonitoringAnnotations annotations) {
647     this.keyset = keyset;
648     this.entries = entries;
649     this.annotations = annotations;
650   }
651 
652   /**
653    * @return a new {@link KeysetHandle} from a {@code keyset}.
654    * @throws GeneralSecurityException if the keyset is null or empty.
655    */
fromKeyset(Keyset keyset)656   static final KeysetHandle fromKeyset(Keyset keyset) throws GeneralSecurityException {
657     assertEnoughKeyMaterial(keyset);
658     List<Entry> entries = getEntriesFromKeyset(keyset);
659 
660     return new KeysetHandle(keyset, entries);
661   }
662 
663   /**
664    * @return a new {@link KeysetHandle} from a {@code keyset} and {@code annotations}.
665    * @throws GeneralSecurityException if the keyset is null or empty.
666    */
fromKeysetAndAnnotations( Keyset keyset, MonitoringAnnotations annotations)667   static final KeysetHandle fromKeysetAndAnnotations(
668       Keyset keyset, MonitoringAnnotations annotations) throws GeneralSecurityException {
669     assertEnoughKeyMaterial(keyset);
670     List<Entry> entries = getEntriesFromKeyset(keyset);
671     return new KeysetHandle(keyset, entries, annotations);
672   }
673 
674   /** Returns the actual keyset data. */
getKeyset()675   Keyset getKeyset() {
676     return keyset;
677   }
678 
679   /** Creates a new builder. */
newBuilder()680   public static Builder newBuilder() {
681     return new Builder();
682   }
683 
684   /** Creates a new builder, initially containing all entries from {@code handle}. */
newBuilder(KeysetHandle handle)685   public static Builder newBuilder(KeysetHandle handle) {
686     Builder builder = new Builder();
687     for (int i = 0; i < handle.size(); ++i) {
688       // Currently, this can be null (as old APIs do not check validity e.g. in {@link #fromKeyset})
689       @Nullable KeysetHandle.Entry entry = handle.entries.get(i);
690       if (entry == null) {
691         builder.setErrorToThrow(
692             new GeneralSecurityException(
693                 "Keyset-Entry in original keyset at position "
694                     + i
695                     + " has wrong status or key parsing failed"));
696         break;
697       }
698 
699       KeysetHandle.Builder.Entry builderEntry =
700           importKey(entry.getKey()).withFixedId(entry.getId());
701       builderEntry.setStatus(entry.getStatus());
702       if (entry.isPrimary()) {
703         builderEntry.makePrimary();
704       }
705       builder.addEntry(builderEntry);
706     }
707     return builder;
708   }
709 
710   /**
711    * Returns the unique entry where isPrimary() = true and getStatus() = ENABLED.
712    *
713    * <p>Note: currently this may throw IllegalStateException, since it is possible that keysets are
714    * parsed without a primary. In the future, such keysets will be rejected when the keyset is
715    * parsed.
716    */
getPrimary()717   public KeysetHandle.Entry getPrimary() {
718     for (int i = 0; i < keyset.getKeyCount(); ++i) {
719       if (keyset.getKey(i).getKeyId() == keyset.getPrimaryKeyId()) {
720         KeysetHandle.Entry result = entryByIndex(i);
721         if (result.getStatus() != KeyStatus.ENABLED) {
722           throw new IllegalStateException("Keyset has primary which isn't enabled");
723         }
724         return result;
725       }
726     }
727     throw new IllegalStateException("Keyset has no primary");
728   }
729 
730   /** Returns the size of this keyset. */
size()731   public int size() {
732     return keyset.getKeyCount();
733   }
734 
735   /**
736    * Returns the entry at index i. The order is preserved and depends on the order at which the
737    * entries were inserted when the KeysetHandle was built.
738    *
739    * <p>Currently, this may throw "IllegalStateException" in case the status entry of the Key in the
740    * keyset was wrongly set. In this case, we call this KeysetHandle invalid. In the future, Tink
741    * will throw at parsing time in this case, and we will not have invalid KeysetHandles.
742    *
743    * <p>If you want to ensure that this does not throw an IllegalStateException, please first
744    * re-parse the KeysetHandle: {@code KeysetHandle guaranteedValid =
745    * KeysetHandle.newBuilder(maybeInvalidHandle).build();} (This would throw a {@code
746    * GeneralSecurityException} if the {@code maybeInvalidHandle} handle is invalid).
747    *
748    * @throws IndexOutOfBoundsException if i < 0 or i >= size();
749    */
getAt(int i)750   public KeysetHandle.Entry getAt(int i) {
751     if (i < 0 || i >= size()) {
752       throw new IndexOutOfBoundsException("Invalid index " + i + " for keyset of size " + size());
753     }
754     return entryByIndex(i);
755   }
756 
757   /**
758    * Returns the keyset data as a list of {@link KeyHandle}s.
759    *
760    * <p>Please do not use this function in new code. Instead, use {@link #getAt}.
761    *
762    * @deprecated Use "getAt" instead.
763    */
764   @Deprecated
getKeys()765   public List<KeyHandle> getKeys() {
766     ArrayList<KeyHandle> result = new ArrayList<>();
767     for (Keyset.Key key : keyset.getKeyList()) {
768       KeyData keyData = key.getKeyData();
769       result.add(
770           new InternalKeyHandle(
771               new ProtoKey(keyData, KeyTemplate.fromProto(key.getOutputPrefixType())),
772               key.getStatus(),
773               key.getKeyId()));
774     }
775     return Collections.unmodifiableList(result);
776   }
777 
778   /**
779    * Returns the {@link com.google.crypto.tink.proto.KeysetInfo} that doesn't contain actual key
780    * material.
781    */
getKeysetInfo()782   public KeysetInfo getKeysetInfo() {
783     return Util.getKeysetInfo(keyset);
784   }
785 
786   /**
787    * Generates a new {@link KeysetHandle} that contains a single fresh key generated key with the
788    * given {@code Parameters} object.
789    *
790    * @throws GeneralSecurityException if no generation method for the given {@code parameters} has
791    *     been registered.
792    */
generateNew(Parameters parameters)793   public static final KeysetHandle generateNew(Parameters parameters)
794       throws GeneralSecurityException {
795     return KeysetHandle.newBuilder()
796         .addEntry(KeysetHandle.generateEntryFromParameters(parameters).withRandomId().makePrimary())
797         .build();
798   }
799 
800   /**
801    * Generates a new {@link KeysetHandle} that contains a single fresh key generated according to
802    * {@code keyTemplate}.
803    *
804    * <p>Please do not use this function. Instead, use {@link #generateNew(Parameters)}.
805    *
806    * <p>For existing usage, try to use refaster
807    * https://github.com/tink-crypto/tink-java/tree/main/tools/refaster to replace usage
808    * automatically. This will replaces calls {@code KeysetHandle.generateNew(XYZKeyTemplates.ABC);}
809    * with {@code KeysetHandle.generateNew(PredefinedXYZParameters.ABC);} which is a NO-OP.
810    *
811    * <p>If this is not possible, please inline the function in your code.
812    *
813    * @throws GeneralSecurityException if the key template is invalid.
814    */
generateNew(com.google.crypto.tink.proto.KeyTemplate keyTemplate)815   public static final KeysetHandle generateNew(com.google.crypto.tink.proto.KeyTemplate keyTemplate)
816       throws GeneralSecurityException {
817     return generateNew(TinkProtoParametersFormat.parse(keyTemplate.toByteArray()));
818   }
819 
820   /**
821    * Generates a new {@link KeysetHandle} that contains a single fresh key generated according to
822    * {@code keyTemplate}.
823    *
824    * <p>Please do not use this function. Instead, inline it: replace calls with {@code
825    * generateNew(t)} with {@code generateNew(t.toParameters())}.
826    *
827    * @throws GeneralSecurityException if the key template is invalid.
828    */
generateNew(KeyTemplate keyTemplate)829   public static final KeysetHandle generateNew(KeyTemplate keyTemplate)
830       throws GeneralSecurityException {
831     return generateNew(keyTemplate.toParameters());
832   }
833 
834   /**
835    * Returns a {@code KeysetHandle} that contains the single {@code KeyHandle} passed as input.
836    *
837    * @deprecated Use {@link KeysetHandle.Builder.addEntry} instead.
838    */
839   @Deprecated
createFromKey(KeyHandle keyHandle, KeyAccess access)840   public static final KeysetHandle createFromKey(KeyHandle keyHandle, KeyAccess access)
841       throws GeneralSecurityException {
842     KeysetManager km = KeysetManager.withEmptyKeyset().add(keyHandle);
843     km.setPrimary(km.getKeysetHandle().getKeysetInfo().getKeyInfo(0).getKeyId());
844     return km.getKeysetHandle();
845   }
846 
847   /**
848    * Tries to create a {@link KeysetHandle} from an encrypted keyset obtained via {@code reader}.
849    *
850    * <p>Users that need to load cleartext keysets can use {@link CleartextKeysetHandle}.
851    *
852    * @return a new {@link KeysetHandle} from {@code encryptedKeysetProto} that was encrypted with
853    *     {@code masterKey}
854    * @throws GeneralSecurityException if cannot decrypt the keyset or it doesn't contain encrypted
855    *     key material
856    */
read(KeysetReader reader, Aead masterKey)857   public static final KeysetHandle read(KeysetReader reader, Aead masterKey)
858       throws GeneralSecurityException, IOException {
859     return readWithAssociatedData(reader, masterKey, new byte[0]);
860   }
861 
862   /**
863    * Tries to create a {@link KeysetHandle} from an encrypted keyset obtained via {@code reader},
864    * using the provided associated data.
865    *
866    * <p>Users that need to load cleartext keysets can use {@link CleartextKeysetHandle}.
867    *
868    * @return a new {@link KeysetHandle} from {@code encryptedKeysetProto} that was encrypted with
869    *     {@code masterKey}
870    * @throws GeneralSecurityException if cannot decrypt the keyset or it doesn't contain encrypted
871    *     key material
872    */
readWithAssociatedData( KeysetReader reader, Aead masterKey, byte[] associatedData)873   public static final KeysetHandle readWithAssociatedData(
874       KeysetReader reader, Aead masterKey, byte[] associatedData)
875       throws GeneralSecurityException, IOException {
876     EncryptedKeyset encryptedKeyset = reader.readEncrypted();
877     assertEnoughEncryptedKeyMaterial(encryptedKeyset);
878     return KeysetHandle.fromKeyset(decrypt(encryptedKeyset, masterKey, associatedData));
879   }
880 
881   /**
882    * Tries to create a {@link KeysetHandle} from a keyset, obtained via {@code reader}, which
883    * contains no secret key material.
884    *
885    * <p>This can be used to load public keysets or envelope encryption keysets. Users that need to
886    * load cleartext keysets can use {@link CleartextKeysetHandle}.
887    *
888    * @return a new {@link KeysetHandle} from {@code serialized} that is a serialized {@link Keyset}
889    * @throws GeneralSecurityException if the keyset is invalid
890    */
891   @SuppressWarnings("UnusedException")
readNoSecret(KeysetReader reader)892   public static final KeysetHandle readNoSecret(KeysetReader reader)
893       throws GeneralSecurityException, IOException {
894     byte[] serializedKeyset;
895     try {
896       serializedKeyset = reader.read().toByteArray();
897     } catch (InvalidProtocolBufferException e) {
898       // Do not propagate InvalidProtocolBufferException to guarantee no key material is leaked
899       throw new GeneralSecurityException("invalid keyset");
900     }
901     return readNoSecret(serializedKeyset);
902   }
903 
904   /**
905    * Tries to create a {@link KeysetHandle} from a serialized keyset which contains no secret key
906    * material.
907    *
908    * <p>This can be used to load public keysets or envelope encryption keysets. Users that need to
909    * load cleartext keysets can use {@link CleartextKeysetHandle}.
910    *
911    * <p>Note: new code should call {@code TinkProtoKeysetFormat(serialized)} instead.
912    *
913    * @return a new {@link KeysetHandle} from {@code serialized} that is a serialized {@link Keyset}
914    * @throws GeneralSecurityException if the keyset is invalid
915    */
916   @SuppressWarnings("UnusedException")
readNoSecret(final byte[] serialized)917   public static final KeysetHandle readNoSecret(final byte[] serialized)
918       throws GeneralSecurityException {
919     try {
920       Keyset keyset = Keyset.parseFrom(serialized, ExtensionRegistryLite.getEmptyRegistry());
921       assertNoSecretKeyMaterial(keyset);
922       return KeysetHandle.fromKeyset(keyset);
923     } catch (InvalidProtocolBufferException e) {
924       // Do not propagate InvalidProtocolBufferException to guarantee no key material is leaked
925       throw new GeneralSecurityException("invalid keyset");
926     }
927   }
928 
929   /** Serializes, encrypts with {@code masterKey} and writes the keyset to {@code outputStream}. */
write(KeysetWriter keysetWriter, Aead masterKey)930   public void write(KeysetWriter keysetWriter, Aead masterKey)
931       throws GeneralSecurityException, IOException {
932     writeWithAssociatedData(keysetWriter, masterKey, new byte[0]);
933   }
934 
935   /**
936    * Serializes, encrypts with {@code masterKey} and writes the keyset to {@code outputStream} using
937    * the provided associated data.
938    */
writeWithAssociatedData( KeysetWriter keysetWriter, Aead masterKey, byte[] associatedData)939   public void writeWithAssociatedData(
940       KeysetWriter keysetWriter, Aead masterKey, byte[] associatedData)
941       throws GeneralSecurityException, IOException {
942     EncryptedKeyset encryptedKeyset = encrypt(keyset, masterKey, associatedData);
943     keysetWriter.write(encryptedKeyset);
944     return;
945   }
946 
947   /**
948    * Tries to write to {@code writer} this keyset which must not contain any secret key material.
949    *
950    * <p>This can be used to persist public keysets or envelope encryption keysets. Users that need
951    * to persist cleartext keysets can use {@link CleartextKeysetHandle}.
952    *
953    * @throws GeneralSecurityException if the keyset contains any secret key material
954    */
writeNoSecret(KeysetWriter writer)955   public void writeNoSecret(KeysetWriter writer) throws GeneralSecurityException, IOException {
956     assertNoSecretKeyMaterial(keyset);
957     writer.write(keyset);
958     return;
959   }
960 
961   /** Encrypts the keyset with the {@link Aead} master key. */
962   @SuppressWarnings("UnusedException")
encrypt(Keyset keyset, Aead masterKey, byte[] associatedData)963   private static EncryptedKeyset encrypt(Keyset keyset, Aead masterKey, byte[] associatedData)
964       throws GeneralSecurityException {
965     byte[] encryptedKeyset = masterKey.encrypt(keyset.toByteArray(), associatedData);
966     // Check if we can decrypt, to detect errors
967     try {
968       final Keyset keyset2 =
969           Keyset.parseFrom(
970               masterKey.decrypt(encryptedKeyset, associatedData),
971               ExtensionRegistryLite.getEmptyRegistry());
972       if (!keyset2.equals(keyset)) {
973         throw new GeneralSecurityException("cannot encrypt keyset");
974       }
975     } catch (InvalidProtocolBufferException e) {
976       // Do not propagate InvalidProtocolBufferException to guarantee no key material is leaked
977       throw new GeneralSecurityException("invalid keyset, corrupted key material");
978     }
979     return EncryptedKeyset.newBuilder()
980         .setEncryptedKeyset(ByteString.copyFrom(encryptedKeyset))
981         .setKeysetInfo(Util.getKeysetInfo(keyset))
982         .build();
983   }
984 
985   /** Decrypts the encrypted keyset with the {@link Aead} master key. */
986   @SuppressWarnings("UnusedException")
decrypt( EncryptedKeyset encryptedKeyset, Aead masterKey, byte[] associatedData)987   private static Keyset decrypt(
988       EncryptedKeyset encryptedKeyset, Aead masterKey, byte[] associatedData)
989       throws GeneralSecurityException {
990     try {
991       Keyset keyset =
992           Keyset.parseFrom(
993               masterKey.decrypt(encryptedKeyset.getEncryptedKeyset().toByteArray(), associatedData),
994               ExtensionRegistryLite.getEmptyRegistry());
995       // check emptiness here too, in case the encrypted keys unwrapped to nothing?
996       assertEnoughKeyMaterial(keyset);
997       return keyset;
998     } catch (InvalidProtocolBufferException e) {
999       // Do not propagate InvalidProtocolBufferException to guarantee no key material is leaked
1000       throw new GeneralSecurityException("invalid keyset, corrupted key material");
1001     }
1002   }
1003 
1004   /**
1005    * If the managed keyset contains private keys, returns a {@link KeysetHandle} of the public keys.
1006    *
1007    * @throws GeneralSecurityException if the managed keyset is null or if it contains any
1008    *     non-private keys.
1009    */
getPublicKeysetHandle()1010   public KeysetHandle getPublicKeysetHandle() throws GeneralSecurityException {
1011     if (keyset == null) {
1012       throw new GeneralSecurityException("cleartext keyset is not available");
1013     }
1014     Keyset.Builder keysetBuilder = Keyset.newBuilder();
1015     for (Keyset.Key key : keyset.getKeyList()) {
1016       KeyData keyData = createPublicKeyData(key.getKeyData());
1017       keysetBuilder.addKey(key.toBuilder().setKeyData(keyData).build());
1018     }
1019     keysetBuilder.setPrimaryKeyId(keyset.getPrimaryKeyId());
1020     return KeysetHandle.fromKeyset(keysetBuilder.build());
1021   }
1022 
createPublicKeyData(KeyData privateKeyData)1023   private static KeyData createPublicKeyData(KeyData privateKeyData)
1024       throws GeneralSecurityException {
1025     if (privateKeyData.getKeyMaterialType() != KeyData.KeyMaterialType.ASYMMETRIC_PRIVATE) {
1026       throw new GeneralSecurityException("The keyset contains a non-private key");
1027     }
1028     KeyData publicKeyData =
1029         Registry.getPublicKeyData(privateKeyData.getTypeUrl(), privateKeyData.getValue());
1030     validate(publicKeyData);
1031     return publicKeyData;
1032   }
1033 
1034   @SuppressWarnings("deprecation")
validate(KeyData keyData)1035   private static void validate(KeyData keyData) throws GeneralSecurityException {
1036     // This will throw GeneralSecurityException if the keyData is invalid.
1037     // Note: this calls a deprecated function to validate the "KeyData" proto. The usage of this
1038     // deprecated function is unfortunate. However, in the end we simply want to remove this call.
1039     // The only usage of this is in "getPublicKeysetHandle". This should go away, in principle
1040     // the code of getPublicKeysetHandle should simply look at each entry, cast each key to
1041     // {@link PrivateKey} (throw a GeneralSecurityException if this fails), call getPublicKey()
1042     // and insert the result into a new keyset with the same ID and status, then return the result.
1043     // If done like this, there is no reason to validate the returned Key object.
1044     // (However, also note that this particular call here isn't very problematic; the problematic
1045     // part of Registry.getPrimitive is that it misuses generics, but here we just want any Object).
1046     Object unused = Registry.getPrimitive(keyData);
1047   }
1048 
1049   /**
1050    * Extracts and returns the string representation of the {@link
1051    * com.google.crypto.tink.proto.KeysetInfo} of the managed keyset.
1052    */
1053   @SuppressWarnings("LiteProtoToString") // main purpose of toString is for debugging
1054   @Override
toString()1055   public String toString() {
1056     return getKeysetInfo().toString();
1057   }
1058 
1059   /**
1060    * Validates that {@code keyset} doesn't contain any secret key material.
1061    *
1062    * @throws GeneralSecurityException if {@code keyset} contains secret key material.
1063    */
assertNoSecretKeyMaterial(Keyset keyset)1064   private static void assertNoSecretKeyMaterial(Keyset keyset) throws GeneralSecurityException {
1065     for (Keyset.Key key : keyset.getKeyList()) {
1066       if (key.getKeyData().getKeyMaterialType() == KeyData.KeyMaterialType.UNKNOWN_KEYMATERIAL
1067           || key.getKeyData().getKeyMaterialType() == KeyData.KeyMaterialType.SYMMETRIC
1068           || key.getKeyData().getKeyMaterialType() == KeyData.KeyMaterialType.ASYMMETRIC_PRIVATE) {
1069         throw new GeneralSecurityException(
1070             String.format(
1071                 "keyset contains key material of type %s for type url %s",
1072                 key.getKeyData().getKeyMaterialType().name(), key.getKeyData().getTypeUrl()));
1073       }
1074     }
1075   }
1076 
1077   /**
1078    * Validates that a keyset handle contains enough key material to build a keyset on.
1079    *
1080    * @throws GeneralSecurityException if the validation fails
1081    */
assertEnoughKeyMaterial(Keyset keyset)1082   private static void assertEnoughKeyMaterial(Keyset keyset) throws GeneralSecurityException {
1083     if (keyset == null || keyset.getKeyCount() <= 0) {
1084       throw new GeneralSecurityException("empty keyset");
1085     }
1086   }
1087 
1088   /**
1089    * Validates that an encrypted keyset contains enough key material to build a keyset on.
1090    *
1091    * @throws GeneralSecurityException if the validation fails
1092    */
assertEnoughEncryptedKeyMaterial(EncryptedKeyset keyset)1093   private static void assertEnoughEncryptedKeyMaterial(EncryptedKeyset keyset)
1094       throws GeneralSecurityException {
1095     if (keyset == null || keyset.getEncryptedKeyset().size() == 0) {
1096       throw new GeneralSecurityException("empty keyset");
1097     }
1098   }
1099 
1100   /** Allows us to have a name {@code B} for the base primitive. */
getPrimitiveWithKnownInputPrimitive( InternalConfiguration config, Class<P> classObject, Class<B> inputPrimitiveClassObject)1101   private <B, P> P getPrimitiveWithKnownInputPrimitive(
1102       InternalConfiguration config, Class<P> classObject, Class<B> inputPrimitiveClassObject)
1103       throws GeneralSecurityException {
1104     Util.validateKeyset(keyset);
1105     PrimitiveSet.Builder<B> builder = PrimitiveSet.newBuilder(inputPrimitiveClassObject);
1106     builder.setAnnotations(annotations);
1107     for (int i = 0; i < size(); ++i) {
1108       Keyset.Key protoKey = keyset.getKey(i);
1109       if (protoKey.getStatus().equals(KeyStatusType.ENABLED)) {
1110         @Nullable
1111         B primitive = getLegacyPrimitiveOrNull(config, protoKey, inputPrimitiveClassObject);
1112         @Nullable B fullPrimitive = null;
1113         // Entries.get(i) may be null (if the status is invalid in the proto, or parsing failed).
1114         if (entries.get(i) != null) {
1115           fullPrimitive =
1116               getFullPrimitiveOrNull(config, entries.get(i).getKey(), inputPrimitiveClassObject);
1117         }
1118         if (fullPrimitive == null && primitive == null) {
1119           throw new GeneralSecurityException(
1120               "Unable to get primitive "
1121                   + inputPrimitiveClassObject
1122                   + " for key of type "
1123                   + protoKey.getKeyData().getTypeUrl());
1124         }
1125         if (protoKey.getKeyId() == keyset.getPrimaryKeyId()) {
1126           builder.addPrimaryFullPrimitiveAndOptionalPrimitive(fullPrimitive, primitive, protoKey);
1127         } else {
1128           builder.addFullPrimitiveAndOptionalPrimitive(fullPrimitive, primitive, protoKey);
1129         }
1130       }
1131     }
1132     return config.wrap(builder.build(), classObject);
1133   }
1134 
1135   /**
1136    * Returns a primitive from this keyset using the provided {@link Configuration} to create
1137    * resources used in creating the primitive.
1138    */
getPrimitive(Configuration configuration, Class<P> targetClassObject)1139   public <P> P getPrimitive(Configuration configuration, Class<P> targetClassObject)
1140       throws GeneralSecurityException {
1141     if (!(configuration instanceof InternalConfiguration)) {
1142       throw new GeneralSecurityException(
1143           "Currently only subclasses of InternalConfiguration are accepted");
1144     }
1145     InternalConfiguration internalConfig = (InternalConfiguration) configuration;
1146     Class<?> inputPrimitiveClassObject = internalConfig.getInputPrimitiveClass(targetClassObject);
1147     if (inputPrimitiveClassObject == null) {
1148       throw new GeneralSecurityException("No wrapper found for " + targetClassObject.getName());
1149     }
1150     return getPrimitiveWithKnownInputPrimitive(
1151         internalConfig, targetClassObject, inputPrimitiveClassObject);
1152   }
1153 
1154   /**
1155    * Returns a primitive from this keyset, using the global registry to create resources creating
1156    * the primitive.
1157    */
getPrimitive(Class<P> targetClassObject)1158   public <P> P getPrimitive(Class<P> targetClassObject) throws GeneralSecurityException {
1159     return getPrimitive(RegistryConfiguration.get(), targetClassObject);
1160   }
1161 
1162   /**
1163    * Searches the keyset to find the primary key of this {@code KeysetHandle}, and returns the key
1164    * wrapped in a {@code KeyHandle}.
1165    *
1166    * <p>Please do not use this function in new code. Instead, use {@link #getPrimary}.
1167    *
1168    * @deprecated Use {@link #getPrimary} instead.
1169    */
1170   @Deprecated
primaryKey()1171   public KeyHandle primaryKey() throws GeneralSecurityException {
1172     int primaryKeyId = keyset.getPrimaryKeyId();
1173     for (Keyset.Key key : keyset.getKeyList()) {
1174       if (key.getKeyId() == primaryKeyId) {
1175         return new InternalKeyHandle(
1176             new ProtoKey(key.getKeyData(), KeyTemplate.fromProto(key.getOutputPrefixType())),
1177             key.getStatus(),
1178             key.getKeyId());
1179       }
1180     }
1181     throw new GeneralSecurityException("No primary key found in keyset.");
1182   }
1183 
1184   @Nullable
getLegacyPrimitiveOrNull( InternalConfiguration config, Keyset.Key key, Class<B> inputPrimitiveClassObject)1185   private static <B> B getLegacyPrimitiveOrNull(
1186       InternalConfiguration config, Keyset.Key key, Class<B> inputPrimitiveClassObject)
1187       throws GeneralSecurityException {
1188     try {
1189       return config.getLegacyPrimitive(key.getKeyData(), inputPrimitiveClassObject);
1190     } catch (GeneralSecurityException e) {
1191       if (e.getMessage().contains("No key manager found for key type ")
1192           || e.getMessage().contains(" not supported by key manager of type ")) {
1193         // Ignoring because the key may not have a corresponding legacy key manager.
1194         return null;
1195       }
1196       // Otherwise the error is likely legit. Do not swallow.
1197       throw e;
1198     } catch (UnsupportedOperationException e) {
1199       // We are using the new configuration that doesn't work with proto keys.
1200       return null;
1201     }
1202   }
1203 
1204   @Nullable
getFullPrimitiveOrNull( InternalConfiguration config, Key key, Class<B> inputPrimitiveClassObject)1205   private <B> B getFullPrimitiveOrNull(
1206       InternalConfiguration config, Key key, Class<B> inputPrimitiveClassObject)
1207       throws GeneralSecurityException {
1208     try {
1209       return config.getPrimitive(key, inputPrimitiveClassObject);
1210     } catch (GeneralSecurityException e) {
1211       // Ignoring because the key may not yet have a corresponding class.
1212       // TODO(lizatretyakova): stop ignoring when all key classes are migrated from protos.
1213       return null;
1214     }
1215   }
1216 
1217   /**
1218    * Returns true if this keyset is equal to {@code other}, ignoring monitoring annotations.
1219    *
1220    * <p>Note: this may return false even if the keysets represent the same set of functions. For
1221    * example, this can happen if the keys store zero-byte padding of a {@link java.math.BigInteger},
1222    * which are irrelevant to the function computed. Currently, keysets can also be invalid in which
1223    * case this will return false.
1224    */
equalsKeyset(KeysetHandle other)1225   public boolean equalsKeyset(KeysetHandle other) {
1226     if (size() != other.size()) {
1227       return false;
1228     }
1229     boolean primaryFound = false;
1230     for (int i = 0; i < size(); ++i) {
1231       Entry thisEntry = entries.get(i);
1232       Entry otherEntry = other.entries.get(i);
1233       if (thisEntry == null) {
1234         // Can only happen for invalid keyset
1235         return false;
1236       }
1237       if (otherEntry == null) {
1238         // Can only happen for invalid keyset
1239         return false;
1240       }
1241       if (!thisEntry.equalsEntry(otherEntry)) {
1242         return false;
1243       }
1244       primaryFound |= thisEntry.isPrimary;
1245     }
1246     if (!primaryFound) {
1247       return false;
1248     }
1249     return true;
1250   }
1251 }
1252