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