xref: /aosp_15_r20/external/tink/go/keyset/handle.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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