blob: c2828a0073d2dd53e0b7fb8ef45684207833d978 [file] [log] [blame]
package security
import (
"errors"
"fmt"
"reflect"
"strings"
"time"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
)
type setPublicID []security.PublicID
var (
errEmptySet = errors.New("suspected manipulation of identity: empty set")
errSingleElementSet = errors.New("suspected manipulation of identity: single element set")
errNoNilsInSet = errors.New("suspected manipulation of identity: nil element in set")
errMismatchedKeys = errors.New("mismatched keys in elements of set")
)
// NewSetPublicID returns a security.PublicID containing the combined credentials of ids.
// It requires that all elements of ids have the same public key.
func NewSetPublicID(ids ...security.PublicID) (security.PublicID, error) {
// All public keys must match
switch len(ids) {
case 0:
return nil, nil
case 1:
return ids[0], nil
default:
for i := 1; i < len(ids); i++ {
if !reflect.DeepEqual(ids[0].PublicKey(), ids[i].PublicKey()) {
return nil, errMismatchedKeys
}
}
set := setPublicID(ids)
return &set, nil
}
}
func (s *setPublicID) Names() []string {
names := make([]string, 0, len(*s))
for _, id := range *s {
names = append(names, id.Names()...)
}
if len(names) == 0 {
return nil
}
return names
}
func (s *setPublicID) PublicKey() security.PublicKey {
return (*s)[0].PublicKey()
}
func (s *setPublicID) Authorize(context security.Context) (security.PublicID, error) {
var authids []security.PublicID
var errs []error
for _, id := range *s {
if aid, err := id.Authorize(context); err != nil && len(authids) == 0 {
errs = append(errs, err)
} else if aid != nil {
authids = append(authids, aid)
}
}
if len(authids) == 0 {
return nil, joinerrs(errs)
}
return NewSetPublicID(authids...)
}
func (s *setPublicID) ThirdPartyCaveats() []security.ThirdPartyCaveat {
set := make(map[string]security.ThirdPartyCaveat)
for _, id := range *s {
for _, tp := range id.ThirdPartyCaveats() {
set[tp.ID()] = tp
}
}
if len(set) == 0 {
return nil
}
ret := make([]security.ThirdPartyCaveat, 0, len(set))
for _, c := range set {
ret = append(ret, c)
}
return ret
}
func (s *setPublicID) String() string {
strs := make([]string, len(*s))
for ix, id := range *s {
strs[ix] = fmt.Sprintf("%v", id)
}
return strings.Join(strs, "#")
}
func (s *setPublicID) VomEncode() ([]security.PublicID, error) {
return []security.PublicID(*s), nil
}
func (s *setPublicID) VomDecode(ids []security.PublicID) error {
switch len(ids) {
case 0:
return errEmptySet
case 1:
return errSingleElementSet
}
if ids[0] == nil {
return errNoNilsInSet
}
for i := 1; i < len(ids); i++ {
if ids[i] == nil {
return errNoNilsInSet
}
if !reflect.DeepEqual(ids[0].PublicKey(), ids[i].PublicKey()) {
return errMismatchedKeys
}
}
*s = setPublicID(ids)
return nil
}
type setPrivateID []security.PrivateID
// NewSetPrivateID returns a security.PrivateID contiaining the combined credentials of ids.
// It requires that all ids have the same private key.
func NewSetPrivateID(ids ...security.PrivateID) (security.PrivateID, error) {
switch len(ids) {
case 0:
return nil, nil
case 1:
return ids[0], nil
default:
pub := ids[0].PublicID().PublicKey()
for i := 1; i < len(ids); i++ {
if !reflect.DeepEqual(pub, ids[i].PublicID().PublicKey()) {
return nil, errMismatchedKeys
}
}
return setPrivateID(ids), nil
}
}
func (s setPrivateID) PublicID() security.PublicID {
pubs := make([]security.PublicID, len(s))
for ix, id := range s {
pubs[ix] = id.PublicID()
}
set := setPublicID(pubs)
return &set
}
func (s setPrivateID) Sign(message []byte) (security.Signature, error) { return s[0].Sign(message) }
func (s setPrivateID) PublicKey() security.PublicKey { return s[0].PublicKey() }
func (s setPrivateID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.Caveat) (security.PublicID, error) {
pubs := make([]security.PublicID, len(s))
for ix, id := range s {
var err error
if pubs[ix], err = id.Bless(blessee, blessingName, duration, caveats); err != nil {
return nil, err
}
}
return NewSetPublicID(pubs...)
}
func (s setPrivateID) Derive(pub security.PublicID) (security.PrivateID, error) {
switch p := pub.(type) {
case *chainPublicID:
return s[0].Derive(p)
case *setPublicID:
privs := make([]security.PrivateID, len(*p))
var err error
for ix, ip := range *p {
if privs[ix], err = s.Derive(ip); err != nil {
return nil, fmt.Errorf("Derive failed for %d of %d id in set", ix, len(*p))
}
}
return setPrivateID(privs), nil
default:
return nil, fmt.Errorf("PrivateID of type %T cannot be used to Derive from PublicID of type %T", s, pub)
}
}
func (s setPrivateID) MintDischarge(cav security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, dischargeCaveats []security.Caveat) (security.Discharge, error) {
for _, id := range s {
if d, err := id.MintDischarge(cav, ctx, duration, dischargeCaveats); err == nil {
return d, nil
}
}
return nil, fmt.Errorf("discharge cannot be constructed for %T from %T", cav, s)
}
func joinerrs(errs []error) error {
switch len(errs) {
case 0:
return nil
case 1:
return errs[0]
}
strs := make([]string, len(errs))
for i, err := range errs {
strs[i] = err.Error()
}
return fmt.Errorf("none of the blessings in the set are authorized: %v", strings.Join(strs, ", "))
}
func init() {
vom.Register(setPrivateID(nil))
vom.Register(setPublicID(nil))
}