// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package keyset import ( "errors" "fmt" "github.com/google/tink/go/core/registry" "github.com/google/tink/go/subtle/random" tinkpb "github.com/google/tink/go/proto/tink_go_proto" ) // Manager manages a Keyset-proto, with convenience methods that rotate, disable, enable or destroy keys. // Note: It is not thread-safe. type Manager struct { ks *tinkpb.Keyset } // NewManager creates a new instance with an empty Keyset. func NewManager() *Manager { ret := new(Manager) ret.ks = new(tinkpb.Keyset) return ret } // NewManagerFromHandle creates a new instance from the given Handle. func NewManagerFromHandle(kh *Handle) *Manager { ret := new(Manager) ret.ks = kh.ks return ret } // Add generates and adds a fresh key using the given key template. // the key is enabled on creation, but not set to primary. // It returns the ID of the new key func (km *Manager) Add(kt *tinkpb.KeyTemplate) (uint32, error) { if kt == nil { return 0, errors.New("keyset_manager: cannot add key, need key template") } if kt.OutputPrefixType == tinkpb.OutputPrefixType_UNKNOWN_PREFIX { return 0, errors.New("keyset_manager: unknown output prefix type") } if km.ks == nil { return 0, errors.New("keyset_manager: cannot add key to nil keyset") } keyData, err := registry.NewKeyData(kt) if err != nil { return 0, fmt.Errorf("keyset_manager: cannot create KeyData: %s", err) } keyID := km.newKeyID() key := &tinkpb.Keyset_Key{ KeyData: keyData, Status: tinkpb.KeyStatusType_ENABLED, KeyId: keyID, OutputPrefixType: kt.OutputPrefixType, } km.ks.Key = append(km.ks.Key, key) return keyID, nil } // SetPrimary sets the key with given keyID as primary. // Returns an error if the key is not found or not enabled. func (km *Manager) SetPrimary(keyID uint32) error { if km.ks == nil { return errors.New("keyset_manager: cannot set primary, no keyset") } for _, key := range km.ks.Key { if key.KeyId != keyID { continue } if key.Status == tinkpb.KeyStatusType_ENABLED { km.ks.PrimaryKeyId = keyID return nil } return errors.New("keyset_manager: cannot set key as primary because it's not enabled") } return fmt.Errorf("keyset_manager: key with id %d not found", keyID) } // Enable will enable the key with given keyID. // Returns an error if the key is not found or is not enabled or disabled already. func (km *Manager) Enable(keyID uint32) error { if km.ks == nil { return errors.New("keyset_manager: cannot enable key, no keyset") } for i, key := range km.ks.Key { if key.KeyId != keyID { continue } if key.Status == tinkpb.KeyStatusType_ENABLED || key.Status == tinkpb.KeyStatusType_DISABLED { km.ks.Key[i].Status = tinkpb.KeyStatusType_ENABLED return nil } return fmt.Errorf("keyset_manager: cannot enable key with id %d with status %s", keyID, key.Status.String()) } return fmt.Errorf("keyset_manager: key with id %d not found", keyID) } // Disable will disable the key with given keyID. // Returns an error if the key is not found or it is the primary key. func (km *Manager) Disable(keyID uint32) error { if km.ks == nil { return errors.New("keyset_manager: cannot disable key, no keyset") } if km.ks.PrimaryKeyId == keyID { return errors.New("keyset_manager: cannot disable the primary key") } for i, key := range km.ks.Key { if key.KeyId != keyID { continue } if key.Status == tinkpb.KeyStatusType_ENABLED || key.Status == tinkpb.KeyStatusType_DISABLED { km.ks.Key[i].Status = tinkpb.KeyStatusType_DISABLED return nil } return fmt.Errorf("keyset_manager: cannot disable key with id %d with status %s", keyID, key.Status.String()) } return fmt.Errorf("keyset_manager: key with id %d not found", keyID) } // Delete will delete the key with given keyID, removing the key from the keyset entirely. // Returns an error if the key is not found or it is the primary key. func (km *Manager) Delete(keyID uint32) error { if km.ks == nil { return errors.New("keyset_manager: cannot delete key, no keyset") } if km.ks.PrimaryKeyId == keyID { return errors.New("keyset_manager: cannot delete the primary key") } deleteIdx, found := 0, false for i, key := range km.ks.Key { if key.KeyId == keyID { found = true deleteIdx = i } } if !found { return fmt.Errorf("keyset_manager: key with id %d not found", keyID) } // swap elements km.ks.Key[deleteIdx] = km.ks.Key[len(km.ks.Key)-1] // trim last element km.ks.Key = km.ks.Key[:len(km.ks.Key)-1] return nil } // Handle creates a new Handle for the managed keyset. func (km *Manager) Handle() (*Handle, error) { return &Handle{ks: km.ks}, nil } // newKeyID generates a key id that has not been used by any key in the keyset. func (km *Manager) newKeyID() uint32 { for { ret := random.GetRandomUint32() ok := true for _, key := range km.ks.Key { if key.KeyId == ret { ok = false break } } if ok { return ret } } }