blob: 2011a93bbd15cc07c913a1dff9c7cddeb0bc628b [file] [log] [blame]
package security
import (
"fmt"
"reflect"
"testing"
"time"
"veyron/runtimes/google/security/caveat"
"veyron/runtimes/google/security/wire"
"veyron2/security"
)
func TestNameAndAuth(t *testing.T) {
var (
cUnknownAlice = newChain("alice").PublicID()
cTrustedAlice = bless(cUnknownAlice, veyronChain, "alice", nil)
cMistrustedAlice = bless(cUnknownAlice, newChain("veyron"), "alice", nil)
tUntrustedAlice = newTree("alice").PublicID()
tTrustedAlice = bless(tUntrustedAlice, veyronTree, "alice", nil)
tMistrustedAlice = bless(tUntrustedAlice, newTree("veyron"), "alice", nil)
)
testdata := []struct {
id security.PublicID
name, authName string
}{
{id: cUnknownAlice, name: "untrusted/alice", authName: "untrusted/alice"},
{id: cTrustedAlice, name: "veyron/alice", authName: "veyron/alice"},
{id: cMistrustedAlice, name: "untrusted/veyron/alice", authName: ""},
{id: tUntrustedAlice, name: "untrusted/alice", authName: "untrusted/alice"},
{id: tTrustedAlice, name: "untrusted/alice#veyron/alice", authName: "untrusted/alice#veyron/alice"},
{id: tMistrustedAlice, name: "untrusted/alice#untrusted/veyron/alice", authName: "untrusted/alice"},
}
for _, d := range testdata {
if got, want := fmt.Sprintf("%s", d.id), d.name; got != want {
t.Errorf("Got %q(%T) want %q", d.id, d.id, d.name)
}
authID, err := d.id.Authorize(NewContext(ContextArgs{}))
if (authID != nil) == (err != nil) {
t.Errorf("%q.Authorize returned (%v, %v), exactly one return value must be nil", d.id, authID, err)
continue
}
if err := verifyAuthorizedID(d.id, authID, d.authName); err != nil {
t.Error(err)
}
}
}
func TestMatch(t *testing.T) {
type matchInstance struct {
pattern security.PrincipalPattern
want bool
}
testdata := []struct {
id security.PublicID
matchData []matchInstance
}{
{
// 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},
},
},
{
// self-blessed alice tree, not a trusted identity provider so should only match "*"
id: newTree("alice").PublicID(),
matchData: []matchInstance{
{pattern: "*", want: true},
{pattern: "alice", want: false},
{pattern: "alice/*", want: false},
},
},
{
// veyron/alice: rooted in the trusted "veyron" identity provider
id: bless(newChain("immaterial").PublicID(), veyronChain, "alice", nil),
matchData: []matchInstance{
{pattern: "*", want: true},
{pattern: "veyron/*", want: true},
{pattern: "veyron/alice", want: true},
{pattern: "veyron/alice/*", want: true},
{pattern: "veyron/alice/TV", want: true},
{pattern: "veyron", want: false},
{pattern: "veyron/ali", want: false},
{pattern: "veyron/aliced", want: false},
{pattern: "veyron/bob", want: false},
{pattern: "google/alice", want: false},
},
},
{
// alice#veyron/alice#google/alice: two trusted identity providers
id: bless(bless(newTree("alice").PublicID(), veyronTree, "alice", nil), googleTree, "alice", nil),
matchData: []matchInstance{
{pattern: "*", want: true},
// Since alice is not a trusted identity
// provider, the tree's self-blessed identity
// should not match "alice/*"
{pattern: "alice", want: false},
{pattern: "alice/*", want: false},
{pattern: "veyron/*", want: true},
{pattern: "veyron/alice", want: true},
{pattern: "veyron/alice/TV", want: true},
{pattern: "veyron/alice/*", want: true},
{pattern: "ali", want: false},
{pattern: "aliced", want: false},
{pattern: "veyron", want: false},
{pattern: "veyron/ali", want: false},
{pattern: "veyron/aliced", want: false},
{pattern: "veyron/bob", want: false},
{pattern: "google/alice", want: true},
{pattern: "google/alice/TV", want: true},
{pattern: "google/alice/*", want: true},
},
},
}
for _, d := range testdata {
for _, m := range d.matchData {
if got := d.id.Match(m.pattern); got != m.want {
t.Errorf("%q.Match(%s), Got %t, want %t", d.id, m.pattern, got, m.want)
}
}
}
}
func TestExpiredIdentity(t *testing.T) {
testdata := []struct {
blessor security.PrivateID
blessee security.PublicID
authorized string
}{
{veyronChain, newChain("alice").PublicID(), ""},
{veyronTree, newTree("alice").PublicID(), "untrusted/alice"},
}
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)
authorizedID, _ := id.Authorize(NewContext(ContextArgs{}))
if err := verifyAuthorizedID(id, authorizedID, d.authorized); err != nil {
t.Error(err)
}
}
}
func TestTamperedIdentityChain(t *testing.T) {
alice := newChain("alice").PublicID().(*chainPublicID)
// Tamper with the alice's public key
nCerts := len(alice.certificates)
pKey, _ := alice.certificates[nCerts-1].PublicKey.Decode()
pKey.Y.SetInt64(1)
if err := alice.certificates[nCerts-1].PublicKey.Encode(pKey); err != nil {
t.Fatalf("Failed publicKey.Encode:%v", err)
}
if _, err := roundTrip(alice); err != wire.ErrNoIntegrity {
t.Errorf("Got %v want %v from roundTrip(%v)", err, wire.ErrNoIntegrity, alice)
}
}
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")
cVeyronAlice = derive(bless(cAlice.PublicID(), veyronChain, "alice", nil), cAlice)
tAlice = newTree("alice")
tBob = newTree("bob")
tVeyronAlice = derive(bless(tAlice.PublicID(), veyronTree, "alice", nil), tAlice)
)
testdata := []struct {
blessor security.PrivateID
blessee security.PublicID
blessing string // name provided to security.PublicID.Bless
blessed string // name of the blessed identity. Empty if the Bless operation should have failed
err string
}{
{
blessor: veyronChain,
blessee: cAlice.PublicID(),
err: `invalid blessing name:""`,
},
{
blessor: veyronChain,
blessee: cAlice.PublicID(),
blessing: "alice/bob",
err: `invalid blessing name:"alice/bob"`,
},
{
blessor: veyronChain,
blessee: cAlice.PublicID(),
blessing: "alice",
blessed: "veyron/alice",
},
{
blessor: cVeyronAlice,
blessee: cBob.PublicID(),
blessing: "friend_bob",
blessed: "veyron/alice/friend_bob",
},
{
blessor: cAlice,
blessee: cBob.PublicID(),
blessing: "friend_bob",
blessed: "untrusted/alice/friend_bob",
},
{
blessor: veyronTree,
blessee: tAlice.PublicID(),
err: `invalid blessing name:""`,
},
{
blessor: veyronTree,
blessee: tAlice.PublicID(),
blessing: "alice/bob",
err: `invalid blessing name:"alice/bob"`,
},
{
blessor: veyronTree,
blessee: tAlice.PublicID(),
blessing: "alice",
blessed: "untrusted/alice#veyron/alice",
},
{
blessor: tVeyronAlice,
blessee: tBob.PublicID(),
blessing: "friend_bob",
blessed: "untrusted/bob#untrusted/alice/friend_bob#veyron/alice/friend_bob",
},
{
blessor: googleTree,
blessee: tVeyronAlice.PublicID(),
blessing: "googler",
blessed: "untrusted/alice#veyron/alice#google/googler",
},
}
for _, d := range testdata {
blessed, err := d.blessor.Bless(d.blessee, d.blessing, 1*time.Minute, nil)
// Exaclty one of (blessed, err) should be nil
if (blessed != nil) == (err != nil) {
t.Errorf("%q.Bless(%q, %q, ...) returned (%v, %v): exactly one return value should be nil", d.blessor, d.blessee, d.blessing, blessed, err)
continue
}
// If err != nil, should match d.err
if err != nil {
if err.Error() != d.err {
t.Errorf("Got error [%s], want [%s] from %q.Bless(%q, %q, ...)", err, d.err, d.blessor, d.blessee, d.blessing)
}
continue
}
// If d.err is specified, then err should not have been nil
if len(d.err) != 0 {
t.Errorf("Got %q want error=%v from %q.Bless(%q, %q, ...)", blessed, d.err, d.blessor, d.blessee, d.blessing)
continue
}
// Compare names
if got, want := fmt.Sprintf("%s", blessed), d.blessed; got != want {
t.Errorf("Got %q want %q from %q.Bless(%q, %q, ...)", got, want, d.blessor, d.blessee, d.blessing)
}
// Public keys should match for blessed and blessee
if !reflect.DeepEqual(blessed.PublicKey(), d.blessee.PublicKey()) {
t.Errorf("PublicKey mismatch in %q.Bless(%q, %q, ...)", d.blessor, d.blessee, d.blessing)
}
// Verify wire encoding of the blessed
if _, err := roundTrip(blessed); err != nil {
t.Errorf("roundTrip(%q) failed: %v (from %q.Bless(%q, %q, ...))", blessed, err, d.blessor, d.blessee, d.blessing)
}
}
}
func TestAuthorizeWithCaveats(t *testing.T) {
var (
// Alice's chain and tree identities
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()
// Caveats
// Can only call "Play" at the Google service
cavOnlyPlayAtGoogle = methodRestrictionCaveat("google", []string{"Play"})
// Can only talk to the "Google" service
cavOnlyGoogle = peerIdentityCaveat("google")
// Can only call the PublicProfile method on veyron/alice/*
cavOnlyPublicProfile = methodRestrictionCaveat("veyron/alice/*", []string{"PublicProfile"})
)
type rpc struct {
server security.PublicID
method string
// Expected output: exactly one should be non-empty
authName, authErr string
}
testdata := []struct {
client security.PublicID
tests []rpc
}{
// client has a chain identity
{
client: bless(cAlice, veyronChain, "alice", cavOnlyPlayAtGoogle),
tests: []rpc{
{server: bob, method: "Hello", authName: "veyron/alice"},
{server: bob, authName: "veyron/alice"},
{server: googleTree.PublicID(), method: "Hello", authErr: "caveat.MethodRestriction{Methods:[Play]} forbids invocation of method Hello"},
{server: googleTree.PublicID(), method: "Play", authName: "veyron/alice"},
{server: googleTree.PublicID(), authName: "veyron/alice"},
},
},
{
client: bless(cAlice, veyronChain, "alice", cavOnlyGoogle),
tests: []rpc{
{server: bob, method: "Hello", authErr: "caveat.PeerIdentity{Peers:[google]} forbids RPCing with peer untrusted/bob"},
{server: googleTree.PublicID(), method: "Hello", authName: "veyron/alice"},
{server: googleTree.PublicID(), method: "Play", authName: "veyron/alice"},
},
},
{
client: bless(cAlice, veyronChain, "alice", append(cavOnlyGoogle, cavOnlyPlayAtGoogle...)),
tests: []rpc{
{server: bob, method: "Hello", authErr: "caveat.PeerIdentity{Peers:[google]} forbids RPCing with peer untrusted/bob"},
{server: googleTree.PublicID(), method: "Hello", authErr: "caveat.MethodRestriction{Methods:[Play]} forbids invocation of method Hello"},
{server: googleTree.PublicID(), method: "Play", authName: "veyron/alice"},
},
},
{
client: bless(cAlice, veyronChain, "alice", cavOnlyPublicProfile),
tests: []rpc{
{server: cVeyronAliceTV, method: "PrivateProfile", authErr: "caveat.MethodRestriction{Methods:[PublicProfile]} forbids invocation of method PrivateProfile"},
{server: cVeyronAliceTV, method: "PublicProfile", authName: "veyron/alice"},
},
},
// client has a tree identity
{
client: bless(tAlice, veyronTree, "alice", cavOnlyPlayAtGoogle),
tests: []rpc{
{server: bob, method: "Hello", authName: "untrusted/alice#veyron/alice"},
{server: bob, authName: "untrusted/alice#veyron/alice"},
{server: googleTree.PublicID(), method: "Hello", authName: "untrusted/alice"},
{server: googleTree.PublicID(), method: "Play", authName: "untrusted/alice#veyron/alice"},
{server: googleTree.PublicID(), authName: "untrusted/alice#veyron/alice"},
},
},
{
client: bless(tAlice, veyronTree, "alice", cavOnlyGoogle),
tests: []rpc{
{server: bob, method: "Hello", authName: "untrusted/alice"},
{server: googleTree.PublicID(), method: "Hello", authName: "untrusted/alice#veyron/alice"},
{server: googleTree.PublicID(), method: "Play", authName: "untrusted/alice#veyron/alice"},
},
},
{
client: bless(tAlice, veyronTree, "alice", append(cavOnlyGoogle, cavOnlyPlayAtGoogle...)),
tests: []rpc{
{server: bob, method: "Hello", authName: "untrusted/alice"},
{server: googleTree.PublicID(), method: "Hello", authName: "untrusted/alice"},
{server: googleTree.PublicID(), method: "Play", authName: "untrusted/alice#veyron/alice"},
},
},
{
client: bless(tAlice, veyronTree, "alice", cavOnlyPublicProfile),
tests: []rpc{
{server: tVeyronAliceTV, method: "PrivateProfile", authName: "untrusted/alice"},
{server: tVeyronAliceTV, method: "PublicProfile", authName: "untrusted/alice#veyron/alice"},
},
},
}
for _, d := range testdata {
// Validate that the client identity (with all its blessings) is valid for wire transmission.
if _, err := roundTrip(d.client); err != nil {
t.Errorf("roundTrip(%q): %v", d.client, err)
continue
}
for _, test := range d.tests {
if (len(test.authName) == 0) == (len(test.authErr) == 0) {
t.Fatalf("Bad testdata. One of authName 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)
if !matchesErrorPattern(err, test.authErr) {
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.authName); err != nil {
t.Errorf("%q.Authorize(%v) returned identity %v want %q", d.client, ctx, authID, test.authName)
}
}
}
}
func TestAuthorizeWithThirdPartyCaveats(t *testing.T) {
mkveyronchain := func(name string) security.PrivateID {
base := newChain(name)
return derive(bless(base.PublicID(), veyronChain, name, nil), base)
}
mkveyrontree := func(name string) security.PrivateID {
base := newTree(name)
return derive(bless(base.PublicID(), veyronTree, name, nil), base)
}
// Principals (type conversions just to protect against accidentally
// calling the wrong factory function)
var (
alice = mkveyronchain("alice")
cBob = mkveyronchain("bob").(*chainPrivateID)
tBob = mkveyrontree("bob").(*treePrivateID)
cCarol = mkveyronchain("carol").(*chainPrivateID).PublicID()
tCarol = mkveyrontree("carol").(*treePrivateID).PublicID()
)
// 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)
if err != nil {
t.Fatalf("%q.MintDischarge failed: %v", id, err)
}
return d
}
// Discharges
var (
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 (
ctxEmpty = NewContext(ContextArgs{Debug: "ctxEmpty"})
ctxAlice = NewContext(ContextArgs{
Discharges: security.CaveatDischargeMap{dAlice.CaveatID(): dAlice},
Debug: "ctxAlice",
})
// Context containing the discharge dGoogle but the server is not a Google server, so
// the service caveat is not satisfied
ctxGoogleAtOther = NewContext(ContextArgs{
Discharges: security.CaveatDischargeMap{dGoogle.CaveatID(): dGoogle},
Debug: "ctxGoogleAtOther",
})
// Context containing the discharge dGoogle at a google server.
ctxGoogleAtGoogle = NewContext(ContextArgs{
Discharges: security.CaveatDischargeMap{dGoogle.CaveatID(): dGoogle},
LocalID: googleChain.PublicID(),
Debug: "ctxGoogleAtGoogle",
})
ctxExpired = NewContext(ContextArgs{
Discharges: security.CaveatDischargeMap{dExpired.CaveatID(): dExpired},
Debug: "ctxExpired",
})
ctxInvalid = NewContext(ContextArgs{
Discharges: security.CaveatDischargeMap{dInvalid.CaveatID(): dInvalid},
Debug: "ctxInvalid",
})
)
type want struct {
// Exactly one of these should be non-empty
name, err string
}
chaintests := map[security.Context]want{
ctxEmpty: want{err: "missing discharge"},
ctxAlice: want{name: "veyron/bob/friend"},
ctxGoogleAtOther: want{err: "forbids RPCing with peer"},
ctxGoogleAtGoogle: want{name: "veyron/bob/friend"},
ctxExpired: want{err: "at this time"},
ctxInvalid: want{err: "invalid signature"},
}
treetests := map[security.Context]want{
ctxEmpty: want{name: "untrusted/carol#veyron/carol"},
ctxAlice: want{name: "untrusted/carol#veyron/carol#untrusted/bob/friend#veyron/bob/friend"},
ctxGoogleAtOther: want{name: "untrusted/carol#veyron/carol"},
ctxGoogleAtGoogle: want{name: "untrusted/carol#veyron/carol#untrusted/bob/friend#veyron/bob/friend"},
ctxExpired: want{name: "untrusted/carol#veyron/carol"},
ctxInvalid: want{name: "untrusted/carol#veyron/carol"},
}
caveats := []security.ServiceCaveat{security.UniversalCaveat(aliceProximityCaveat)}
testdata := []struct {
id security.PublicID
tests map[security.Context]want
}{
{bless(cCarol, cBob, "friend", caveats), chaintests},
{bless(tCarol, tBob, "friend", caveats), treetests},
}
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 ctx, want := range d.tests {
if (len(want.name) == 0) == (len(want.err) == 0) {
t.Fatalf("Bad testdata. One of (name, err) must be non-empty: %q, %v", d.id, ctx)
}
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)
}
if err := verifyAuthorizedID(d.id, authID, want.name); err != nil {
t.Errorf("%q.Authorize(%v) returned identity %v want %q", d.id, ctx, authID, want.name)
}
}
}
}
func TestThirdPartyCaveatAccessors(t *testing.T) {
mkTPCaveat := func(restriction string, id security.PublicID) security.ThirdPartyCaveat {
tpCav, err := caveat.NewPublicKeyCaveat(restriction, id, "someLocation")
if err != nil {
t.Fatalf("NewPublicKeyCaveat(%q, %q, ...) failed: %v", restriction, id, err)
}
return tpCav
}
mintDischarge := func(caveat security.ThirdPartyCaveat, id security.PrivateID, caveats []security.ServiceCaveat) security.ThirdPartyDischarge {
d, err := id.MintDischarge(caveat, time.Minute, caveats)
if err != nil {
t.Fatalf("%q.MintDischarge failed: %v", id, err)
}
return d
}
// Principals (type conversions just to protect against accidentally
// calling the wrong factory function)
var (
alice = newChain("alice").(*chainPrivateID)
cBob = newChain("bob").(*chainPrivateID)
tBob = newTree("bob").(*treePrivateID)
)
// Caveats
var (
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: 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}},
}
testdata := []struct {
privID security.PrivateID
pubID security.PublicID
}{
{privID: veyronChain, pubID: cBob.PublicID()},
{privID: veyronTree, pubID: tBob.PublicID()},
}
for _, d := range testdata {
for _, c := range caveatsData {
// 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)
}
// 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)
}
}
}
}
func TestBlessingChainAmplification(t *testing.T) {
var (
// alice has blessings from trusted identity providers google and veyron
alice = newChain("alice")
googleAlice = derive(bless(alice.PublicID(), googleChain, "alice", nil), alice)
veyronAlice = derive(bless(alice.PublicID(), veyronChain, "alice", nil), alice)
bob = newChain("bob").PublicID()
)
// veyron/alice blesses bob for 5 minutes
veyronAliceBob, err := veyronAlice.Bless(bob, "bob@veyron@alice", 5*time.Minute, nil)
if err != nil {
t.Fatal(err)
}
authID, _ := veyronAliceBob.Authorize(NewContext(ContextArgs{}))
if err := verifyAuthorizedID(veyronAliceBob, authID, "veyron/alice/bob@veyron@alice"); err != nil {
t.Fatal(err)
}
// google/alice blesses bob for 1 millisecond
googleAliceBob, err := googleAlice.Bless(bob, "bob@google@alice", 1*time.Millisecond, nil)
if err != nil {
t.Fatal(err)
}
// Wait for 1ms so that the blessing expires
time.Sleep(time.Millisecond)
authID, _ = googleAliceBob.Authorize(NewContext(ContextArgs{}))
if err := verifyAuthorizedID(googleAliceBob, authID, ""); err != nil {
t.Fatal(err)
}
// At this point, Bob has a valid blessing from veyron/alice and an
// expired blessing from google/alice. Bob should not be able to
// construct a valid blessing from google/alice by combining certificates.
veyronBob := veyronAliceBob.(*chainPublicID)
googleBob := googleAliceBob.(*chainPublicID)
// googleBob should be a valid identity before any modifications
if _, err := roundTrip(googleBob); err != nil {
t.Fatal(err)
}
// Keep the "google/alice" certificate and replace "alice/bob" from
// "google/alice/bob" with the one from "veyron/alice/bob"
cert := googleBob.certificates[2]
googleBob.certificates[2] = veyronBob.certificates[2]
// This hacked up identity should fail integrity tests
if _, err := roundTrip(googleBob); err != wire.ErrNoIntegrity {
t.Fatalf("roundTrip(%q) returned %v want %v", googleBob, err, wire.ErrNoIntegrity)
}
// Restoring the certificate should restore validity.
googleBob.certificates[2] = cert
if _, err := roundTrip(googleBob); err != nil {
t.Fatal(err)
}
// Replacing the "google/alice" certificate with the "veyron/alice"
// certificate should also cause the identity to be invalid.
googleBob.certificates[1] = veyronBob.certificates[1]
if _, err := roundTrip(googleBob); err != wire.ErrNoIntegrity {
t.Fatalf("roundTrip(%q) returned %v want %v", googleBob, err, wire.ErrNoIntegrity)
}
}
func TestDerive(t *testing.T) {
var (
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()
)
testdata := []struct {
priv security.PrivateID
pub security.PublicID
err bool
}{
{priv: cAlice, pub: cVeyronAlice},
{priv: cAlice, pub: cBob, err: true},
{priv: tAlice, pub: tVeyronAlice},
{priv: tAlice, pub: tBob, err: true},
}
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)
continue
}
if err != nil {
// If it was not supposed to be, the previous check
// would have registered the error.
continue
}
if !reflect.DeepEqual(derivedID.PublicID(), d.pub) {
t.Errorf("%q.Derive(%q) returned %q. PublicID mismatch", d.priv, d.pub, derivedID)
}
if !reflect.DeepEqual(derivedID.PrivateKey(), d.priv.PrivateKey()) {
t.Errorf("%q.Derive(%q) returned %q. PrivateKey mismatch", d.priv, d.pub, derivedID)
}
if _, err := roundTrip(derivedID.PublicID()); err != nil {
t.Errorf("roundTrip(%q=%q.Derive(%q)) failed: %v", derivedID, d.priv, d.pub, err)
}
}
}