1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 package com.google.crypto.tink.tinkkey; 17 18 import static com.google.crypto.tink.internal.KeyTemplateProtoConverter.getOutputPrefixType; 19 20 import com.google.crypto.tink.KeyManager; 21 import com.google.crypto.tink.KeyTemplate; 22 import com.google.crypto.tink.KeyTemplate.OutputPrefixType; 23 import com.google.crypto.tink.TinkProtoParametersFormat; 24 import com.google.crypto.tink.internal.KeyManagerRegistry; 25 import com.google.crypto.tink.internal.Util; 26 import com.google.crypto.tink.proto.KeyData; 27 import com.google.crypto.tink.tinkkey.internal.ProtoKey; 28 import com.google.errorprone.annotations.Immutable; 29 import com.google.protobuf.ExtensionRegistryLite; 30 import com.google.protobuf.InvalidProtocolBufferException; 31 import java.security.GeneralSecurityException; 32 33 /** 34 * Wraps a {@link TinkKey} and enforces access to the underlying {@link TinkKey} with {@link 35 * KeyAccess}. Specifically, if the underlying {@link TinkKey} has a secret, then one can only get 36 * it with a {@link SecretKeyAccess} instance. 37 * 38 * <p>Do not use this in new code. Instead, use {@link com.google.crypto.tink.Key} and 39 * these facilities. 40 */ 41 @Immutable 42 public class KeyHandle { newKeyData(com.google.crypto.tink.KeyTemplate keyTemplate)43 private static KeyData newKeyData(com.google.crypto.tink.KeyTemplate keyTemplate) 44 throws GeneralSecurityException { 45 try { 46 byte[] serializedKeyTemplate = 47 TinkProtoParametersFormat.serialize(keyTemplate.toParameters()); 48 com.google.crypto.tink.proto.KeyTemplate protoTemplate = 49 com.google.crypto.tink.proto.KeyTemplate.parseFrom( 50 serializedKeyTemplate, ExtensionRegistryLite.getEmptyRegistry()); 51 KeyManager<?> manager = 52 KeyManagerRegistry.globalInstance().getUntypedKeyManager(protoTemplate.getTypeUrl()); 53 if (KeyManagerRegistry.globalInstance().isNewKeyAllowed(protoTemplate.getTypeUrl())) { 54 return manager.newKeyData(protoTemplate.getValue()); 55 } else { 56 throw new GeneralSecurityException( 57 "newKey-operation not permitted for key type " + protoTemplate.getTypeUrl()); 58 } 59 } catch (InvalidProtocolBufferException e) { 60 throw new GeneralSecurityException("Failed to parse serialized parameters", e); 61 } 62 } 63 64 /** 65 * KeyStatusType is metadata associated to a key which is only meaningful when the key is part of 66 * a {@link Keyset}. A key's status in the Keyset is either ENABLED (able to perform cryptographic 67 * operations), DISABLED (unable to perform operations, but could be re-enabled), or DESTROYED 68 * (the key's data is no longer present in the keyset). 69 */ 70 public enum KeyStatusType { 71 ENABLED, 72 DISABLED, 73 DESTROYED; 74 } 75 76 /** 77 * Returns a {@link KeyHandle} instance with {@code key} as the underlying {@link TinkKey} if the 78 * caller provides the correct {@link KeyAccess} instance. 79 * 80 * @throws GeneralSecurityException if {@code access} does not grant access to {@code key} 81 */ createFromKey(TinkKey key, KeyAccess access)82 public static KeyHandle createFromKey(TinkKey key, KeyAccess access) 83 throws GeneralSecurityException { 84 KeyHandle result = new KeyHandle(key); 85 result.checkAccess(access); 86 return result; 87 } 88 89 /** 90 * Returns a {@link KeyHandle} instance where the underlying {@link TinkKey} wraps the input 91 * {@code keyData}. The returned KeyHandle has a secret if keyData has key material of type 92 * UNKNOWN_KEYMATERIAL, SYMMETRIC, or ASYMMETRIC_PRIVATE. 93 * 94 * <p>Do not use this in new code. Instead, use {@link com.google.crypto.tink.Key} and these 95 * facilities. 96 */ createFromKey(KeyData keyData, OutputPrefixType opt)97 public static KeyHandle createFromKey(KeyData keyData, OutputPrefixType opt) { 98 return new KeyHandle(new ProtoKey(keyData, opt)); 99 } 100 101 private final TinkKey key; 102 private final KeyStatusType status; 103 private final int id; 104 105 /** 106 * Constructs a KeyHandle wrapping the input TinkKey. The KeyStatusType is set to ENABLED and an 107 * arbitrary key ID is assigned. 108 */ KeyHandle(TinkKey key)109 private KeyHandle(TinkKey key) { 110 this.key = key; 111 this.status = KeyStatusType.ENABLED; 112 this.id = Util.randKeyId(); 113 } 114 115 /** 116 * Constructor intended for Tink internal purposes; allows one to set all the member variables of 117 * a {@link KeyHandle}. 118 */ KeyHandle(TinkKey key, KeyStatusType status, int keyId)119 protected KeyHandle(TinkKey key, KeyStatusType status, int keyId) { 120 this.key = key; 121 this.status = status; 122 this.id = keyId; 123 } 124 125 /** 126 * Generates a new {@link KeyHandle} that contains a fresh key generated according to {@code 127 * keyTemplate}. 128 * 129 * @throws GeneralSecurityException if the key template's type URL has not been registered with 130 * the {@link Registry}. 131 */ generateNew(KeyTemplate keyTemplate)132 public static KeyHandle generateNew(KeyTemplate keyTemplate) throws GeneralSecurityException { 133 ProtoKey protoKey = new ProtoKey(newKeyData(keyTemplate), getOutputPrefixType(keyTemplate)); 134 return new KeyHandle(protoKey); 135 } 136 137 /** Returns {@code true} if the underlying {@link TinkKey} has a secret. */ hasSecret()138 public boolean hasSecret() { 139 return key.hasSecret(); 140 } 141 142 /** Returns the status of the key. See {@link KeyStatusType}. */ getStatus()143 public KeyStatusType getStatus() { 144 return this.status; 145 } 146 147 /** 148 * Returns the key ID of this key. The key ID is not guaranteed to be unique among all KeyHandles. 149 */ getId()150 public int getId() { 151 return id; 152 } 153 154 /** 155 * Returns the underlying {@link TinkKey} key if {@code access} is a {@link SecretKeyAccess} and 156 * the key has a secret, or if the key does not have a secret, otherwise throws a {@link 157 * GeneralSecurityException}. 158 */ getKey(KeyAccess access)159 public TinkKey getKey(KeyAccess access) throws GeneralSecurityException { 160 checkAccess(access); 161 return key; 162 } 163 164 /** 165 * Returns the {@link KeyTemplate} of the underlying {@link TinkKey}. 166 * 167 * @throws UnsupportedOperationException if the underlying {@link TinkKey} has not implemented 168 * getKeyTemplate(). 169 */ getKeyTemplate()170 public KeyTemplate getKeyTemplate() { 171 return key.getKeyTemplate(); 172 } 173 checkAccess(KeyAccess access)174 private void checkAccess(KeyAccess access) throws GeneralSecurityException { 175 if (hasSecret() && !access.canAccessSecret()) { 176 throw new GeneralSecurityException("No access"); 177 } 178 } 179 } 180