Merge "veyron/runtimes/google/security: Preparation for moving identity implementation to the public API and to VDL."
diff --git a/runtimes/google/security/identity_chain.go b/runtimes/google/security/identity_chain.go
index 0c03280..a15d1de 100644
--- a/runtimes/google/security/identity_chain.go
+++ b/runtimes/google/security/identity_chain.go
@@ -129,15 +129,16 @@
func (id *chainPrivateID) String() string { return fmt.Sprintf("PrivateID:%v", id.publicID) }
func (id *chainPrivateID) VomEncode() (*wire.ChainPrivateID, error) {
- var err error
- w := &wire.ChainPrivateID{Secret: id.privateKey.D.Bytes()}
- w.PublicID, err = id.publicID.VomEncode()
- return w, err
+ pub, err := id.publicID.VomEncode()
+ if err != nil {
+ return nil, err
+ }
+ return &wire.ChainPrivateID{Secret: id.privateKey.D.Bytes(), PublicID: *pub}, nil
}
func (id *chainPrivateID) VomDecode(w *wire.ChainPrivateID) error {
id.publicID = new(chainPublicID)
- if err := id.publicID.VomDecode(w.PublicID); err != nil {
+ if err := id.publicID.VomDecode(&w.PublicID); err != nil {
return err
}
id.privateKey = &ecdsa.PrivateKey{
@@ -184,21 +185,28 @@
}, nil
}
-// Derive returns a new PrivateID that contains the PrivateID's private key and the
-// provided PublicID. The provided PublicID must have the same public key as the public
-// key of the PrivateID's PublicID.
func (id *chainPrivateID) Derive(pub security.PublicID) (security.PrivateID, error) {
if !reflect.DeepEqual(pub.PublicKey(), id.publicID.publicKey) {
return nil, errDeriveMismatch
}
- chainPub, ok := pub.(*chainPublicID)
- if !ok {
- return nil, fmt.Errorf("PrivateID of type %T cannot be obtained from PublicID of type %T", id, pub)
+ switch p := pub.(type) {
+ case *chainPublicID:
+ return &chainPrivateID{
+ publicID: p,
+ privateKey: id.privateKey,
+ }, nil
+ case *setPublicID:
+ privs := make([]security.PrivateID, len(*p))
+ var err error
+ for ix, ip := range *p {
+ if privs[ix], err = id.Derive(ip); err != nil {
+ return nil, fmt.Errorf("Derive failed for public id %d of %d in set: %v", ix, len(*p), err)
+ }
+ }
+ return setPrivateID(privs), nil
+ default:
+ return nil, fmt.Errorf("PrivateID of type %T cannot be used to Derive from PublicID of type %T", id, pub)
}
- return &chainPrivateID{
- publicID: chainPub,
- privateKey: id.privateKey,
- }, nil
}
func (id *chainPrivateID) MintDischarge(cav security.ThirdPartyCaveat, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
diff --git a/runtimes/google/security/identity_set.go b/runtimes/google/security/identity_set.go
new file mode 100644
index 0000000..05dd296
--- /dev/null
+++ b/runtimes/google/security/identity_set.go
@@ -0,0 +1,227 @@
+package security
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+ "time"
+
+ "veyron2/security"
+ "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) Match(pattern security.PrincipalPattern) bool {
+ for _, id := range *s {
+ if id.Match(pattern) {
+ return true
+ }
+ }
+ return false
+}
+
+func (s *setPublicID) PublicKey() *ecdsa.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.ServiceCaveat {
+ set := make(map[security.ThirdPartyCaveatID]security.ServiceCaveat)
+ for _, id := range *s {
+ for _, c := range id.ThirdPartyCaveats() {
+ if tp, ok := c.Caveat.(security.ThirdPartyCaveat); ok {
+ set[tp.ID()] = c
+ }
+ // else { This should not be possible! }
+ }
+ }
+ if len(set) == 0 {
+ return nil
+ }
+ ret := make([]security.ServiceCaveat, 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:
+ for i := 1; i < len(ids); i++ {
+ if !reflect.DeepEqual(ids[0].PrivateKey(), ids[i].PrivateKey()) {
+ 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) PrivateKey() *ecdsa.PrivateKey { return s[0].PrivateKey() }
+
+func (s setPrivateID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.ServiceCaveat) (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) {
+ if !reflect.DeepEqual(pub.PublicKey(), &s.PrivateKey().PublicKey) {
+ return nil, errDeriveMismatch
+ }
+ var err error
+ switch p := pub.(type) {
+ case *chainPublicID:
+ return s[0].Derive(p)
+ case *setPublicID:
+ privs := make([]security.PrivateID, len(*p))
+ 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, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
+ for _, id := range s {
+ if d, err := id.MintDischarge(cav, 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))
+}
diff --git a/runtimes/google/security/identity_test.go b/runtimes/google/security/identity_test.go
index a5d7c7b..0f2200f 100644
--- a/runtimes/google/security/identity_test.go
+++ b/runtimes/google/security/identity_test.go
@@ -1,13 +1,17 @@
package security
import (
+ "bytes"
+ "fmt"
"reflect"
+ "sort"
"testing"
"time"
"veyron/runtimes/google/security/caveat"
"veyron/runtimes/google/security/wire"
"veyron2/security"
+ "veyron2/vom"
)
type S []string
@@ -17,28 +21,25 @@
cUnknownAlice = newChain("alice").PublicID()
cTrustedAlice = bless(cUnknownAlice, veyronChain, "alice", nil)
cMistrustedAlice = bless(cUnknownAlice, newChain("veyron"), "alice", nil)
+ cGoogleAlice = bless(cUnknownAlice, googleChain, "alice", nil)
- tUntrustedAlice = newTree("alice").PublicID()
- tTrustedAlice = bless(tUntrustedAlice, veyronTree, "alice", nil)
- tMistrustedAlice = bless(tUntrustedAlice, newTree("veyron"), "alice", nil)
+ sAlice = newSetPublicID(cTrustedAlice, cGoogleAlice)
+ sBadAlice = newSetPublicID(cUnknownAlice, cMistrustedAlice)
+ sGoogleAlice = newSetPublicID(cMistrustedAlice, cGoogleAlice)
)
testdata := []struct {
- id security.PublicID
- names []string
- authErr string
- authNames []string
+ id security.PublicID
+ names []string
+ err string
}{
{id: cUnknownAlice},
- {id: cTrustedAlice, names: S{"veyron/alice"}, authNames: S{"veyron/alice"}},
- {id: cMistrustedAlice, authErr: "Mistrusted"},
- {id: tUntrustedAlice},
- {id: tTrustedAlice, names: S{"veyron/alice"}, authNames: S{"veyron/alice"}},
- {id: tMistrustedAlice},
+ {id: cTrustedAlice, names: S{"veyron/alice"}},
+ {id: cMistrustedAlice, err: "Mistrusted"},
+ {id: sAlice, names: S{"veyron/alice", "google/alice"}},
+ {id: sBadAlice},
+ {id: sGoogleAlice, names: S{"google/alice"}},
}
for _, d := range testdata {
- if len(d.authNames) != 0 && len(d.authErr) != 0 {
- t.Fatalf("Bad testdata. At most one of authNames and authErr must be non-empty: %q", d)
- }
if got, want := d.id.Names(), d.names; !reflect.DeepEqual(got, want) {
t.Errorf("%q(%T).Names(): got: %q, want: %q", d.id, d.id, got, want)
}
@@ -47,16 +48,17 @@
t.Errorf("%q.Authorize returned: (%v, %v), exactly one return value must be nil", d.id, authID, err)
continue
}
- if !matchesErrorPattern(err, d.authErr) {
- t.Errorf("%q.Authorize returned error: %v, want to match: %q", d.id, err, d.authErr)
+ if !matchesErrorPattern(err, d.err) {
+ t.Errorf("%q.Authorize returned error: %v, want to match: %q", d.id, err, d.err)
}
- if err := verifyAuthorizedID(d.id, authID, d.authNames); err != nil {
+ if err := verifyAuthorizedID(d.id, authID, d.names); err != nil {
t.Error(err)
}
}
}
func TestMatch(t *testing.T) {
+ alice := newChain("alice")
type matchInstance struct {
pattern security.PrincipalPattern
want bool
@@ -67,17 +69,7 @@
}{
{
// self-signed alice chain, not a trusted identity provider so should only match "*"
- id: newChain("alice").PublicID(),
- matchData: []matchInstance{
- {pattern: "*", want: true},
- {pattern: "alice", want: false},
- {pattern: "alice/*", want: false},
- {pattern: "untrusted/alice/*", want: false},
- },
- },
- {
- // self-blessed alice tree, not a trusted identity provider so should only match "*"
- id: newTree("alice").PublicID(),
+ id: alice.PublicID(),
matchData: []matchInstance{
{pattern: "*", want: true},
{pattern: "alice", want: false},
@@ -102,11 +94,10 @@
},
{
// alice#veyron/alice#google/alice: two trusted identity providers
- id: bless(bless(newTree("alice").PublicID(), veyronTree, "alice", nil), googleTree, "alice", nil),
+ id: newSetPublicID(alice.PublicID(), bless(alice.PublicID(), veyronChain, "alice", nil), bless(alice.PublicID(), googleChain, "alice", nil)),
matchData: []matchInstance{
{pattern: "*", want: true},
- // Since alice is not a trusted identity
- // provider, the tree's self-blessed identity
+ // Since alice is not a trusted identity provider, the self-blessed identity
// should not match "alice/*"
{pattern: "alice", want: false},
{pattern: "alice/*", want: false},
@@ -135,26 +126,34 @@
}
}
-func TestExpiredIdentity(t *testing.T) {
- testdata := []struct {
- blessor security.PrivateID
- blessee security.PublicID
- }{
- {veyronChain, newChain("alice").PublicID()},
- {veyronTree, newTree("alice").PublicID()},
+func TestExpiredIdentityChain(t *testing.T) {
+ id, err := veyronChain.Bless(newChain("immaterial").PublicID(), "alice", time.Millisecond, nil)
+ if err != nil {
+ t.Fatal(err)
}
- for _, d := range testdata {
- id, err := d.blessor.Bless(d.blessee, "alice", time.Millisecond, nil)
- if err != nil {
- t.Errorf("%q.Bless(%q, ...) failed: %v", d.blessor, d.blessee, err)
- continue
- }
- time.Sleep(time.Millisecond)
- if authID, _ := id.Authorize(NewContext(ContextArgs{})); authID != nil {
- if got := authID.Names(); got != nil {
- t.Errorf("%q.Names(): got: %q, want: nil", got)
- }
- }
+ time.Sleep(time.Millisecond)
+ if authid, _ := id.Authorize(NewContext(ContextArgs{})); authid != nil && len(authid.Names()) != 0 {
+ t.Errorf("%q.Authorize returned %v, wanted empty slice", id, authid)
+ }
+}
+
+func TestExpiredIdentityInSet(t *testing.T) {
+ var (
+ alice = newChain("alice").PublicID()
+ googleAlice = bless(alice, googleChain, "googler", nil)
+ )
+ veyronAlice, err := veyronChain.Bless(alice, "veyroner", time.Millisecond, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ time.Sleep(time.Millisecond)
+ setid := newSetPublicID(alice, googleAlice, veyronAlice)
+ authid, _ := setid.Authorize(NewContext(ContextArgs{}))
+ if authid == nil {
+ t.Fatalf("%q.Authorize returned nil, expected google/alice", setid)
+ }
+ if got, want := authid.Names(), "google/googler"; len(got) != 1 || got[0] != want {
+ t.Errorf("Got %v want [%v]", got, want)
}
}
@@ -172,95 +171,77 @@
}
}
-func TestTamperedIdentityTree(t *testing.T) {
- alice := newTree("alice").PublicID().(*treePublicID)
- // Tamper with the alice's public key
- alice.publicKey.Y.SetInt64(1)
- // Check that integrity verification fails
- if _, err := roundTrip(alice); err != wire.ErrNoIntegrity {
- t.Errorf("Got %v want %v from roundTrip(%v)", err, wire.ErrNoIntegrity, alice)
- }
-}
-
func TestBless(t *testing.T) {
var (
cAlice = newChain("alice")
- cBob = newChain("bob")
+ cBob = newChain("bob").PublicID()
cVeyronAlice = derive(bless(cAlice.PublicID(), veyronChain, "alice", nil), cAlice)
+ cGoogleAlice = derive(bless(cAlice.PublicID(), googleChain, "alice", nil), cAlice)
+ cVeyronBob = bless(cBob, veyronChain, "bob", nil)
- tAlice = newTree("alice")
- tBob = newTree("bob")
- tVeyronAlice = derive(bless(tAlice.PublicID(), veyronTree, "alice", nil), tAlice)
+ sVeyronAlice = newSetPrivateID(cAlice, cVeyronAlice, cGoogleAlice)
+ sVeyronBob = newSetPublicID(cBob, cVeyronBob)
)
testdata := []struct {
- blessor security.PrivateID
- blessee security.PublicID
- blessing string // name provided to security.PublicID.Bless
- blessedNames []string // names of the blessed identity. Empty if the Bless operation should have failed
- err string
+ blessor security.PrivateID
+ blessee security.PublicID
+ blessing string // name provided to security.PublicID.Bless
+ blessed []string // names of the blessed identity. Empty if the Bless operation should have failed
+ err string
}{
+ // Blessings from veyron/alice (chain implementation)
{
- blessor: veyronChain,
- blessee: cAlice.PublicID(),
+ blessor: cVeyronAlice,
+ blessee: cBob,
err: `invalid blessing name:""`,
},
{
- blessor: veyronChain,
- blessee: cAlice.PublicID(),
+ blessor: cVeyronAlice,
+ blessee: cBob,
blessing: "alice/bob",
err: `invalid blessing name:"alice/bob"`,
},
{
- blessor: veyronChain,
- blessee: cAlice.PublicID(),
- blessing: "alice",
- blessedNames: S{"veyron/alice"},
- },
- {
- blessor: cVeyronAlice,
- blessee: cBob.PublicID(),
- blessing: "friend_bob",
- blessedNames: S{"veyron/alice/friend_bob"},
- },
- {
- blessor: cAlice,
- blessee: cBob.PublicID(),
+ blessor: cVeyronAlice,
+ blessee: cBob,
blessing: "friend_bob",
+ blessed: S{"veyron/alice/friend_bob"},
},
{
- blessor: veyronTree,
- blessee: tAlice.PublicID(),
+ blessor: cVeyronAlice,
+ blessee: sVeyronBob,
+ blessing: "friend_bob",
+ blessed: S{"veyron/alice/friend_bob"},
+ },
+ // Blessings from alice#google/alice#veyron/alice
+ {
+ blessor: sVeyronAlice,
+ blessee: cBob,
err: `invalid blessing name:""`,
},
{
- blessor: veyronTree,
- blessee: tAlice.PublicID(),
+ blessor: sVeyronAlice,
+ blessee: cBob,
blessing: "alice/bob",
err: `invalid blessing name:"alice/bob"`,
},
{
- blessor: veyronTree,
- blessee: tAlice.PublicID(),
- blessing: "alice",
- blessedNames: S{"veyron/alice"},
+ blessor: sVeyronAlice,
+ blessee: cBob,
+ blessing: "friend_bob",
+ blessed: S{"veyron/alice/friend_bob", "google/alice/friend_bob"},
},
{
- blessor: tVeyronAlice,
- blessee: tBob.PublicID(),
- blessing: "friend_bob",
- blessedNames: S{"veyron/alice/friend_bob"},
- },
- {
- blessor: googleTree,
- blessee: tVeyronAlice.PublicID(),
- blessing: "googler",
- blessedNames: S{"veyron/alice", "google/googler"},
+ blessor: sVeyronAlice,
+ blessee: sVeyronBob,
+ blessing: "friend_bob",
+ blessed: S{"veyron/alice/friend_bob", "google/alice/friend_bob"},
},
}
for _, d := range testdata {
- if len(d.blessedNames) != 0 && len(d.err) != 0 {
- t.Fatalf("Bad testdata. At most one of blessedNames and err must be non-empty: %q", d)
+ if (len(d.blessed) == 0) == (len(d.err) == 0) {
+ t.Fatalf("Bad testdata. Exactly one of blessed and err must be non-empty: %+v", d)
}
blessed, err := d.blessor.Bless(d.blessee, d.blessing, 1*time.Minute, nil)
// Exactly one of (blessed, err) should be nil
@@ -276,7 +257,7 @@
continue
}
// Compare names
- if got, want := blessed.Names(), d.blessedNames; !reflect.DeepEqual(got, want) {
+ if got, want := blessed.Names(), d.blessed; !reflect.DeepEqual(got, want) {
t.Errorf("%q.Names(): got: %q, want: %q", blessed, got, want)
}
// Public keys should match for blessed and blessee
@@ -292,19 +273,14 @@
func TestAuthorizeWithCaveats(t *testing.T) {
var (
- // Alice's chain and tree identities
+ // alice
pcAlice = newChain("alice")
cAlice = pcAlice.PublicID().(*chainPublicID)
- ptAlice = newTree("alice")
- tAlice = ptAlice.PublicID().(*treePublicID)
// veyron/alice/tv
cVeyronAliceTV = bless(newChain("tv").PublicID(),
derive(bless(cAlice, veyronChain, "alice", nil), pcAlice),
"tv", nil).(*chainPublicID)
- tVeyronAliceTV = bless(newTree("tv").PublicID(),
- derive(bless(tAlice, veyronTree, "alice", nil), ptAlice),
- "tv", nil).(*treePublicID)
// Some random server called bob
bob = newChain("bob").PublicID()
@@ -335,66 +311,43 @@
tests: []rpc{
{server: bob, method: "Hello", authNames: S{"veyron/alice"}},
{server: bob, authNames: S{"veyron/alice"}},
- {server: googleTree.PublicID(), method: "Hello", authErr: "caveat.MethodRestriction{\"Play\"} forbids invocation of method Hello"},
- {server: googleTree.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
- {server: googleTree.PublicID(), authNames: S{"veyron/alice"}},
+ {server: googleChain.PublicID(), method: "Hello", authErr: `caveat.MethodRestriction{"Play"} forbids invocation of method Hello`},
+ {server: googleChain.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
+ {server: googleChain.PublicID(), authNames: S{"veyron/alice"}},
},
},
{
client: bless(cAlice, veyronChain, "alice", cavOnlyGoogle),
tests: []rpc{
- {server: bob, method: "Hello", authErr: "caveat.PeerIdentity{\"google\"} forbids RPCing with peer"},
- {server: googleTree.PublicID(), method: "Hello", authNames: S{"veyron/alice"}},
- {server: googleTree.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
+ {server: bob, method: "Hello", authErr: `caveat.PeerIdentity{"google"} forbids RPCing with peer`},
+ {server: googleChain.PublicID(), method: "Hello", authNames: S{"veyron/alice"}},
+ {server: googleChain.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
},
},
{
client: bless(cAlice, veyronChain, "alice", append(cavOnlyGoogle, cavOnlyPlayAtGoogle...)),
tests: []rpc{
- {server: bob, method: "Hello", authErr: "caveat.PeerIdentity{\"google\"} forbids RPCing with peer"},
- {server: googleTree.PublicID(), method: "Hello", authErr: "caveat.MethodRestriction{\"Play\"} forbids invocation of method Hello"},
- {server: googleTree.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
+ {server: bob, method: "Hello", authErr: `caveat.PeerIdentity{"google"} forbids RPCing with peer`},
+ {server: googleChain.PublicID(), method: "Hello", authErr: `caveat.MethodRestriction{"Play"} forbids invocation of method Hello`},
+ {server: googleChain.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
},
},
{
client: bless(cAlice, veyronChain, "alice", cavOnlyPublicProfile),
tests: []rpc{
- {server: cVeyronAliceTV, method: "PrivateProfile", authErr: "caveat.MethodRestriction{\"PublicProfile\"} forbids invocation of method PrivateProfile"},
+ {server: cVeyronAliceTV, method: "PrivateProfile", authErr: `caveat.MethodRestriction{"PublicProfile"} forbids invocation of method PrivateProfile`},
{server: cVeyronAliceTV, method: "PublicProfile", authNames: S{"veyron/alice"}},
},
},
- // client has a tree identity
+ // client has multiple blessings
{
- client: bless(tAlice, veyronTree, "alice", cavOnlyPlayAtGoogle),
+ client: newSetPublicID(bless(cAlice, veyronChain, "valice", append(cavOnlyPlayAtGoogle, cavOnlyPublicProfile...)), bless(cAlice, googleChain, "galice", cavOnlyGoogle)),
tests: []rpc{
- {server: bob, method: "Hello", authNames: S{"veyron/alice"}},
- {server: bob, authNames: S{"veyron/alice"}},
- {server: googleTree.PublicID(), method: "Hello"},
- {server: googleTree.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
- {server: googleTree.PublicID(), authNames: S{"veyron/alice"}},
- },
- },
- {
- client: bless(tAlice, veyronTree, "alice", cavOnlyGoogle),
- tests: []rpc{
- {server: bob, method: "Hello"},
- {server: googleTree.PublicID(), method: "Hello", authNames: S{"veyron/alice"}},
- {server: googleTree.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
- },
- },
- {
- client: bless(tAlice, veyronTree, "alice", append(cavOnlyGoogle, cavOnlyPlayAtGoogle...)),
- tests: []rpc{
- {server: bob, method: "Hello"},
- {server: googleTree.PublicID(), method: "Hello"},
- {server: googleTree.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
- },
- },
- {
- client: bless(tAlice, veyronTree, "alice", cavOnlyPublicProfile),
- tests: []rpc{
- {server: tVeyronAliceTV, method: "PrivateProfile"},
- {server: tVeyronAliceTV, method: "PublicProfile", authNames: S{"veyron/alice"}},
+ {server: bob, method: "Hello", authNames: S{"veyron/valice"}},
+ {server: googleChain.PublicID(), method: "Play", authNames: S{"veyron/valice", "google/galice"}},
+ {server: googleChain.PublicID(), method: "Buy", authNames: S{"google/galice"}},
+ {server: cVeyronAliceTV, method: "PublicProfile", authNames: S{"veyron/valice"}},
+ {server: cVeyronAliceTV, method: "PrivateProfile", authErr: "none of the blessings in the set are authorized"},
},
},
}
@@ -405,8 +358,8 @@
continue
}
for _, test := range d.tests {
- if len(test.authNames) != 0 && len(test.authErr) != 0 {
- t.Fatalf("Bad testdata. At most one of authNames and authErr must be non-empty: %q, %+v", d.client, test)
+ if (len(test.authNames) == 0) == (len(test.authErr) == 0) {
+ t.Fatalf("Bad testdata. Exactly one of authNames and authErr must be non-empty: %+q/%+v", d.client, test)
}
ctx := NewContext(ContextArgs{LocalID: test.server, RemoteID: d.client, Method: test.method})
authID, err := d.client.Authorize(ctx)
@@ -414,51 +367,51 @@
t.Errorf("%q.Authorize(%v) returned error: %v, want to match: %q", d.client, ctx, err, test.authErr)
}
if err := verifyAuthorizedID(d.client, authID, test.authNames); err != nil {
- t.Errorf("%q.Authorize(%v) returned identity: %v want identity with names: %q", d.client, ctx, authID, test.authNames)
+ t.Errorf("%q.Authorize(%v) returned identity: %v want identity with names: %q [%v]", d.client, ctx, authID, test.authNames, err)
}
}
}
}
func TestAuthorizeWithThirdPartyCaveats(t *testing.T) {
- mkveyronchain := func(name string) security.PrivateID {
- base := newChain(name)
- return derive(bless(base.PublicID(), veyronChain, name, nil), base)
+ mkveyron := func(id security.PrivateID, name string) security.PrivateID {
+ return derive(bless(id.PublicID(), veyronChain, name, nil), id)
}
- mkveyrontree := func(name string) security.PrivateID {
- base := newTree(name)
- return derive(bless(base.PublicID(), veyronTree, name, nil), base)
+ mkgoogle := func(id security.PrivateID, name string) security.PrivateID {
+ return derive(bless(id.PublicID(), googleChain, name, nil), id)
}
- // Principals (type conversions just to protect against accidentally
- // calling the wrong factory function)
+ mkcaveat := func(id security.PrivateID) []security.ServiceCaveat {
+ c, err := caveat.NewPublicKeyCaveat("proximity", id.PublicID(), fmt.Sprintf("%v location", id.PublicID()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ return []security.ServiceCaveat{security.UniversalCaveat(c)}
+ }
var (
- alice = mkveyronchain("alice")
- cBob = mkveyronchain("bob").(*chainPrivateID)
- tBob = mkveyrontree("bob").(*treePrivateID)
- cCarol = mkveyronchain("carol").(*chainPrivateID).PublicID()
- tCarol = mkveyrontree("carol").(*treePrivateID).PublicID()
+ alice = newChain("alice")
+ bob = newChain("bob")
+ carol = newChain("carol").PublicID()
+
+ // aliceProximityCaveat is a caveat whose discharge can only be minted by alice.
+ aliceProximityCaveat = mkcaveat(alice)
+ bobProximityCaveat = mkcaveat(bob)
)
- // aliceProximityCaveat is a caveat that can only be minted by alice
- aliceProximityCaveat, err := caveat.NewPublicKeyCaveat("proximity", alice.PublicID(), "alice location")
- if err != nil {
- t.Fatal(err)
- }
+
mintDischarge := func(id security.PrivateID, duration time.Duration, caveats []security.ServiceCaveat) security.ThirdPartyDischarge {
- d, err := id.MintDischarge(aliceProximityCaveat, duration, caveats)
+ d, err := id.MintDischarge(aliceProximityCaveat[0].Caveat.(security.ThirdPartyCaveat), duration, caveats)
if err != nil {
t.Fatalf("%q.MintDischarge failed: %v", id, err)
}
return d
}
- // Discharges
var (
+ // Discharges
dAlice = mintDischarge(alice, time.Minute, nil)
dGoogle = mintDischarge(alice, time.Minute, peerIdentityCaveat("google"))
dExpired = mintDischarge(alice, 0, nil)
- dInvalid = mintDischarge(cBob, time.Minute, nil) // Invalid because carol cannot mint valid discharges for aliceProximityCaveat
- )
- // Contexts
- var (
+ dInvalid = mintDischarge(bob, time.Minute, nil) // Invalid because bob cannot mint valid discharges for aliceProximityCaveat
+
+ // Contexts
ctxEmpty = NewContext(ContextArgs{Debug: "ctxEmpty"})
ctxAlice = NewContext(ContextArgs{
Discharges: security.CaveatDischargeMap{dAlice.CaveatID(): dAlice},
@@ -484,57 +437,69 @@
Discharges: security.CaveatDischargeMap{dInvalid.CaveatID(): dInvalid},
Debug: "ctxInvalid",
})
+
+ // Contexts that should always end in authorization errors
+ errtests = map[security.Context]string{
+ ctxEmpty: "missing discharge",
+ ctxGoogleAtOther: "forbids RPCing with peer",
+ ctxExpired: "at this time",
+ ctxInvalid: "invalid signature",
+ }
)
- type want struct {
- // Exactly one of these should be non-empty
- authNames []string
- err string
- }
-
- chaintests := map[security.Context]want{
- ctxEmpty: want{err: "missing discharge"},
- ctxAlice: want{authNames: S{"veyron/bob/friend"}},
- ctxGoogleAtOther: want{err: "forbids RPCing with peer"},
- ctxGoogleAtGoogle: want{authNames: S{"veyron/bob/friend"}},
- ctxExpired: want{err: "at this time"},
- ctxInvalid: want{err: "invalid signature"},
- }
- treetests := map[security.Context]want{
- ctxEmpty: want{authNames: S{"veyron/carol"}},
- ctxAlice: want{authNames: S{"veyron/carol", "veyron/bob/friend"}},
- ctxGoogleAtOther: want{authNames: S{"veyron/carol"}},
- ctxGoogleAtGoogle: want{authNames: S{"veyron/carol", "veyron/bob/friend"}},
- ctxExpired: want{authNames: S{"veyron/carol"}},
- ctxInvalid: want{authNames: S{"veyron/carol"}},
- }
- caveats := []security.ServiceCaveat{security.UniversalCaveat(aliceProximityCaveat)}
testdata := []struct {
- id security.PublicID
- tests map[security.Context]want
+ id security.PublicID
+ authNames S // For ctxAlice and ctxGoogleAtGoogle
}{
- {bless(cCarol, cBob, "friend", caveats), chaintests},
- {bless(tCarol, tBob, "friend", caveats), treetests},
+ // carol blessed by bob with the third-party caveat should be authorized when the context contains a valid discharge
+ {
+ id: bless(carol, mkveyron(bob, "bob"), "friend", aliceProximityCaveat),
+ authNames: S{"veyron/bob/friend"},
+ },
+ // veyron/vbob/vfriend with bobProximityCaveat and google/gbob/gfriend with aliceProximityCaveat
+ // Only google/gbob/gfriend should be authorized since the discharge for the former is missing
+ {
+ id: newSetPublicID(
+ bless(carol, mkveyron(bob, "vbob"), "vfriend", bobProximityCaveat),
+ bless(carol, mkgoogle(bob, "gbob"), "gfriend", aliceProximityCaveat)),
+ authNames: S{"google/gbob/gfriend"},
+ },
+ // veyron/vbob/friend#google/gbob/friend both have the same caveat and both are satisfied
+ {
+ id: bless(carol, newSetPrivateID(mkveyron(bob, "vbob"), mkgoogle(bob, "gbob")), "friend", aliceProximityCaveat),
+ authNames: S{"veyron/vbob/friend", "google/gbob/friend"},
+ },
}
- for _, d := range testdata {
- if _, err := roundTrip(d.id); err != nil {
- t.Errorf("%q is not round-trippable: %v", d.id, d.id, err)
+ for _, test := range testdata {
+ if _, err := roundTrip(test.id); err != nil {
+ t.Errorf("%q is not round-trippable: %v", test.id, test.id, err)
}
- for ctx, want := range d.tests {
- if (len(want.authNames) != 0) && (len(want.err) != 0) {
- t.Fatalf("Bad testdata. Atmost one of (authNames, err) must be non-empty: %q, %v", d.id, ctx)
+ for _, ctx := range []security.Context{ctxAlice, ctxGoogleAtGoogle} {
+ authID, _ := test.id.Authorize(ctx)
+ if err := verifyAuthorizedID(test.id, authID, test.authNames); err != nil {
+ t.Errorf("%q.Authorize(%v): %v", test.id, ctx, err)
}
- authID, err := d.id.Authorize(ctx)
- if !matchesErrorPattern(err, want.err) {
- t.Errorf("%q.Authorize(%v) returned error: %v, want to match: %q", d.id, ctx, err, want.err)
+ }
+ for ctx, want := range errtests {
+ authID, err := test.id.Authorize(ctx)
+ if authID != nil {
+ t.Errorf("%q.Authorize(%v) returned %v, should have returned nil", test.id, ctx, authID)
}
- if err := verifyAuthorizedID(d.id, authID, want.authNames); err != nil {
- t.Errorf("%q.Authorize(%v): %v", d.id, ctx, err)
+ if !matchesErrorPattern(err, want) {
+ t.Errorf("%q.Authorize(%v) returned error: %v, want to match: %q", test.id, ctx, err, want)
}
}
}
}
+type SortedThirdPartyCaveats []security.ServiceCaveat
+
+func (s SortedThirdPartyCaveats) Len() int { return len(s) }
+func (s SortedThirdPartyCaveats) Less(i, j int) bool {
+ return s[i].Caveat.(security.ThirdPartyCaveat).ID() < s[j].Caveat.(security.ThirdPartyCaveat).ID()
+}
+func (s SortedThirdPartyCaveats) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
func TestThirdPartyCaveatAccessors(t *testing.T) {
mkTPCaveat := func(restriction string, id security.PublicID) security.ThirdPartyCaveat {
tpCav, err := caveat.NewPublicKeyCaveat(restriction, id, "someLocation")
@@ -551,48 +516,57 @@
return d
}
- // Principals (type conversions just to protect against accidentally
- // calling the wrong factory function)
+ sortTPCaveats := func(c []security.ServiceCaveat) []security.ServiceCaveat {
+ sort.Stable(SortedThirdPartyCaveats(c))
+ return c
+ }
+
var (
- alice = newChain("alice").(*chainPrivateID)
- cBob = newChain("bob").(*chainPrivateID)
- tBob = newTree("bob").(*treePrivateID)
- )
- // Caveats
- var (
+ // Principals (type conversions just to protect against accidentally
+ // calling the wrong factory function)
+ alice = newChain("alice").(*chainPrivateID)
+ cBob = newChain("bob").(*chainPrivateID)
+ cBobBuilder = derive(bless(cBob.PublicID(), cBob, "builder", nil), cBob) // Bob also calls himself bob/builder
+ sBob = newSetPrivateID(cBob, cBobBuilder).(setPrivateID)
+
+ // Caveats
tpCavService = security.ServiceCaveat{Service: "someService", Caveat: mkTPCaveat("foo", alice.PublicID())}
tpCavUniversal = security.UniversalCaveat(mkTPCaveat("bar", alice.PublicID()))
cav = methodRestrictionCaveat("someService", nil)[0]
)
- caveatsData := []struct {
- caveats []security.ServiceCaveat
- thirdPartyCaveats []security.ServiceCaveat
+ caveats := []struct {
+ firstparty []security.ServiceCaveat
+ thirdparty []security.ServiceCaveat
}{
- {caveats: nil, thirdPartyCaveats: nil},
- {caveats: []security.ServiceCaveat{cav}, thirdPartyCaveats: nil},
- {caveats: []security.ServiceCaveat{tpCavService}, thirdPartyCaveats: []security.ServiceCaveat{tpCavService}},
- {caveats: []security.ServiceCaveat{tpCavService, tpCavUniversal}, thirdPartyCaveats: []security.ServiceCaveat{tpCavService, tpCavUniversal}},
- {caveats: []security.ServiceCaveat{tpCavService, cav, tpCavUniversal}, thirdPartyCaveats: []security.ServiceCaveat{tpCavService, tpCavUniversal}},
+ {firstparty: nil, thirdparty: nil},
+ {firstparty: []security.ServiceCaveat{cav}},
+ {thirdparty: []security.ServiceCaveat{tpCavService}},
+ {thirdparty: []security.ServiceCaveat{tpCavService, tpCavUniversal}},
+ {firstparty: []security.ServiceCaveat{cav}, thirdparty: []security.ServiceCaveat{tpCavService, tpCavUniversal}},
}
testdata := []struct {
privID security.PrivateID
pubID security.PublicID
}{
- {privID: veyronChain, pubID: cBob.PublicID()},
- {privID: veyronTree, pubID: tBob.PublicID()},
+ {privID: veyronChain, pubID: cBob.PublicID()}, // Chain blessing a chain
+ {privID: veyronChain, pubID: sBob.PublicID()}, // Chain blessing a set
+ {privID: sBob, pubID: cBob.PublicID()}, // Set blessing a chain
+ //{privID: veyronTree, pubID: tBob.PublicID()},
}
for _, d := range testdata {
- for _, c := range caveatsData {
+ for _, c := range caveats {
+ all := append(c.firstparty, c.thirdparty...)
// Test ThirdPartyCaveat accessors on security.PublicIDs.
- id := bless(d.pubID, d.privID, "irrelevant", c.caveats)
- if got, want := id.ThirdPartyCaveats(), c.thirdPartyCaveats; !reflect.DeepEqual(got, want) {
- t.Errorf("Test credential %q with caveats %+v: got ThirdPartyCaveats() = %+v, want %+v", id, c.caveats, got, want)
+ id := bless(d.pubID, d.privID, "irrelevant", all)
+ want := sortTPCaveats(c.thirdparty)
+ if got := sortTPCaveats(id.ThirdPartyCaveats()); !reflect.DeepEqual(got, want) {
+ t.Errorf("%q(%T) got ThirdPartyCaveats() = %+v, want %+v", id, id, got, want)
}
// Test ThirdPartyCaveat accessors on security.ThirdPartyCaveatDischarges.
- dis := mintDischarge(mkTPCaveat("baz", alice.PublicID()), d.privID, c.caveats)
- if got, want := dis.ThirdPartyCaveats(), c.thirdPartyCaveats; !reflect.DeepEqual(got, want) {
- t.Errorf("Test credential %q with caveats %+v: got ThirdPartyCaveats() = %+v, want %+v", dis, c.caveats, got, want)
+ dis := mintDischarge(mkTPCaveat("baz", alice.PublicID()), d.privID, all)
+ if got := sortTPCaveats(dis.ThirdPartyCaveats()); !reflect.DeepEqual(got, want) {
+ t.Errorf("%q got ThirdPartyCaveats() = %+v, want %+v", dis, got, want)
}
}
}
@@ -666,24 +640,28 @@
cAlice = newChain("alice")
cVeyronAlice = bless(cAlice.PublicID(), veyronChain, "alice", nil)
cBob = newChain("bob").PublicID()
- tAlice = newTree("alice")
- tVeyronAlice = bless(tAlice.PublicID(), veyronTree, "alice", nil)
- tBob = newTree("bob").PublicID()
+ sVeyronAlice = newSetPrivateID(cAlice, derive(cVeyronAlice, cAlice))
+
+ tChain = reflect.TypeOf(cAlice)
+ tSet = reflect.TypeOf(sVeyronAlice)
+ tErr = reflect.TypeOf(nil)
)
testdata := []struct {
priv security.PrivateID
pub security.PublicID
- err bool
+ typ reflect.Type
}{
- {priv: cAlice, pub: cVeyronAlice},
- {priv: cAlice, pub: cBob, err: true},
- {priv: tAlice, pub: tVeyronAlice},
- {priv: tAlice, pub: tBob, err: true},
+ {priv: cAlice, pub: cVeyronAlice, typ: tChain}, // chain.Derive(chain) = chain
+ {priv: cAlice, pub: sVeyronAlice.PublicID(), typ: tSet}, // chain.Derive(set) = set
+ {priv: cAlice, pub: cBob, typ: tErr},
+ {priv: sVeyronAlice, pub: cAlice.PublicID(), typ: tChain}, // set.Derive(chain) = chain
+ {priv: sVeyronAlice, pub: sVeyronAlice.PublicID(), typ: tSet}, // set.Derive(set) = set
+ {priv: sVeyronAlice, pub: cBob, typ: tErr},
}
for _, d := range testdata {
derivedID, err := d.priv.Derive(d.pub)
- if (err != nil) != d.err {
- t.Errorf("%q.Derive(%q) returned error: %v, wanted: %t", d.priv, d.pub, err, d.err)
+ if reflect.TypeOf(derivedID) != d.typ {
+ t.Errorf("%T=%q.Derive(%T=%q) yielded %T, want %v", d.priv, d.priv, d.pub, d.pub, derivedID, d.typ)
continue
}
if err != nil {
@@ -702,3 +680,37 @@
}
}
}
+
+func TestNewSetFailures(t *testing.T) {
+ var (
+ alice = newChain("alice")
+ bob = newChain("bob")
+ )
+ if s, err := NewSetPrivateID(alice, bob); err == nil {
+ t.Errorf("Got %v, want error since PrivateKeys do not match", s)
+ }
+ if s, err := NewSetPublicID(alice.PublicID(), bob.PublicID()); err == nil {
+ t.Errorf("Got %v, want error since PublicKeys do not match", s)
+ }
+}
+
+func TestSetIdentityAmplification(t *testing.T) {
+ var (
+ alice = newChain("alice").PublicID()
+ bob = newChain("bob").PublicID()
+
+ sAlice = newSetPublicID(bless(alice, veyronChain, "valice", nil), bless(alice, googleChain, "galice", nil))
+ )
+
+ // Manipulate sAlice before writing it out to the wire so that it has Bob's authorizations.
+ reflect.ValueOf(sAlice).Elem().Index(1).Set(reflect.ValueOf(bob))
+ // Encode/decode the identity.
+ var buf bytes.Buffer
+ if err := vom.NewEncoder(&buf).Encode(sAlice); err != nil {
+ t.Fatal(err)
+ }
+ var decoded security.PublicID
+ if err := vom.NewDecoder(&buf).Decode(&decoded); err == nil || decoded != nil {
+ t.Fatalf("Got (%v, %v), want wire decode of manipulated identity to fail", decoded, err)
+ }
+}
diff --git a/runtimes/google/security/identity_tree.go b/runtimes/google/security/identity_tree.go
deleted file mode 100644
index 6c9eabd..0000000
--- a/runtimes/google/security/identity_tree.go
+++ /dev/null
@@ -1,276 +0,0 @@
-package security
-
-// This file describes a blessing tree based implementation of security.PublicID.
-
-import (
- "bytes"
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/rand"
- "errors"
- "fmt"
- "math/big"
- "reflect"
- "strings"
- "time"
-
- icaveat "veyron/runtimes/google/security/caveat"
- "veyron/runtimes/google/security/keys"
- "veyron/runtimes/google/security/wire"
- "veyron/security/caveat"
- "veyron2/security"
- "veyron2/vom"
-)
-
-// nameSeparator is used to join blessing chain names to form a blessing tree name.
-const nameSeparator = "#"
-
-// treePublicID implements security.PublicID.
-type treePublicID struct {
- blessings []wire.Blessing
- publicKey *ecdsa.PublicKey
- paths []treePath
-}
-
-func (id *treePublicID) Names() []string {
- var names []string
- for _, p := range id.paths {
- if keys.LevelOfTrust(p.providerKey, p.providerName) == keys.Trusted {
- names = append(names, p.name)
- }
- }
- return names
-}
-
-// Match determines if the PublicID's chained name can be extended to match the
-// provided PrincipalPattern. An extension of a chained name is any name obtained
-// by joining additional strings to the name using wire.ChainSeparator. Ex: extensions
-// of the name "foo/bar" are the names "foo/bar", "foo/bar/baz", "foo/bar/baz/car", and
-// so on.
-func (id *treePublicID) Match(pattern security.PrincipalPattern) bool {
- return matchPrincipalPattern(id.Names(), pattern)
-}
-
-func (id *treePublicID) PublicKey() *ecdsa.PublicKey { return id.publicKey }
-
-func (id *treePublicID) String() string {
- var buf bytes.Buffer
- for i, p := range id.paths {
- if i > 0 {
- buf.WriteString(nameSeparator)
- }
- buf.WriteString(p.String())
- }
- return buf.String()
-}
-
-func (id *treePublicID) VomEncode() (*wire.TreePublicID, error) {
- var pKey wire.PublicKey
- if err := pKey.Encode(id.publicKey); err != nil {
- return nil, err
- }
- return &wire.TreePublicID{Blessings: id.blessings, PublicKey: pKey}, nil
-}
-
-func (id *treePublicID) VomDecode(w *wire.TreePublicID) error {
- if err := w.VerifyIntegrity(); err != nil {
- return err
- }
- return id.initFromWire(w)
-}
-
-func (id *treePublicID) initFromWire(w *wire.TreePublicID) error {
- names, providers := w.Names()
- paths := make([]treePath, len(names))
- for i, p := range paths {
- if len(names[i]) == 0 || providers[i] == nil {
- return fmt.Errorf("invalid identity provider: (name=%q, key=%v)", names[i], providers[i])
- }
- p.name = strings.Join(names[i], wire.ChainSeparator)
- p.providerName = names[i][0]
- p.providerKey = providers[i]
- paths[i] = p
- }
- key, err := w.PublicKey.Decode()
- if err != nil {
- return err
- }
- id.blessings = w.Blessings
- id.publicKey = key
- id.paths = paths
- return nil
-}
-
-// Authorize returns a new treePublicID with only those blessing chains from
-// the existing PublicID whose caveats validate with respect to the provided
-// context and whose identity providers (leaf blessings) are trusted. This
-// method assumes that the existing PublicID was obtained after successfully
-// decoding a serialized PublicID and hence has integrity.
-func (id *treePublicID) Authorize(context security.Context) (security.PublicID, error) {
- w, err := id.VomEncode()
- if err != nil {
- return nil, err
- }
- wAuthorizedID := w.Authorize(context)
- if wAuthorizedID == nil {
- return nil, errors.New("tree identity with no blessings")
- }
- authorizedID := &treePublicID{}
- if err := authorizedID.initFromWire(wAuthorizedID); err != nil {
- return nil, err
- }
- return authorizedID, nil
-}
-
-// treePath encapsulates the name created by following blessings that link the
-// public key of a treePublicID to an identity provider.
-type treePath struct {
- name string
- providerName string
- providerKey *ecdsa.PublicKey
-}
-
-func (t *treePath) String() string {
- if keys.LevelOfTrust(t.providerKey, t.providerName) != keys.Trusted {
- return wire.UntrustedIDProviderPrefix + t.name
- }
- return t.name
-}
-
-func (id *treePublicID) ThirdPartyCaveats() (thirdPartyCaveats []security.ServiceCaveat) {
- if w, err := id.VomEncode(); err == nil {
- thirdPartyCaveats = w.ThirdPartyCaveats()
- }
- return
-}
-
-// treePrivateID implements security.PrivateID.
-type treePrivateID struct {
- publicID *treePublicID
- privateKey *ecdsa.PrivateKey
-}
-
-// PublicID returns the PublicID associated with the PrivateID.
-func (id *treePrivateID) PublicID() security.PublicID { return id.publicID }
-
-// PrivateKey returns the private key associated with the PrivateID.
-func (id *treePrivateID) PrivateKey() *ecdsa.PrivateKey { return id.privateKey }
-
-func (id *treePrivateID) String() string { return fmt.Sprintf("PrivateID:%v", id.publicID) }
-
-func (id *treePrivateID) VomEncode() (*wire.TreePrivateID, error) {
- var err error
- w := &wire.TreePrivateID{Secret: id.privateKey.D.Bytes()}
- w.PublicID, err = id.publicID.VomEncode()
- return w, err
-}
-
-func (id *treePrivateID) VomDecode(w *wire.TreePrivateID) error {
- if err := id.publicID.VomDecode(w.PublicID); err != nil {
- return err
- }
- id.privateKey = &ecdsa.PrivateKey{
- PublicKey: *id.publicID.publicKey,
- D: new(big.Int).SetBytes(w.Secret),
- }
- return nil
-}
-
-// Bless returns a new PublicID by extendinig the blessee's blessings with a
-// new one signed by id's private key and named blessingName with caveats and
-// an additional expiry caveat for the provided duration.
-func (id *treePrivateID) Bless(blesseeID security.PublicID, blessingName string, duration time.Duration, caveats []security.ServiceCaveat) (security.PublicID, error) {
- // The integrity of the PublicID blessee is assumed to have been verified
- // (typically by a Vom decode)
- if err := wire.ValidateBlessingName(blessingName); err != nil {
- return nil, err
- }
- blessee, ok := blesseeID.(*treePublicID)
- if !ok {
- return nil, fmt.Errorf("PrivateID of type %T cannot bless PublicID of type %T", id, blesseeID)
- }
- if blessee.blessings == nil {
- return nil, errors.New("blessee must at least have one blessing before it can be blessed further")
- }
-
- blessing := wire.Blessing{Name: blessingName}
- var err error
- if blessing.Blessor, err = id.publicID.VomEncode(); err != nil {
- return nil, err
- }
- now := time.Now()
- caveats = append(caveats, security.UniversalCaveat(&caveat.Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}))
- if blessing.Caveats, err = wire.EncodeCaveats(caveats); err != nil {
- return nil, err
- }
- var pKey wire.PublicKey
- if err := pKey.Encode(blessee.publicKey); err != nil {
- return nil, err
- }
- if err := blessing.Sign(pKey, id.privateKey); err != nil {
- return nil, err
- }
- w := &wire.TreePublicID{
- Blessings: append(blessee.blessings, blessing),
- PublicKey: pKey,
- }
- blessed := &treePublicID{}
- if err := blessed.initFromWire(w); err != nil {
- return nil, err
- }
- return blessed, nil
-}
-
-// Derive returns a new PrivateID containing priv's private key and the provided PublicID.
-// The provided PublicID must have the same public key as the public key of priv's PublicID.
-func (id *treePrivateID) Derive(pub security.PublicID) (security.PrivateID, error) {
- if !reflect.DeepEqual(pub.PublicKey(), id.publicID.publicKey) {
- return nil, errDeriveMismatch
- }
- treePub, ok := pub.(*treePublicID)
- if !ok {
- return nil, fmt.Errorf("PrivateID of type %T cannot be obtained from PublicID of type %T", id, pub)
- }
- return &treePrivateID{
- publicID: treePub,
- privateKey: id.privateKey,
- }, nil
-
-}
-
-func (id *treePrivateID) MintDischarge(cav security.ThirdPartyCaveat, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
- switch c := cav.(type) {
- case *icaveat.PublicKeyCaveat:
- return icaveat.NewPublicKeyDischarge(c, id.privateKey, duration, dischargeCaveats)
- }
- return nil, fmt.Errorf("discharge cannot be constructed for ThirdPartyCaveat of type %T from PrivateID of type %T", cav, id)
-}
-
-// newTreePrivateID returns a new PrivateID containing a freshly generated private
-// key, and a single self-signed blessing for the provided name and the public key
-// corresponding to the generated private key.
-func newTreePrivateID(name string) (security.PrivateID, error) {
- key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
- if err != nil {
- return nil, err
- }
- var pKey wire.PublicKey
- if err := pKey.Encode(&key.PublicKey); err != nil {
- return nil, err
- }
- blessing := wire.Blessing{Name: name}
- if err := blessing.Sign(pKey, key); err != nil {
- return nil, err
- }
- w := &wire.TreePublicID{Blessings: []wire.Blessing{blessing}, PublicKey: pKey}
- publicID := &treePublicID{}
- if err := publicID.initFromWire(w); err != nil {
- return nil, err
- }
- return &treePrivateID{publicID: publicID, privateKey: key}, nil
-}
-
-func init() {
- vom.Register(treePublicID{})
- vom.Register(treePrivateID{})
-}
diff --git a/runtimes/google/security/performance_test.go b/runtimes/google/security/performance_test.go
index ded5af8..06cbe5e 100644
--- a/runtimes/google/security/performance_test.go
+++ b/runtimes/google/security/performance_test.go
@@ -19,21 +19,7 @@
// Wire size with 0 blessings: 676 bytes ("untrusted/X")
// Wire size with 1 blessings: 976 bytes ("untrusted/X/X")
// Wire size with 2 blessings: 1275 bytes ("untrusted/X/X/X")
-//
-// -- tree implementation --
-//
-// BenchmarkNewTree 1338252 ns/op
-// BenchmarkBlessTree 774195 ns/op
-// BenchmarkEncode0BlessingTree 51532 ns/op
-// BenchmarkEncode1BlessingTree 63069 ns/op
-// BenchmarkDecode0BlessingTree 2591321 ns/op
-// BenchmarkDecode1BlessingTree 7575987 ns/op
-//
-// Wire size with 0 blessings: 687 bytes ("untrusted/X")
-// Wire size with 1 blessings: 1066 bytes ("untrusted/X#untrusted/1/X")
-// Wire size with 2 blessings: 1444 bytes ("untrusted/X#untrusted/1/X#untrusted/2/X")
import (
- "fmt"
"testing"
"time"
@@ -118,55 +104,3 @@
priv = derive(bless(pub, priv, "X", nil), priv)
}
}
-
-// -- tree implementation benchmarks --
-
-func BenchmarkNewTree(b *testing.B) {
- for i := 0; i < b.N; i++ {
- if _, err := newTreePrivateID("X"); err != nil {
- b.Fatalf("newTreePrivateID #%d: %v", i, err)
- }
-
- }
-}
-
-func BenchmarkBlessTree(b *testing.B) {
- benchmarkBless(b, newTree("alice"), newTree("bob").PublicID())
-}
-
-func BenchmarkEncode0BlessingTree(b *testing.B) {
- benchmarkEncode(b, newTree("alice").PublicID())
-}
-
-func BenchmarkEncode1BlessingTree(b *testing.B) {
- benchmarkEncode(b, bless(newTree("alice").PublicID(), veyronTree, "alice", nil))
-}
-
-func BenchmarkDecode0BlessingTree(b *testing.B) {
- idBytes, err := encode(newTree("alice").PublicID())
- if err != nil {
- b.Fatal(err)
- }
- benchmarkDecode(b, idBytes)
-}
-
-func BenchmarkDecode1BlessingTree(b *testing.B) {
- idBytes, err := encode(bless(newTree("alice").PublicID(), veyronTree, "alice", nil))
- if err != nil {
- b.Fatal(err)
- }
- benchmarkDecode(b, idBytes)
-}
-
-func TestTreeWireSize(t *testing.T) {
- const N = 3
- id := newTree("X").PublicID()
- for i := 0; i < N; i++ {
- buf, err := encode(id)
- if err != nil {
- t.Fatalf("Failed to encode %q: %v", id, err)
- }
- t.Logf("Wire size of %T with %d blessings: %d bytes (%q)", id, i, len(buf), id)
- id = bless(id, newTree(fmt.Sprintf("%d", i+1)), "X", nil)
- }
-}
diff --git a/runtimes/google/security/util.go b/runtimes/google/security/util.go
index aab52f4..dcb6f1a 100644
--- a/runtimes/google/security/util.go
+++ b/runtimes/google/security/util.go
@@ -19,9 +19,9 @@
switch t := id.(type) {
case *chainPrivateID:
keys.Trust(t.publicID.rootKey, t.publicID.certificates[0].Name)
- case *treePrivateID:
- for _, p := range t.publicID.paths {
- keys.Trust(p.providerKey, p.providerName)
+ case setPrivateID:
+ for _, priv := range t {
+ TrustIdentityProviders(priv)
}
default:
// Silently ignore
diff --git a/runtimes/google/security/util_test.go b/runtimes/google/security/util_test.go
index f3c4e35..f1f76fc 100644
--- a/runtimes/google/security/util_test.go
+++ b/runtimes/google/security/util_test.go
@@ -19,9 +19,7 @@
// Trusted identity providers
// (type assertion just to ensure test sanity)
veyronChain = newChain("veyron").(*chainPrivateID)
- veyronTree = newTree("veyron").(*treePrivateID)
googleChain = newChain("google").(*chainPrivateID)
- googleTree = newTree("google").(*treePrivateID)
)
func matchesErrorPattern(err error, pattern string) bool {
@@ -59,8 +57,16 @@
return id
}
-func newTree(name string) security.PrivateID {
- id, err := newTreePrivateID(name)
+func newSetPublicID(ids ...security.PublicID) security.PublicID {
+ id, err := NewSetPublicID(ids...)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+func newSetPrivateID(ids ...security.PrivateID) security.PrivateID {
+ id, err := NewSetPrivateID(ids...)
if err != nil {
panic(err)
}
@@ -116,55 +122,44 @@
func init() {
// Mark "veyron" and "google" as trusted identity providers.
keys.Trust(veyronChain.PublicID().PublicKey(), "veyron")
- keys.Trust(veyronTree.PublicID().PublicKey(), "veyron")
keys.Trust(googleChain.PublicID().PublicKey(), "google")
- keys.Trust(googleTree.PublicID().PublicKey(), "google")
}
func TestTrustIdentityProviders(t *testing.T) {
var (
- cSelf = newChain("chainself")
- // cBlessed = "chainprovider/somebody"
- cProvider = newChain("chainprovider")
+ cSelf = newChain("chainself")
+ cProvider = newChain("provider")
cBlessed = derive(bless(cSelf.PublicID(), cProvider, "somebody", nil), cSelf)
- tSelf = newTree("treeself")
- tProvider = newTree("treeprovider")
- tBlessed = derive(bless(tSelf.PublicID(), tProvider, "somebody", nil), tSelf)
+ cProvider1 = newChain("provider")
+ cProvider2 = newChain("provider")
+ cSomebody1 = derive(bless(cSelf.PublicID(), cProvider1, "somebody1", nil), cSelf)
+ cSomebody2 = derive(bless(cSelf.PublicID(), cProvider2, "somebody2", nil), cSelf)
+ setID = newSetPrivateID(cSomebody1, cSomebody2)
fake = security.FakePrivateID("fake")
)
// Initially nobody is trusted
m := map[security.PrivateID]bool{
- cSelf: false,
- cProvider: false,
- tSelf: false,
- tProvider: false,
+ cSelf: false,
+ cProvider: false,
+ cBlessed: false,
+ cProvider1: false,
+ cProvider2: false,
+ cSomebody1: false,
+ cSomebody2: false,
}
test := func() {
for priv, want := range m {
id := priv.PublicID()
- key := id.PublicKey()
- var tl keys.TrustLevel
- switch impl := id.(type) {
- case *chainPublicID:
- tl = keys.LevelOfTrust(key, impl.certificates[0].Name)
- case *treePublicID:
- tl = keys.LevelOfTrust(impl.paths[0].providerKey, impl.paths[0].providerName)
- default:
- t.Fatal("Unexpected security.PublicID implementation, identity provider TrustLevel checks only apply to the chain and tree implementations of security.PublicID")
- }
-
- switch tl {
+ switch tl := keys.LevelOfTrust(id.PublicKey(), "provider"); tl {
case keys.Trusted:
if !want {
t.Errorf("%q is trusted, should not be", id)
}
- case keys.Mistrusted:
- t.Errorf("%q is mistrusted. This test should not allow anyone to be mistrusted", id)
- case keys.Unknown:
+ case keys.Unknown, keys.Mistrusted:
if want {
- t.Errorf("%q is not trusted, it should be", id)
+ t.Errorf("%q is %v, it should be trusted", id, tl)
}
default:
t.Errorf("%q has an invalid trust level: %v", tl)
@@ -172,19 +167,15 @@
}
}
test()
- // Trusting cSelf
- TrustIdentityProviders(cSelf)
- m[cSelf] = true
- test()
// Trusting cBlessed should cause cProvider to be trusted
TrustIdentityProviders(cBlessed)
m[cProvider] = true
test()
- // Trusting tBlessed should cause both tSelf and tProvider to be
- // trusted (since tBlessed has both as identity providers)
- TrustIdentityProviders(tBlessed)
- m[tSelf] = true
- m[tProvider] = true
+ // Trusting setID should cause both cProvider1 and cProvider2
+ // to be trusted.
+ TrustIdentityProviders(setID)
+ m[cProvider1] = true
+ m[cProvider2] = true
// Trusting a fake identity should be a no-op
TrustIdentityProviders(fake)
test()
diff --git a/runtimes/google/security/wire/methods.go b/runtimes/google/security/wire/methods.go
index a84ac24..0e7b9de 100644
--- a/runtimes/google/security/wire/methods.go
+++ b/runtimes/google/security/wire/methods.go
@@ -13,7 +13,6 @@
"math/big"
"strings"
- "veyron/runtimes/google/security/keys"
"veyron2/security"
"veyron2/vom"
)
@@ -125,7 +124,7 @@
func (c *Caveat) Validate(ctx security.Context) error {
// TODO(ataly): Is checking that the localID matches the caveat's Service pattern
// the right choice here?
- if c.Service != security.AllPrincipals && (ctx.LocalID() == nil || !ctx.LocalID().Match(security.PrincipalPattern(c.Service))) {
+ if c.Service != security.AllPrincipals && (ctx.LocalID() == nil || !ctx.LocalID().Match(c.Service)) {
return nil
}
cav, err := c.Decode()
@@ -176,7 +175,7 @@
return nil
}
-func (c *Certificate) verifyIntegrity(issuerSignature Signature, key *ecdsa.PublicKey) bool {
+func (c *Certificate) verify(issuerSignature Signature, key *ecdsa.PublicKey) bool {
var r, s big.Int
return ecdsa.Verify(key, c.contentHash(issuerSignature), r.SetBytes(c.Signature.R), s.SetBytes(c.Signature.S))
}
@@ -226,7 +225,7 @@
}
// TODO(ashankar, ataly): Do we worry about timing attacks by
// early exiting on invalid certificate?
- if !c.verifyIntegrity(issuerSignature, verificationKey) {
+ if !c.verify(issuerSignature, verificationKey) {
return ErrNoIntegrity
}
if verificationKey, err = c.PublicKey.Decode(); err != nil {
@@ -237,167 +236,6 @@
return nil
}
-// -- Helper methods on the wire format for the chain implementation of Identity --
-
-// contentHash returns a SHA256 hash of the contents of the blessing and the provided
-// public key.
-func (b *Blessing) contentHash(pubKey PublicKey) []byte {
- h := sha256.New()
- tmp := make([]byte, binary.MaxVarintLen64)
- h.Write([]byte{byte(pubKey.Curve)})
- WriteBytes(h, tmp, pubKey.XY)
- WriteString(h, tmp, b.Name)
- for _, cav := range b.Caveats {
- WriteString(h, tmp, string(cav.Service))
- WriteBytes(h, tmp, cav.Bytes)
- }
- return h.Sum(nil)
-}
-
-// Sign uses the provided private key to sign the contents of the Blessing along with
-// the provided public key. The private key typically belongs to the principal providing
-// the blessing and the public key to the principal being blessed.
-func (b *Blessing) Sign(pubKey PublicKey, key *ecdsa.PrivateKey) error {
- r, s, err := ecdsa.Sign(rand.Reader, key, b.contentHash(pubKey))
- if err != nil {
- return err
- }
- b.Signature.R = r.Bytes()
- b.Signature.S = s.Bytes()
- return nil
-}
-
-func (b *Blessing) verifyIntegrity(pubKey PublicKey) bool {
- var key *ecdsa.PublicKey
- var err error
- if b.Blessor == nil {
- // This is a leaf blessing.
- key, err = pubKey.Decode()
- } else {
- key, err = b.Blessor.PublicKey.Decode()
- }
- if err != nil {
- return false
- }
- var r, s big.Int
- return ecdsa.Verify(key, b.contentHash(pubKey), r.SetBytes(b.Signature.R), s.SetBytes(b.Signature.S))
-}
-
-// validateCaveats verifies if all caveats present on the blessing validate with
-// respect to the provided context.
-func (b *Blessing) validateCaveats(ctx security.Context) error {
- for _, cav := range b.Caveats {
- if err := cav.Validate(ctx); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Names returns an array of blessing-chains and associated identity provider's
-// public key (i.e., the public key of the leaf blessing). More formally,
-//
-// names[i] is the list of names of all blessings on a path from a leaf
-// blessing (signed by providers[i]) to the public key of id.
-func (id *TreePublicID) Names() (names [][]string, providers []*ecdsa.PublicKey) {
- for _, b := range id.Blessings {
- if b.Blessor == nil {
- // leaf blessing - id is an identity provider for this blessing.
- names = append(names, []string{b.Name})
- key, _ := id.PublicKey.Decode()
- providers = append(providers, key)
- continue
- }
- n2, p2 := b.Blessor.Names()
- providers = append(providers, p2...)
- for _, n := range n2 {
- names = append(names, append(n, b.Name))
- }
- }
- return
-}
-
-// ThirdPartyCaveats returns the set of third-party restrictions embedded a TreePublicID.
-// The resulting objects are wrapped in security.ServiceCaveat objects according to the
-// services they are bound to.
-func (id *TreePublicID) ThirdPartyCaveats() (thirdPartyCaveats []security.ServiceCaveat) {
- for _, b := range id.Blessings {
- thirdPartyCaveats = append(thirdPartyCaveats, DecodeThirdPartyCaveats(b.Caveats)...)
- if b.Blessor != nil {
- thirdPartyCaveats = append(thirdPartyCaveats, b.Blessor.ThirdPartyCaveats()...)
- }
- }
- return
-}
-
-// VerifyIntegrity verifies that the TreePublicID has a valid blessing tree, i.e,
-// (1) each blessing on the tree has a signature that can be verified using the
-// public key specified in its child node, (2) the leaf blessing's signature can
-// be verified using the public key that is being blessed by it, and (3) all
-// blessing names are valid.
-func (id *TreePublicID) VerifyIntegrity() error {
- if len(id.Blessings) == 0 {
- return ErrNoIntegrity
- }
- for _, b := range id.Blessings {
- if err := ValidateBlessingName(b.Name); err != nil {
- return err
- }
- if !b.verifyIntegrity(id.PublicKey) {
- return ErrNoIntegrity
- }
- // TODO(ashankar, ataly): Do we worry about timing attacks by
- // early exiting on invalid blessing?
- if b.Blessor == nil {
- continue
- }
- if err := b.Blessor.VerifyIntegrity(); err != nil {
- return ErrNoIntegrity
- }
- }
- return nil
-}
-
-// Authorize returns a new TreePublicID with just those blessing chains from the existing
-// PublicID whose caveats validate with respect to the provided context and whose leaf
-// public key is trusted. This method assumes that the existing TreePublicID has
-// integrity (see VerifyIntegrity).
-func (id *TreePublicID) Authorize(ctx security.Context) *TreePublicID {
- authorizedID := &TreePublicID{PublicKey: id.PublicKey}
- for _, b := range id.Blessings {
- if b.validateCaveats(ctx) != nil {
- continue
- }
- bID := b.Blessor
- if bID == nil {
- // This is a leaf blessing.
- key, err := id.PublicKey.Decode()
- if err != nil {
- continue
- }
- switch keys.LevelOfTrust(key, b.Name) {
- case keys.Unknown, keys.Trusted:
- // No-op
- default:
- // Mistrusted blessings are silently dropped.
- continue
- }
- } else if bID = b.Blessor.Authorize(ctx); bID == nil {
- continue
- }
- authorizedID.Blessings = append(authorizedID.Blessings, Blessing{
- Blessor: bID,
- Name: b.Name,
- Caveats: b.Caveats,
- Signature: b.Signature,
- })
- }
- if authorizedID.Blessings == nil {
- return nil
- }
- return authorizedID
-}
-
// ValidateBlessingName verifies if the provided name is fit to be the name of a blessing.
func ValidateBlessingName(name string) error {
// TODO(ataly, ashankar): Define the list of reserved characters (such as "*", "#",
diff --git a/runtimes/google/security/wire/wire.go b/runtimes/google/security/wire/wire.go
index d09cb8c..b672efe 100644
--- a/runtimes/google/security/wire/wire.go
+++ b/runtimes/google/security/wire/wire.go
@@ -10,9 +10,7 @@
// that make up the public key.
package wire
-import (
- "veyron2/security"
-)
+import "veyron2/security"
const (
keyCurveP256 keyCurve = 0
@@ -81,49 +79,7 @@
// ChainPrivateID represents the chain implementation of PrivateIDs from veyron/runtimes/google/security.
type ChainPrivateID struct {
// PublicID associated with the PrivateID.
- PublicID *ChainPublicID
- // Secret represents the secret integer that together with an ECDSA public key makes up the
- // corresponding private key.
- Secret []byte
-}
-
-// Blessing is a signed assertion binding a name to a public key under a certain set
-// of caveats. The aforesaid public key is also called the "public key being blessed".
-// The issuer of a blessing is the principal that possesses the private key
-// under which the Blessing was signed. The PublicID of the issuer is also linked to
-// from the blessing.
-type Blessing struct {
- // Blessor is the PublicID of the issuer of the blessing. It is nil if the blessing
- // is self-signed, i.e, the public key being blessed and the private key signing
- // the blessing correspond.
- Blessor *TreePublicID
- // Name specified in the blessing, e.g., Alice, Bob. Name must not have the
- // characters "/" or "#".
- Name string
- // Caveats under which the blessing is valid.
- Caveats []Caveat
- // Signature of the contents of the blessing along with the public key being
- // blessed.
- Signature Signature
-}
-
-// TreePublicID represents the tree implementation of PublicIDs from veyron/runtimes/google/security.
-// It consists of a public key and a list of blessings binding different names to the public key.
-// For each blessing, the blesser's PublicID (which is linked to from the blessing) may in turn
-// have blessings of its own thus resulting in a tree of blessings. The blessings at the leaves
-// of the tree are "self signed". This blessing tree effectively binds a tree of names to the
-// PublicID depending on which blessings are valid.
-type TreePublicID struct {
- // PublicKey is the ECDSA public key associated with the PublicID.
- PublicKey PublicKey
- // Blessings is the list of blessings for the aforesaid public key.
- Blessings []Blessing
-}
-
-// TreePrivateID represents the tree implementation of PrivateIDs from veyron/runtimes/google/security.
-type TreePrivateID struct {
- // PublicID associated with the PrivateID.
- PublicID *TreePublicID
+ PublicID ChainPublicID
// Secret represents the secret integer that together with an ECDSA public key makes up the
// corresponding private key.
Secret []byte