xref: /aosp_15_r20/external/tink/go/core/primitiveset/primitiveset.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
17// Package primitiveset provides a container for a set of cryptographic
18// primitives.
19//
20// It provides also additional properties for the primitives it holds. In
21// particular, one of the primitives in the set can be distinguished as "the
22// primary" one.
23package primitiveset
24
25import (
26	"fmt"
27
28	"github.com/google/tink/go/core/cryptofmt"
29	tinkpb "github.com/google/tink/go/proto/tink_go_proto"
30)
31
32// Entry represents a single entry in the keyset. In addition to the actual
33// primitive, it holds the identifier and status of the primitive.
34type Entry struct {
35	KeyID      uint32
36	Primitive  interface{}
37	Prefix     string
38	PrefixType tinkpb.OutputPrefixType
39	Status     tinkpb.KeyStatusType
40	TypeURL    string
41}
42
43func newEntry(keyID uint32, primitive interface{}, prefix string, prefixType tinkpb.OutputPrefixType, status tinkpb.KeyStatusType, typeURL string) *Entry {
44	return &Entry{
45		KeyID:      keyID,
46		Primitive:  primitive,
47		Prefix:     prefix,
48		Status:     status,
49		PrefixType: prefixType,
50		TypeURL:    typeURL,
51	}
52}
53
54// PrimitiveSet is used for supporting key rotation: primitives in a set
55// correspond to keys in a keyset. Users will usually work with primitive
56// instances, which essentially wrap primitive sets. For example an instance of
57// an AEAD-primitive for a given keyset holds a set of AEAD-primitives
58// corresponding to the keys in the keyset, and uses the set members to do the
59// actual crypto operations: to encrypt data the primary AEAD-primitive from
60// the set is used, and upon decryption the ciphertext's prefix determines the
61// id of the primitive from the set.
62//
63// PrimitiveSet is a public to allow its use in implementations of custom
64// primitives.
65type PrimitiveSet struct {
66	// Primary entry.
67	Primary *Entry
68
69	// The primitives are stored in a map of (ciphertext prefix, list of
70	// primitives sharing the prefix). This allows quickly retrieving the
71	// primitives sharing some particular prefix.
72	Entries map[string][]*Entry
73	// Stores entries in the original keyset key order.
74	EntriesInKeysetOrder []*Entry
75
76	Annotations map[string]string
77}
78
79// New returns an empty instance of PrimitiveSet.
80func New() *PrimitiveSet {
81	return &PrimitiveSet{
82		Primary:              nil,
83		Entries:              make(map[string][]*Entry),
84		EntriesInKeysetOrder: make([]*Entry, 0),
85		Annotations:          nil,
86	}
87}
88
89// RawEntries returns all primitives in the set that have RAW prefix.
90func (ps *PrimitiveSet) RawEntries() ([]*Entry, error) {
91	return ps.EntriesForPrefix(cryptofmt.RawPrefix)
92}
93
94// EntriesForPrefix returns all primitives in the set that have the given prefix.
95func (ps *PrimitiveSet) EntriesForPrefix(prefix string) ([]*Entry, error) {
96	result, found := ps.Entries[prefix]
97	if !found {
98		return []*Entry{}, nil
99	}
100	return result, nil
101}
102
103// Add creates a new entry in the primitive set and returns the added entry.
104func (ps *PrimitiveSet) Add(primitive interface{}, key *tinkpb.Keyset_Key) (*Entry, error) {
105	if key == nil || primitive == nil {
106		return nil, fmt.Errorf("primitive_set: key and primitive must not be nil")
107	}
108	if key.GetKeyData() == nil {
109		return nil, fmt.Errorf("primitive_set: keyData must not be nil")
110	}
111	if key.GetStatus() != tinkpb.KeyStatusType_ENABLED {
112		return nil, fmt.Errorf("primitive_set: The key must be ENABLED")
113	}
114	prefix, err := cryptofmt.OutputPrefix(key)
115	if err != nil {
116		return nil, fmt.Errorf("primitive_set: %s", err)
117	}
118	e := newEntry(
119		key.GetKeyId(),
120		primitive,
121		prefix,
122		key.GetOutputPrefixType(),
123		key.GetStatus(),
124		key.GetKeyData().GetTypeUrl(),
125	)
126	ps.Entries[prefix] = append(ps.Entries[prefix], e)
127	ps.EntriesInKeysetOrder = append(ps.EntriesInKeysetOrder, e)
128	return e, nil
129}
130