1*e7b1675dSTing-Kang Chang// Copyright 2019 Google LLC 2*e7b1675dSTing-Kang Chang// 3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang// 7*e7b1675dSTing-Kang Chang// http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang// 9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang// limitations under the License. 14*e7b1675dSTing-Kang Chang// 15*e7b1675dSTing-Kang Chang//////////////////////////////////////////////////////////////////////////////// 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Changpackage keyset 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "errors" 21*e7b1675dSTing-Kang Chang "fmt" 22*e7b1675dSTing-Kang Chang 23*e7b1675dSTing-Kang Chang "github.com/google/tink/go/core/registry" 24*e7b1675dSTing-Kang Chang "github.com/google/tink/go/subtle/random" 25*e7b1675dSTing-Kang Chang 26*e7b1675dSTing-Kang Chang tinkpb "github.com/google/tink/go/proto/tink_go_proto" 27*e7b1675dSTing-Kang Chang) 28*e7b1675dSTing-Kang Chang 29*e7b1675dSTing-Kang Chang// Manager manages a Keyset-proto, with convenience methods that rotate, disable, enable or destroy keys. 30*e7b1675dSTing-Kang Chang// Note: It is not thread-safe. 31*e7b1675dSTing-Kang Changtype Manager struct { 32*e7b1675dSTing-Kang Chang ks *tinkpb.Keyset 33*e7b1675dSTing-Kang Chang} 34*e7b1675dSTing-Kang Chang 35*e7b1675dSTing-Kang Chang// NewManager creates a new instance with an empty Keyset. 36*e7b1675dSTing-Kang Changfunc NewManager() *Manager { 37*e7b1675dSTing-Kang Chang ret := new(Manager) 38*e7b1675dSTing-Kang Chang ret.ks = new(tinkpb.Keyset) 39*e7b1675dSTing-Kang Chang return ret 40*e7b1675dSTing-Kang Chang} 41*e7b1675dSTing-Kang Chang 42*e7b1675dSTing-Kang Chang// NewManagerFromHandle creates a new instance from the given Handle. 43*e7b1675dSTing-Kang Changfunc NewManagerFromHandle(kh *Handle) *Manager { 44*e7b1675dSTing-Kang Chang ret := new(Manager) 45*e7b1675dSTing-Kang Chang ret.ks = kh.ks 46*e7b1675dSTing-Kang Chang return ret 47*e7b1675dSTing-Kang Chang} 48*e7b1675dSTing-Kang Chang 49*e7b1675dSTing-Kang Chang// Add generates and adds a fresh key using the given key template. 50*e7b1675dSTing-Kang Chang// the key is enabled on creation, but not set to primary. 51*e7b1675dSTing-Kang Chang// It returns the ID of the new key 52*e7b1675dSTing-Kang Changfunc (km *Manager) Add(kt *tinkpb.KeyTemplate) (uint32, error) { 53*e7b1675dSTing-Kang Chang if kt == nil { 54*e7b1675dSTing-Kang Chang return 0, errors.New("keyset_manager: cannot add key, need key template") 55*e7b1675dSTing-Kang Chang } 56*e7b1675dSTing-Kang Chang if kt.OutputPrefixType == tinkpb.OutputPrefixType_UNKNOWN_PREFIX { 57*e7b1675dSTing-Kang Chang return 0, errors.New("keyset_manager: unknown output prefix type") 58*e7b1675dSTing-Kang Chang } 59*e7b1675dSTing-Kang Chang if km.ks == nil { 60*e7b1675dSTing-Kang Chang return 0, errors.New("keyset_manager: cannot add key to nil keyset") 61*e7b1675dSTing-Kang Chang } 62*e7b1675dSTing-Kang Chang keyData, err := registry.NewKeyData(kt) 63*e7b1675dSTing-Kang Chang if err != nil { 64*e7b1675dSTing-Kang Chang return 0, fmt.Errorf("keyset_manager: cannot create KeyData: %s", err) 65*e7b1675dSTing-Kang Chang } 66*e7b1675dSTing-Kang Chang keyID := km.newKeyID() 67*e7b1675dSTing-Kang Chang key := &tinkpb.Keyset_Key{ 68*e7b1675dSTing-Kang Chang KeyData: keyData, 69*e7b1675dSTing-Kang Chang Status: tinkpb.KeyStatusType_ENABLED, 70*e7b1675dSTing-Kang Chang KeyId: keyID, 71*e7b1675dSTing-Kang Chang OutputPrefixType: kt.OutputPrefixType, 72*e7b1675dSTing-Kang Chang } 73*e7b1675dSTing-Kang Chang km.ks.Key = append(km.ks.Key, key) 74*e7b1675dSTing-Kang Chang return keyID, nil 75*e7b1675dSTing-Kang Chang} 76*e7b1675dSTing-Kang Chang 77*e7b1675dSTing-Kang Chang// SetPrimary sets the key with given keyID as primary. 78*e7b1675dSTing-Kang Chang// Returns an error if the key is not found or not enabled. 79*e7b1675dSTing-Kang Changfunc (km *Manager) SetPrimary(keyID uint32) error { 80*e7b1675dSTing-Kang Chang if km.ks == nil { 81*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot set primary, no keyset") 82*e7b1675dSTing-Kang Chang } 83*e7b1675dSTing-Kang Chang for _, key := range km.ks.Key { 84*e7b1675dSTing-Kang Chang if key.KeyId != keyID { 85*e7b1675dSTing-Kang Chang continue 86*e7b1675dSTing-Kang Chang } 87*e7b1675dSTing-Kang Chang if key.Status == tinkpb.KeyStatusType_ENABLED { 88*e7b1675dSTing-Kang Chang km.ks.PrimaryKeyId = keyID 89*e7b1675dSTing-Kang Chang return nil 90*e7b1675dSTing-Kang Chang } 91*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot set key as primary because it's not enabled") 92*e7b1675dSTing-Kang Chang 93*e7b1675dSTing-Kang Chang } 94*e7b1675dSTing-Kang Chang return fmt.Errorf("keyset_manager: key with id %d not found", keyID) 95*e7b1675dSTing-Kang Chang} 96*e7b1675dSTing-Kang Chang 97*e7b1675dSTing-Kang Chang// Enable will enable the key with given keyID. 98*e7b1675dSTing-Kang Chang// Returns an error if the key is not found or is not enabled or disabled already. 99*e7b1675dSTing-Kang Changfunc (km *Manager) Enable(keyID uint32) error { 100*e7b1675dSTing-Kang Chang if km.ks == nil { 101*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot enable key, no keyset") 102*e7b1675dSTing-Kang Chang } 103*e7b1675dSTing-Kang Chang for i, key := range km.ks.Key { 104*e7b1675dSTing-Kang Chang if key.KeyId != keyID { 105*e7b1675dSTing-Kang Chang continue 106*e7b1675dSTing-Kang Chang } 107*e7b1675dSTing-Kang Chang if key.Status == tinkpb.KeyStatusType_ENABLED || key.Status == tinkpb.KeyStatusType_DISABLED { 108*e7b1675dSTing-Kang Chang km.ks.Key[i].Status = tinkpb.KeyStatusType_ENABLED 109*e7b1675dSTing-Kang Chang return nil 110*e7b1675dSTing-Kang Chang } 111*e7b1675dSTing-Kang Chang return fmt.Errorf("keyset_manager: cannot enable key with id %d with status %s", keyID, key.Status.String()) 112*e7b1675dSTing-Kang Chang } 113*e7b1675dSTing-Kang Chang return fmt.Errorf("keyset_manager: key with id %d not found", keyID) 114*e7b1675dSTing-Kang Chang} 115*e7b1675dSTing-Kang Chang 116*e7b1675dSTing-Kang Chang// Disable will disable the key with given keyID. 117*e7b1675dSTing-Kang Chang// Returns an error if the key is not found or it is the primary key. 118*e7b1675dSTing-Kang Changfunc (km *Manager) Disable(keyID uint32) error { 119*e7b1675dSTing-Kang Chang if km.ks == nil { 120*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot disable key, no keyset") 121*e7b1675dSTing-Kang Chang } 122*e7b1675dSTing-Kang Chang if km.ks.PrimaryKeyId == keyID { 123*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot disable the primary key") 124*e7b1675dSTing-Kang Chang } 125*e7b1675dSTing-Kang Chang for i, key := range km.ks.Key { 126*e7b1675dSTing-Kang Chang if key.KeyId != keyID { 127*e7b1675dSTing-Kang Chang continue 128*e7b1675dSTing-Kang Chang } 129*e7b1675dSTing-Kang Chang if key.Status == tinkpb.KeyStatusType_ENABLED || key.Status == tinkpb.KeyStatusType_DISABLED { 130*e7b1675dSTing-Kang Chang km.ks.Key[i].Status = tinkpb.KeyStatusType_DISABLED 131*e7b1675dSTing-Kang Chang return nil 132*e7b1675dSTing-Kang Chang } 133*e7b1675dSTing-Kang Chang return fmt.Errorf("keyset_manager: cannot disable key with id %d with status %s", keyID, key.Status.String()) 134*e7b1675dSTing-Kang Chang } 135*e7b1675dSTing-Kang Chang return fmt.Errorf("keyset_manager: key with id %d not found", keyID) 136*e7b1675dSTing-Kang Chang} 137*e7b1675dSTing-Kang Chang 138*e7b1675dSTing-Kang Chang// Delete will delete the key with given keyID, removing the key from the keyset entirely. 139*e7b1675dSTing-Kang Chang// Returns an error if the key is not found or it is the primary key. 140*e7b1675dSTing-Kang Changfunc (km *Manager) Delete(keyID uint32) error { 141*e7b1675dSTing-Kang Chang if km.ks == nil { 142*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot delete key, no keyset") 143*e7b1675dSTing-Kang Chang } 144*e7b1675dSTing-Kang Chang if km.ks.PrimaryKeyId == keyID { 145*e7b1675dSTing-Kang Chang return errors.New("keyset_manager: cannot delete the primary key") 146*e7b1675dSTing-Kang Chang } 147*e7b1675dSTing-Kang Chang deleteIdx, found := 0, false 148*e7b1675dSTing-Kang Chang for i, key := range km.ks.Key { 149*e7b1675dSTing-Kang Chang if key.KeyId == keyID { 150*e7b1675dSTing-Kang Chang found = true 151*e7b1675dSTing-Kang Chang deleteIdx = i 152*e7b1675dSTing-Kang Chang } 153*e7b1675dSTing-Kang Chang } 154*e7b1675dSTing-Kang Chang if !found { 155*e7b1675dSTing-Kang Chang return fmt.Errorf("keyset_manager: key with id %d not found", keyID) 156*e7b1675dSTing-Kang Chang } 157*e7b1675dSTing-Kang Chang // swap elements 158*e7b1675dSTing-Kang Chang km.ks.Key[deleteIdx] = km.ks.Key[len(km.ks.Key)-1] 159*e7b1675dSTing-Kang Chang // trim last element 160*e7b1675dSTing-Kang Chang km.ks.Key = km.ks.Key[:len(km.ks.Key)-1] 161*e7b1675dSTing-Kang Chang return nil 162*e7b1675dSTing-Kang Chang} 163*e7b1675dSTing-Kang Chang 164*e7b1675dSTing-Kang Chang// Handle creates a new Handle for the managed keyset. 165*e7b1675dSTing-Kang Changfunc (km *Manager) Handle() (*Handle, error) { 166*e7b1675dSTing-Kang Chang return &Handle{ks: km.ks}, nil 167*e7b1675dSTing-Kang Chang} 168*e7b1675dSTing-Kang Chang 169*e7b1675dSTing-Kang Chang// newKeyID generates a key id that has not been used by any key in the keyset. 170*e7b1675dSTing-Kang Changfunc (km *Manager) newKeyID() uint32 { 171*e7b1675dSTing-Kang Chang for { 172*e7b1675dSTing-Kang Chang ret := random.GetRandomUint32() 173*e7b1675dSTing-Kang Chang ok := true 174*e7b1675dSTing-Kang Chang for _, key := range km.ks.Key { 175*e7b1675dSTing-Kang Chang if key.KeyId == ret { 176*e7b1675dSTing-Kang Chang ok = false 177*e7b1675dSTing-Kang Chang break 178*e7b1675dSTing-Kang Chang } 179*e7b1675dSTing-Kang Chang } 180*e7b1675dSTing-Kang Chang if ok { 181*e7b1675dSTing-Kang Chang return ret 182*e7b1675dSTing-Kang Chang } 183*e7b1675dSTing-Kang Chang } 184*e7b1675dSTing-Kang Chang} 185