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