blob: 5f314d86fca38075d69c9569329ef179023d8953 [file] [log] [blame]
package security
import (
"bytes"
"errors"
"fmt"
"sync"
"veyron.io/veyron/veyron/security/serialization"
"veyron.io/veyron/veyron2/security"
)
const (
blessingRootsDataFile = "blessingroots.data"
blessingRootsSigFile = "blessingroots.sig"
)
// blessingRoots implements security.BlessingRoots.
type blessingRoots struct {
dir string
signer serialization.Signer
mu sync.RWMutex
store map[string][]security.BlessingPattern // GUARDED_BY(mu)
}
func storeMapKey(root security.PublicKey) (string, error) {
rootBytes, err := root.MarshalBinary()
if err != nil {
return "", err
}
return string(rootBytes), nil
}
func (br *blessingRoots) Add(root security.PublicKey, pattern security.BlessingPattern) error {
key, err := storeMapKey(root)
if err != nil {
return err
}
br.mu.Lock()
defer br.mu.Unlock()
patterns := br.store[key]
for _, p := range patterns {
if p == pattern {
return nil
}
}
br.store[key] = append(patterns, pattern)
if err := br.save(); err != nil {
br.store[key] = patterns[:len(patterns)-1]
return err
}
return nil
}
func (br *blessingRoots) Recognized(root security.PublicKey, blessing string) error {
key, err := storeMapKey(root)
if err != nil {
return err
}
br.mu.RLock()
defer br.mu.RUnlock()
for _, p := range br.store[key] {
if p.MatchedBy(blessing) {
return nil
}
}
return errors.New("PublicKey is not a recognized root for this blessing")
}
// DebugString return a human-readable string encoding of the roots
// DebugString encodes all roots into a string in the following
// format
//
// Public key : Pattern
// <public key> : <pattern>
// ...
// <public key> : <pattern>
func (br *blessingRoots) DebugString() string {
const format = "%-47s : %s\n"
b := bytes.NewBufferString(fmt.Sprintf(format, "Public key", "Pattern"))
for keyBytes, pattern := range br.store {
key, err := security.UnmarshalPublicKey([]byte(keyBytes))
if err != nil {
return fmt.Sprintf("failed to decode public key: %v", err)
}
b.WriteString(fmt.Sprintf(format, key, pattern))
}
return b.String()
}
func (br *blessingRoots) save() error {
if (br.signer == nil) && (br.dir == "") {
return nil
}
return encodeAndStore(br.store, br.dir, blessingRootsDataFile, blessingRootsSigFile, br.signer)
}
// newInMemoryBlessingRoots returns an in-memory security.BlessingRoots.
//
// The returned BlessingRoots is initialized with an empty set of keys.
func newInMemoryBlessingRoots() security.BlessingRoots {
return &blessingRoots{
store: make(map[string][]security.BlessingPattern),
}
}
// newPersistingBlessingRoots returns a security.BlessingRoots that signs
// and persists all updates to the provided directory. Signing is carried
// out using the provided signer.
//
// The returned BlessingRoots is initialized from the existing data present
// in the directory. The data is verified to have been written by a persisting
// BlessingRoots object constructed from the same signer.
//
// Any errors obtained in reading or verifying the data are returned.
func newPersistingBlessingRoots(directory string, signer serialization.Signer) (security.BlessingRoots, error) {
if directory == "" || signer == nil {
return nil, errors.New("directory or signer is not specified")
}
br := &blessingRoots{
store: make(map[string][]security.BlessingPattern),
dir: directory,
signer: signer,
}
if err := decodeFromStorage(&br.store, br.dir, blessingRootsDataFile, blessingRootsSigFile, br.signer.PublicKey()); err != nil {
return nil, err
}
return br, nil
}