1// Copyright 2019 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 17package keyset 18 19import ( 20 "errors" 21 "fmt" 22 23 "google.golang.org/protobuf/encoding/prototext" 24 "google.golang.org/protobuf/proto" 25 26 "github.com/google/tink/go/core/primitiveset" 27 "github.com/google/tink/go/core/registry" 28 "github.com/google/tink/go/tink" 29 tinkpb "github.com/google/tink/go/proto/tink_go_proto" 30) 31 32var errInvalidKeyset = fmt.Errorf("keyset.Handle: invalid keyset") 33 34// Handle provides access to a Keyset protobuf, to limit the exposure of actual protocol 35// buffers that hold sensitive key material. 36type Handle struct { 37 ks *tinkpb.Keyset 38 annotations map[string]string 39} 40 41func newWithOptions(ks *tinkpb.Keyset, opts ...Option) (*Handle, error) { 42 h := &Handle{ks: ks} 43 if err := applyOptions(h, opts...); err != nil { 44 return nil, err 45 } 46 return h, nil 47} 48 49// NewHandle creates a keyset handle that contains a single fresh key generated according 50// to the given KeyTemplate. 51func NewHandle(kt *tinkpb.KeyTemplate) (*Handle, error) { 52 manager := NewManager() 53 keyID, err := manager.Add(kt) 54 if err != nil { 55 return nil, fmt.Errorf("keyset.Handle: cannot generate new keyset: %s", err) 56 } 57 err = manager.SetPrimary(keyID) 58 if err != nil { 59 return nil, fmt.Errorf("keyset.Handle: cannot set primary: %s", err) 60 } 61 handle, err := manager.Handle() 62 if err != nil { 63 return nil, fmt.Errorf("keyset.Handle: cannot get keyset handle: %s", err) 64 } 65 return handle, nil 66} 67 68// NewHandleWithNoSecrets creates a new instance of KeysetHandle using the given keyset which does 69// not contain any secret key material. 70func NewHandleWithNoSecrets(ks *tinkpb.Keyset) (*Handle, error) { 71 if ks == nil { 72 return nil, errors.New("keyset.Handle: nil keyset") 73 } 74 h := &Handle{ks: ks} 75 if h.hasSecrets() { 76 // If you need to do this, you have to use func insecurecleartextkeyset.Read() instead. 77 return nil, errors.New("importing unencrypted secret key material is forbidden") 78 } 79 return h, nil 80} 81 82// Read tries to create a Handle from an encrypted keyset obtained via reader. 83func Read(reader Reader, masterKey tink.AEAD) (*Handle, error) { 84 return ReadWithAssociatedData(reader, masterKey, []byte{}) 85} 86 87// ReadWithAssociatedData tries to create a Handle from an encrypted keyset obtained via reader using the provided associated data. 88func ReadWithAssociatedData(reader Reader, masterKey tink.AEAD, associatedData []byte) (*Handle, error) { 89 encryptedKeyset, err := reader.ReadEncrypted() 90 if err != nil { 91 return nil, err 92 } 93 ks, err := decrypt(encryptedKeyset, masterKey, associatedData) 94 if err != nil { 95 return nil, err 96 } 97 return &Handle{ks: ks}, nil 98} 99 100// ReadWithNoSecrets tries to create a keyset.Handle from a keyset obtained via reader. 101func ReadWithNoSecrets(reader Reader) (*Handle, error) { 102 ks, err := reader.Read() 103 if err != nil { 104 return nil, err 105 } 106 return NewHandleWithNoSecrets(ks) 107} 108 109// Public returns a Handle of the public keys if the managed keyset contains private keys. 110func (h *Handle) Public() (*Handle, error) { 111 privKeys := h.ks.Key 112 pubKeys := make([]*tinkpb.Keyset_Key, len(privKeys)) 113 114 for i := 0; i < len(privKeys); i++ { 115 if privKeys[i] == nil || privKeys[i].KeyData == nil { 116 return nil, errInvalidKeyset 117 } 118 privKeyData := privKeys[i].KeyData 119 pubKeyData, err := publicKeyData(privKeyData) 120 if err != nil { 121 return nil, fmt.Errorf("keyset.Handle: %s", err) 122 } 123 pubKeys[i] = &tinkpb.Keyset_Key{ 124 KeyData: pubKeyData, 125 Status: privKeys[i].Status, 126 KeyId: privKeys[i].KeyId, 127 OutputPrefixType: privKeys[i].OutputPrefixType, 128 } 129 } 130 ks := &tinkpb.Keyset{ 131 PrimaryKeyId: h.ks.PrimaryKeyId, 132 Key: pubKeys, 133 } 134 return &Handle{ks: ks}, nil 135} 136 137// String returns a string representation of the managed keyset. 138// The result does not contain any sensitive key material. 139func (h *Handle) String() string { 140 c, err := prototext.MarshalOptions{}.Marshal(getKeysetInfo(h.ks)) 141 if err != nil { 142 return "" 143 } 144 return string(c) 145} 146 147// KeysetInfo returns KeysetInfo representation of the managed keyset. 148// The result does not contain any sensitive key material. 149func (h *Handle) KeysetInfo() *tinkpb.KeysetInfo { 150 return getKeysetInfo(h.ks) 151} 152 153// Write encrypts and writes the enclosing keyset. 154func (h *Handle) Write(writer Writer, masterKey tink.AEAD) error { 155 return h.WriteWithAssociatedData(writer, masterKey, []byte{}) 156} 157 158// WriteWithAssociatedData encrypts and writes the enclosing keyset using the provided associated data. 159func (h *Handle) WriteWithAssociatedData(writer Writer, masterKey tink.AEAD, associatedData []byte) error { 160 encrypted, err := encrypt(h.ks, masterKey, associatedData) 161 if err != nil { 162 return err 163 } 164 return writer.WriteEncrypted(encrypted) 165} 166 167// WriteWithNoSecrets exports the keyset in h to the given Writer w returning an error if the keyset 168// contains secret key material. 169func (h *Handle) WriteWithNoSecrets(w Writer) error { 170 if h.hasSecrets() { 171 return errors.New("exporting unencrypted secret key material is forbidden") 172 } 173 174 return w.Write(h.ks) 175} 176 177// Primitives creates a set of primitives corresponding to the keys with 178// status=ENABLED in the keyset of the given keyset handle, assuming all the 179// corresponding key managers are present (keys with status!=ENABLED are skipped). 180// 181// The returned set is usually later "wrapped" into a class that implements 182// the corresponding Primitive-interface. 183func (h *Handle) Primitives() (*primitiveset.PrimitiveSet, error) { 184 return h.PrimitivesWithKeyManager(nil) 185} 186 187// PrimitivesWithKeyManager creates a set of primitives corresponding to 188// the keys with status=ENABLED in the keyset of the given keysetHandle, using 189// the given key manager (instead of registered key managers) for keys supported 190// by it. Keys not supported by the key manager are handled by matching registered 191// key managers (if present), and keys with status!=ENABLED are skipped. 192// 193// This enables custom treatment of keys, for example providing extra context 194// (e.g. credentials for accessing keys managed by a KMS), or gathering custom 195// monitoring/profiling information. 196// 197// The returned set is usually later "wrapped" into a class that implements 198// the corresponding Primitive-interface. 199func (h *Handle) PrimitivesWithKeyManager(km registry.KeyManager) (*primitiveset.PrimitiveSet, error) { 200 if err := Validate(h.ks); err != nil { 201 return nil, fmt.Errorf("registry.PrimitivesWithKeyManager: invalid keyset: %s", err) 202 } 203 primitiveSet := primitiveset.New() 204 primitiveSet.Annotations = h.annotations 205 for _, key := range h.ks.Key { 206 if key.Status != tinkpb.KeyStatusType_ENABLED { 207 continue 208 } 209 var primitive interface{} 210 var err error 211 if km != nil && km.DoesSupport(key.KeyData.TypeUrl) { 212 primitive, err = km.Primitive(key.KeyData.Value) 213 } else { 214 primitive, err = registry.PrimitiveFromKeyData(key.KeyData) 215 } 216 if err != nil { 217 return nil, fmt.Errorf("registry.PrimitivesWithKeyManager: cannot get primitive from key: %s", err) 218 } 219 entry, err := primitiveSet.Add(primitive, key) 220 if err != nil { 221 return nil, fmt.Errorf("registry.PrimitivesWithKeyManager: cannot add primitive: %s", err) 222 } 223 if key.KeyId == h.ks.PrimaryKeyId { 224 primitiveSet.Primary = entry 225 } 226 } 227 return primitiveSet, nil 228} 229 230// hasSecrets returns true if the keyset handle contains key material considered secret. This 231// includes symmetric keys, private keys of asymmetric crypto systems, and keys of an unknown type. 232func (h *Handle) hasSecrets() bool { 233 for _, k := range h.ks.Key { 234 if k == nil || k.KeyData == nil { 235 continue 236 } 237 if k.KeyData.KeyMaterialType == tinkpb.KeyData_UNKNOWN_KEYMATERIAL { 238 return true 239 } 240 if k.KeyData.KeyMaterialType == tinkpb.KeyData_ASYMMETRIC_PRIVATE { 241 return true 242 } 243 if k.KeyData.KeyMaterialType == tinkpb.KeyData_SYMMETRIC { 244 return true 245 } 246 } 247 return false 248} 249 250func publicKeyData(privKeyData *tinkpb.KeyData) (*tinkpb.KeyData, error) { 251 if privKeyData.KeyMaterialType != tinkpb.KeyData_ASYMMETRIC_PRIVATE { 252 return nil, fmt.Errorf("keyset.Handle: keyset contains a non-private key") 253 } 254 km, err := registry.GetKeyManager(privKeyData.TypeUrl) 255 if err != nil { 256 return nil, err 257 } 258 pkm, ok := km.(registry.PrivateKeyManager) 259 if !ok { 260 return nil, fmt.Errorf("keyset.Handle: %s does not belong to a PrivateKeyManager", privKeyData.TypeUrl) 261 } 262 return pkm.PublicKeyData(privKeyData.Value) 263} 264 265func decrypt(encryptedKeyset *tinkpb.EncryptedKeyset, masterKey tink.AEAD, associatedData []byte) (*tinkpb.Keyset, error) { 266 if encryptedKeyset == nil || masterKey == nil { 267 return nil, fmt.Errorf("keyset.Handle: invalid encrypted keyset") 268 } 269 decrypted, err := masterKey.Decrypt(encryptedKeyset.EncryptedKeyset, associatedData) 270 if err != nil { 271 return nil, fmt.Errorf("keyset.Handle: decryption failed: %s", err) 272 } 273 keyset := new(tinkpb.Keyset) 274 if err := proto.Unmarshal(decrypted, keyset); err != nil { 275 return nil, errInvalidKeyset 276 } 277 return keyset, nil 278} 279 280func encrypt(keyset *tinkpb.Keyset, masterKey tink.AEAD, associatedData []byte) (*tinkpb.EncryptedKeyset, error) { 281 serializedKeyset, err := proto.Marshal(keyset) 282 if err != nil { 283 return nil, errInvalidKeyset 284 } 285 encrypted, err := masterKey.Encrypt(serializedKeyset, associatedData) 286 if err != nil { 287 return nil, fmt.Errorf("keyset.Handle: encrypted failed: %s", err) 288 } 289 // get keyset info 290 encryptedKeyset := &tinkpb.EncryptedKeyset{ 291 EncryptedKeyset: encrypted, 292 KeysetInfo: getKeysetInfo(keyset), 293 } 294 return encryptedKeyset, nil 295} 296 297// getKeysetInfo returns a KeysetInfo from a Keyset protobuf. 298func getKeysetInfo(keyset *tinkpb.Keyset) *tinkpb.KeysetInfo { 299 if keyset == nil { 300 panic("keyset.Handle: keyset must be non nil") 301 } 302 nKey := len(keyset.Key) 303 keyInfos := make([]*tinkpb.KeysetInfo_KeyInfo, nKey) 304 for i, key := range keyset.Key { 305 keyInfos[i] = getKeyInfo(key) 306 } 307 return &tinkpb.KeysetInfo{ 308 PrimaryKeyId: keyset.PrimaryKeyId, 309 KeyInfo: keyInfos, 310 } 311} 312 313// getKeyInfo returns a KeyInfo from a Key protobuf. 314func getKeyInfo(key *tinkpb.Keyset_Key) *tinkpb.KeysetInfo_KeyInfo { 315 return &tinkpb.KeysetInfo_KeyInfo{ 316 TypeUrl: key.KeyData.TypeUrl, 317 Status: key.Status, 318 KeyId: key.KeyId, 319 OutputPrefixType: key.OutputPrefixType, 320 } 321} 322