vault: Adding keyring
This commit is contained in:
parent
70b3b37ffb
commit
57c763a3fa
|
@ -0,0 +1,158 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Keyring is used to manage multiple encryption keys used by
|
||||
// the barrier. New keys can be installed and each has a sequential term.
|
||||
// The term used to encrypt a key is prefixed to the key written out.
|
||||
// All data is encrypted with the latest key, but storing the old keys
|
||||
// allows for decryption of keys written previously. Along with the encryption
|
||||
// keys, the keyring also tracks the master key. This is necessary so that
|
||||
// when a new key is added to the keyring, we can encrypt with the master key
|
||||
// and write out the new keyring.
|
||||
type Keyring struct {
|
||||
masterKey []byte
|
||||
keys map[uint32]*Key
|
||||
activeTerm uint32
|
||||
l sync.RWMutex
|
||||
}
|
||||
|
||||
// EncodedKeyring is used for serialization of the keyring
|
||||
type EncodedKeyring struct {
|
||||
MasterKey []byte
|
||||
Keys []*Key
|
||||
}
|
||||
|
||||
// Key represents a single term, along with the key used.
|
||||
type Key struct {
|
||||
Term uint32
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// NewKeyring creates a new keyring
|
||||
func NewKeyring() *Keyring {
|
||||
k := &Keyring{
|
||||
keys: make(map[uint32]*Key),
|
||||
activeTerm: 0,
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
// AddKey adds a new key to the keyring
|
||||
func (k *Keyring) AddKey(term uint32, value []byte) error {
|
||||
k.l.Lock()
|
||||
defer k.l.Unlock()
|
||||
|
||||
// Ensure there is no confict
|
||||
if key, ok := k.keys[term]; ok {
|
||||
if !bytes.Equal(key.Value, value) {
|
||||
return fmt.Errorf("Conflicting key for term %d already installed", term)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Install the new key
|
||||
key := &Key{
|
||||
Term: term,
|
||||
Value: value,
|
||||
}
|
||||
k.keys[term] = key
|
||||
|
||||
// Update the active term if newer
|
||||
if term > k.activeTerm {
|
||||
k.activeTerm = term
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveKey removes a new key to the keyring
|
||||
func (k *Keyring) RemoveKey(term uint32) error {
|
||||
k.l.Lock()
|
||||
defer k.l.Unlock()
|
||||
|
||||
// Ensure this is not the active key
|
||||
if term == k.activeTerm {
|
||||
return fmt.Errorf("Cannot remove active key")
|
||||
}
|
||||
|
||||
// Delete the key
|
||||
delete(k.keys, term)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActiveTerm returns the currently active term
|
||||
func (k *Keyring) ActiveTerm() uint32 {
|
||||
k.l.RLock()
|
||||
defer k.l.RUnlock()
|
||||
return k.activeTerm
|
||||
}
|
||||
|
||||
// ActiveKey returns the active encryption key, or nil
|
||||
func (k *Keyring) ActiveKey() *Key {
|
||||
k.l.RLock()
|
||||
defer k.l.RUnlock()
|
||||
return k.keys[k.activeTerm]
|
||||
}
|
||||
|
||||
// TermKey returns the key for the given term, or nil
|
||||
func (k *Keyring) TermKey(term uint32) *Key {
|
||||
k.l.RLock()
|
||||
defer k.l.RUnlock()
|
||||
return k.keys[term]
|
||||
}
|
||||
|
||||
// SetMasterKey is used to update the master key
|
||||
func (k *Keyring) SetMasterKey(val []byte) {
|
||||
k.l.Lock()
|
||||
defer k.l.Unlock()
|
||||
k.masterKey = val
|
||||
}
|
||||
|
||||
// MasterKey returns the master key
|
||||
func (k *Keyring) MasterKey() []byte {
|
||||
k.l.RLock()
|
||||
defer k.l.RUnlock()
|
||||
return k.masterKey
|
||||
}
|
||||
|
||||
// Serialize is used to create a byte encoded keyring
|
||||
func (k *Keyring) Serialize() ([]byte, error) {
|
||||
k.l.RLock()
|
||||
defer k.l.RUnlock()
|
||||
|
||||
// Create the encoded entry
|
||||
enc := EncodedKeyring{
|
||||
MasterKey: k.masterKey,
|
||||
}
|
||||
for _, key := range k.keys {
|
||||
enc.Keys = append(enc.Keys, key)
|
||||
}
|
||||
|
||||
// JSON encode the keyring
|
||||
buf, err := json.Marshal(enc)
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// DeserializeKeyring is used to deserialize and return a new keyring
|
||||
func DeserializeKeyring(buf []byte) (*Keyring, error) {
|
||||
// Deserialize the keyring
|
||||
var enc EncodedKeyring
|
||||
if err := json.Unmarshal(buf, &enc); err != nil {
|
||||
return nil, fmt.Errorf("deserialization failed: %v", err)
|
||||
}
|
||||
|
||||
// Create a new keyring
|
||||
k := NewKeyring()
|
||||
k.SetMasterKey(enc.MasterKey)
|
||||
for _, key := range enc.Keys {
|
||||
if err := k.AddKey(key.Term, key.Value); err != nil {
|
||||
return nil, fmt.Errorf("failed to add key for term %d: %v", key.Term, err)
|
||||
}
|
||||
}
|
||||
return k, nil
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKeyring(t *testing.T) {
|
||||
k := NewKeyring()
|
||||
|
||||
// Term should be 0
|
||||
if term := k.ActiveTerm(); term != 0 {
|
||||
t.Fatalf("bad: %d", term)
|
||||
}
|
||||
|
||||
// Should have no key
|
||||
if key := k.ActiveKey(); key != nil {
|
||||
t.Fatalf("bad: %v", key)
|
||||
}
|
||||
|
||||
// Add a key
|
||||
testKey := []byte("testing")
|
||||
err := k.AddKey(1, testKey)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Term should be 1
|
||||
if term := k.ActiveTerm(); term != 1 {
|
||||
t.Fatalf("bad: %d", term)
|
||||
}
|
||||
|
||||
// Should have key
|
||||
key := k.ActiveKey()
|
||||
if key == nil {
|
||||
t.Fatalf("bad: %v", key)
|
||||
}
|
||||
if !bytes.Equal(key.Value, testKey) {
|
||||
t.Fatalf("bad: %v", key)
|
||||
}
|
||||
if tKey := k.TermKey(1); tKey != key {
|
||||
t.Fatalf("bad: %v", tKey)
|
||||
}
|
||||
|
||||
// Should handle idempotent set
|
||||
err = k.AddKey(1, testKey)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Should not allow conficting set
|
||||
testConflict := []byte("nope")
|
||||
err = k.AddKey(1, testConflict)
|
||||
if err == nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Add a new key
|
||||
testSecond := []byte("second")
|
||||
err = k.AddKey(2, testSecond)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Term should be 2
|
||||
if term := k.ActiveTerm(); term != 2 {
|
||||
t.Fatalf("bad: %d", term)
|
||||
}
|
||||
|
||||
// Should have key
|
||||
newKey := k.ActiveKey()
|
||||
if newKey == nil {
|
||||
t.Fatalf("bad: %v", key)
|
||||
}
|
||||
if !bytes.Equal(newKey.Value, testSecond) {
|
||||
t.Fatalf("bad: %v", key)
|
||||
}
|
||||
if tKey := k.TermKey(2); tKey != newKey {
|
||||
t.Fatalf("bad: %v", tKey)
|
||||
}
|
||||
|
||||
// Read of old key should work
|
||||
if tKey := k.TermKey(1); tKey != key {
|
||||
t.Fatalf("bad: %v", tKey)
|
||||
}
|
||||
|
||||
// Remove the old key
|
||||
err = k.RemoveKey(1)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Read of old key should not work
|
||||
if tKey := k.TermKey(1); tKey != nil {
|
||||
t.Fatalf("bad: %v", tKey)
|
||||
}
|
||||
|
||||
// Remove the active key should fail
|
||||
err = k.RemoveKey(2)
|
||||
if err == nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyring_MasterKey(t *testing.T) {
|
||||
k := NewKeyring()
|
||||
master := []byte("test")
|
||||
master2 := []byte("test2")
|
||||
|
||||
// Check no master
|
||||
out := k.MasterKey()
|
||||
if out != nil {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
|
||||
// Set master
|
||||
k.SetMasterKey(master)
|
||||
out = k.MasterKey()
|
||||
if !bytes.Equal(out, master) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
|
||||
// Update master
|
||||
k.SetMasterKey(master2)
|
||||
out = k.MasterKey()
|
||||
if !bytes.Equal(out, master2) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyring_Serialize(t *testing.T) {
|
||||
k := NewKeyring()
|
||||
master := []byte("test")
|
||||
k.SetMasterKey(master)
|
||||
|
||||
testKey := []byte("testing")
|
||||
testSecond := []byte("second")
|
||||
k.AddKey(1, testKey)
|
||||
k.AddKey(2, testSecond)
|
||||
|
||||
buf, err := k.Serialize()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
k2, err := DeserializeKeyring(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out := k2.MasterKey()
|
||||
if !bytes.Equal(out, master) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
|
||||
if k2.ActiveTerm() != k.ActiveTerm() {
|
||||
t.Fatalf("Term mismatch")
|
||||
}
|
||||
|
||||
var i uint32
|
||||
for i = 1; i < k.ActiveTerm(); i++ {
|
||||
key1 := k2.TermKey(i)
|
||||
key2 := k.TermKey(i)
|
||||
if !reflect.DeepEqual(key1, key2) {
|
||||
t.Fatalf("bad: %v %v", key1, key2)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue