"veyron2/security": Caveat and CaveatValidator
In accordance with the plan in
https://veyron-review.googlesource.com/#/c/4102/ this CL makes the
following changes:
1) Removes the ServiceCaveat type
2) Renames the Caveat type to CaveatValidator
3) Adds new Caveat and ThirdPartyCaveat types
4) Minor method renames in security.Context
Notes:
* CaveatValidator is an interface with a method Validate. All
concrete caveat implemenations must implement this interface.
* A Caveat is a serialized caveat implementation obtained using
the security.NewCaveat(CaveatValidator) factory function.
* A ThirPartyCaveat is an interface that embeds CaveatValidator along
with other methods for obtaining the ID, Location and requirements of
a third-party caveat.
* Bless and MintDischarge methods now directly take Caveat objects and
embed them in the PublicID and Discharge respectively. The caller of
Bless and MintDischarge need not have the CaveatValidator implementation
from which the Caveat objects were obtained.
* While validating a PublicID or a Discharge, any Caveats that fail to
decode to a CaveatValidator are considered invalid, in other words, all
caveats are universally enforced.
Change-Id: I9430bb90af3267d1b6f235be9056175c30f4d2da
diff --git a/security/acl_authorizer_test.go b/security/acl_authorizer_test.go
index 1d04e89..17573c3 100644
--- a/security/acl_authorizer_test.go
+++ b/security/acl_authorizer_test.go
@@ -16,20 +16,20 @@
// context implements Context.
type context struct {
localID, remoteID security.PublicID
- discharges security.CaveatDischargeMap
+ discharges map[string]security.Discharge
method, name, suffix string
label security.Label
}
-func (c *context) Method() string { return c.method }
-func (c *context) Name() string { return c.name }
-func (c *context) Suffix() string { return c.suffix }
-func (c *context) Label() security.Label { return c.label }
-func (c *context) CaveatDischarges() security.CaveatDischargeMap { return c.discharges }
-func (c *context) LocalID() security.PublicID { return c.localID }
-func (c *context) RemoteID() security.PublicID { return c.remoteID }
-func (c *context) LocalEndpoint() naming.Endpoint { return nil }
-func (c *context) RemoteEndpoint() naming.Endpoint { return nil }
+func (c *context) Method() string { return c.method }
+func (c *context) Name() string { return c.name }
+func (c *context) Suffix() string { return c.suffix }
+func (c *context) Label() security.Label { return c.label }
+func (c *context) Discharges() map[string]security.Discharge { return c.discharges }
+func (c *context) LocalID() security.PublicID { return c.localID }
+func (c *context) RemoteID() security.PublicID { return c.remoteID }
+func (c *context) LocalEndpoint() naming.Endpoint { return nil }
+func (c *context) RemoteEndpoint() naming.Endpoint { return nil }
func saveACLToTempFile(acl security.ACL) string {
f, err := ioutil.TempFile("", "saved_acl")
diff --git a/security/audit/id.go b/security/audit/id.go
index 779cc40..a56700a 100644
--- a/security/audit/id.go
+++ b/security/audit/id.go
@@ -37,7 +37,7 @@
return id.id.PublicID()
}
-func (id *auditingID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.ServiceCaveat) (security.PublicID, error) {
+func (id *auditingID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.Caveat) (security.PublicID, error) {
blessed, err := id.id.Bless(blessee, blessingName, duration, caveats)
if err = id.audit(err, "Bless", args{blessee, blessingName, duration, caveats}, blessed); err != nil {
return nil, err
@@ -57,7 +57,7 @@
return NewPrivateID(derived, id.auditor), nil
}
-func (id *auditingID) MintDischarge(caveat security.ThirdPartyCaveat, context security.Context, duration time.Duration, caveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
+func (id *auditingID) MintDischarge(caveat security.ThirdPartyCaveat, context security.Context, duration time.Duration, caveats []security.Caveat) (security.Discharge, error) {
d, err := id.id.MintDischarge(caveat, context, duration, caveats)
if err = id.audit(err, "MintDischarge", args{caveat, context, duration, caveats}, nil); err != nil {
return nil, err
diff --git a/security/audit/id_test.go b/security/audit/id_test.go
index e8f67e9..a0f9304 100644
--- a/security/audit/id_test.go
+++ b/security/audit/id_test.go
@@ -22,7 +22,7 @@
publicID = security.FakePublicID("publicid")
str string
duration time.Duration
- caveats []security.ServiceCaveat
+ caveats []security.Caveat
thirdPartyCaveat thirdPartyCaveat
context context
@@ -190,7 +190,7 @@
}
return id.NextResult.(security.PublicID)
}
-func (id *mockID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.ServiceCaveat) (security.PublicID, error) {
+func (id *mockID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.Caveat) (security.PublicID, error) {
defer id.reset()
result, _ := id.NextResult.(security.PublicID)
return result, id.NextError
@@ -200,9 +200,9 @@
result, _ := id.NextResult.(security.PrivateID)
return result, id.NextError
}
-func (id *mockID) MintDischarge(caveat security.ThirdPartyCaveat, context security.Context, duration time.Duration, caveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
+func (id *mockID) MintDischarge(caveat security.ThirdPartyCaveat, context security.Context, duration time.Duration, caveats []security.Caveat) (security.Discharge, error) {
defer id.reset()
- result, _ := id.NextResult.(security.ThirdPartyDischarge)
+ result, _ := id.NextResult.(security.Discharge)
return result, id.NextError
}
@@ -240,7 +240,7 @@
type thirdPartyCaveat struct{}
func (thirdPartyCaveat) Validate(security.Context) error { return nil }
-func (thirdPartyCaveat) ID() security.ThirdPartyCaveatID { return "thirdPartyCaveatID" }
+func (thirdPartyCaveat) ID() string { return "thirdPartyCaveatID" }
func (thirdPartyCaveat) Location() string { return "thirdPartyCaveatLocation" }
func (thirdPartyCaveat) Requirements() security.ThirdPartyRequirements {
return security.ThirdPartyRequirements{}
@@ -249,21 +249,21 @@
// context implements security.Context
type context struct{}
-func (context) Method() string { return "method" }
-func (context) Name() string { return "name" }
-func (context) Suffix() string { return "suffix" }
-func (context) Label() security.Label { return security.ReadLabel }
-func (context) CaveatDischarges() security.CaveatDischargeMap { return nil }
-func (context) LocalID() security.PublicID { return nil }
-func (context) RemoteID() security.PublicID { return nil }
-func (context) LocalEndpoint() naming.Endpoint { return nil }
-func (context) RemoteEndpoint() naming.Endpoint { return nil }
+func (context) Method() string { return "method" }
+func (context) Name() string { return "name" }
+func (context) Suffix() string { return "suffix" }
+func (context) Label() security.Label { return security.ReadLabel }
+func (context) Discharges() map[string]security.Discharge { return nil }
+func (context) LocalID() security.PublicID { return nil }
+func (context) RemoteID() security.PublicID { return nil }
+func (context) LocalEndpoint() naming.Endpoint { return nil }
+func (context) RemoteEndpoint() naming.Endpoint { return nil }
-// discharge implements the security.ThirdPartyDischarge interface
+// discharge implements the security.Discharge interface
type discharge struct{}
-func (*discharge) CaveatID() security.ThirdPartyCaveatID { return "thirdPartyCaveatID" }
-func (*discharge) ThirdPartyCaveats() []security.ServiceCaveat { return nil }
+func (*discharge) ID() string { return "thirdPartyCaveatID" }
+func (*discharge) ThirdPartyCaveats() []security.ThirdPartyCaveat { return nil }
func call(receiver interface{}, method string, args V) (results []interface{}, err interface{}) {
defer func() {
diff --git a/security/caveat/caveat.go b/security/caveat/caveat.go
index a008381..47b136e 100644
--- a/security/caveat/caveat.go
+++ b/security/caveat/caveat.go
@@ -9,75 +9,72 @@
"veyron2/vom"
)
-// UniversalCaveat takes a Caveat and returns a ServiceCaveat bound to all principals.
-func UniversalCaveat(cav security.Caveat) security.ServiceCaveat {
- return security.ServiceCaveat{Service: security.AllPrincipals, Caveat: cav}
-}
-
-// Expiry is a security.Caveat that restricts the validity period of
-// the credential bearing this caveat.
+// Expiry is a security.CaveatValidator that restricts the validity period of
+// credentials bearing a security.Caveat constructed from this validator.
type Expiry struct {
+ // TODO(ataly,ashankar): Get rid of IssueTime from this caveat.
IssueTime time.Time
ExpiryTime time.Time
}
-func (c *Expiry) Validate(context security.Context) error {
+func (v *Expiry) Validate(context security.Context) error {
now := time.Now()
- if now.Before(c.IssueTime) || now.After(c.ExpiryTime) {
- return fmt.Errorf("%#v forbids credential from being used at this time(%v)", c, now)
+ if now.Before(v.IssueTime) || now.After(v.ExpiryTime) {
+ return fmt.Errorf("%#v forbids credential from being used at this time(%v)", v, now)
}
return nil
}
-// MethodRestriction is a security.Caveat that restricts the set of
-// methods that can be invoked by a credential bearing the caveat.
+// MethodRestriction is a security.CaveatValidator that restricts the set of
+// methods that are authorized via credentials bearing a security.Caveat
+// constructed from this validator.
// An empty set indicates that no methods can be invoked.
type MethodRestriction []string
-func (c MethodRestriction) Validate(ctx security.Context) error {
+func (v MethodRestriction) Validate(ctx security.Context) error {
// If the context has an empty Method then the caveat validates.
if ctx.Method() == "" {
return nil
}
- for _, m := range c {
+ for _, m := range v {
if m == ctx.Method() {
return nil
}
}
- return fmt.Errorf("%#v forbids invocation of method %s", c, ctx.Method())
+ return fmt.Errorf("%#v forbids invocation of method %s", v, ctx.Method())
}
-// PeerIdentity is a security.Caveat that restricts the bearer of a credential
-// with this caveat from making or receiving RPCs to a limited set of peers -
-// those whose identities match one of the provided security.BlessingPatterns.
+// PeerBlessings is a security.CaveatValidator that restricts a credential
+// bearing a security.Caveat constructed from this validator to be used for
+// communicating with a limited set of peers - those who have blessings matching
+// one of the provided security.BlessingPatterns.
// An empty set indicates that no peers can be communicated with.
-type PeerIdentity []security.BlessingPattern
+type PeerBlessings []security.BlessingPattern
-// Validate checks that the identity of the peer is present on the set of services
-// identified by the BlessingPatterns on the caveat.
-func (c PeerIdentity) Validate(ctx security.Context) error {
- for _, p := range c {
+func (v PeerBlessings) Validate(ctx security.Context) error {
+ for _, p := range v {
if ctx.LocalID() != nil && p.MatchedBy(ctx.LocalID().Names()...) {
return nil
}
}
- return fmt.Errorf("%#v forbids RPCing with peer %s", c, ctx.LocalID())
+ return fmt.Errorf("%#v forbids RPCing with peer %s", v, ctx.LocalID())
}
-// NetworkType is a security.Caveat that restricts communication with the
-// remote process to a particular network ("tcp", "udp", "bluetooth" etc.)
+// NetworkType is a security.CaveatValidator that restricts a credential bearing
+// a security.Caveat constructed from this validator to be used only for communicating over
+// a particular network ("tcp", "udp", "bluetooth" etc.)
type NetworkType string
-func (cav NetworkType) Validate(ctx security.Context) error {
- if ctx.RemoteEndpoint().Addr().Network() == string(cav) {
+func (v NetworkType) Validate(ctx security.Context) error {
+ if ctx.RemoteEndpoint().Addr().Network() == string(v) {
return nil
}
- return fmt.Errorf("required network type %q, got %q", cav, ctx.RemoteEndpoint().Addr().Network())
+ return fmt.Errorf("required network type %q, got %q", v, ctx.RemoteEndpoint().Addr().Network())
}
func init() {
vom.Register(Expiry{})
vom.Register(MethodRestriction(nil))
- vom.Register(PeerIdentity(nil))
+ vom.Register(PeerBlessings(nil))
vom.Register(NetworkType(""))
}
diff --git a/security/caveat/caveat_test.go b/security/caveat/caveat_test.go
index b4f44f8..301491a 100644
--- a/security/caveat/caveat_test.go
+++ b/security/caveat/caveat_test.go
@@ -24,15 +24,15 @@
method string
}
-func (c *context) Method() string { return c.method }
-func (c *context) Name() string { return "some_name" }
-func (c *context) Suffix() string { return "some_suffix" }
-func (c *context) Label() security.Label { return security.AdminLabel }
-func (c *context) CaveatDischarges() security.CaveatDischargeMap { return nil }
-func (c *context) LocalID() security.PublicID { return c.local }
-func (c *context) RemoteID() security.PublicID { return c.remote }
-func (c *context) LocalEndpoint() naming.Endpoint { return &c.localEndpoint }
-func (c *context) RemoteEndpoint() naming.Endpoint { return &c.remoteEndpoint }
+func (c *context) Method() string { return c.method }
+func (c *context) Name() string { return "some_name" }
+func (c *context) Suffix() string { return "some_suffix" }
+func (c *context) Label() security.Label { return security.AdminLabel }
+func (c *context) Discharges() map[string]security.Discharge { return nil }
+func (c *context) LocalID() security.PublicID { return c.local }
+func (c *context) RemoteID() security.PublicID { return c.remote }
+func (c *context) LocalEndpoint() naming.Endpoint { return &c.localEndpoint }
+func (c *context) RemoteEndpoint() naming.Endpoint { return &c.remoteEndpoint }
func TestCaveats(t *testing.T) {
var (
@@ -41,7 +41,7 @@
)
now := time.Now()
tests := []struct {
- c security.Caveat
+ c security.CaveatValidator
ok bool
}{
{&caveat.Expiry{IssueTime: now, ExpiryTime: now.Add(time.Hour)}, true},
@@ -51,10 +51,10 @@
{caveat.NetworkType("tcp"), true},
{caveat.MethodRestriction{"Pause", "Play"}, true},
{caveat.MethodRestriction{"List"}, false},
- {caveat.PeerIdentity(nil), false},
- {caveat.PeerIdentity{"fake/alice"}, true},
- {caveat.PeerIdentity{"fake/carol"}, false},
- {caveat.PeerIdentity{"fake/alice", "fake/carol"}, true},
+ {caveat.PeerBlessings(nil), false},
+ {caveat.PeerBlessings{"fake/alice"}, true},
+ {caveat.PeerBlessings{"fake/carol"}, false},
+ {caveat.PeerBlessings{"fake/alice", "fake/carol"}, true},
}
ctx := &context{local: alice, remote: bob, method: "Play", remoteEndpoint: endpoint{addr: &net.TCPAddr{}}}
for _, test := range tests {
diff --git a/security/caveat/public_key_caveat.go b/security/caveat/public_key_caveat.go
index 30cbf74..5b4884b 100644
--- a/security/caveat/public_key_caveat.go
+++ b/security/caveat/public_key_caveat.go
@@ -1,13 +1,15 @@
package caveat
import (
- "bytes"
+ "crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"fmt"
"time"
+ vsecurity "veyron/security"
+
"veyron2/security"
"veyron2/security/wire"
"veyron2/vom"
@@ -22,30 +24,22 @@
// Can we use 4bytes instead?.
const nonceLength = 16
-// errPublicKeyCaveat returns an error indicating that the provided caveat has
-// an invalid or missing discharge.
-func errPublicKeyCaveat(c *publicKeyCaveat, err error) error {
- return fmt.Errorf("%v for publicKeyCaveat{nonce:%v location:%q}", err, c.RandNonce, c.Location())
-}
-
// publicKeyCaveat implements security.ThirdPartyCaveat. It specifies a restriction,
-// a validation key and the location of the third-party responsible for
-// discharging the caveat. A discharge for this caveat is a signed assertion whose
+// a validation key and the location (object name) of the third-party responsible
+// for discharging the caveat. A discharge for this caveat is a signed assertion whose
// signature can be verified using the validation key.
type publicKeyCaveat struct {
// RandNonce specifies a cryptographically random nonce (of fixed length) that
// uniquely identifies the caveat.
RandNonce []uint8
// DischargeMintingCaveat specifies the caveat that has to be validated
- // before minting a discharge for a publicKeyCaveat. A byte slice containing
- // VOM-encoded security.Caveat is used to enable a publicKeyCaveat to be
- // validated by devices that cannot decode the discharge minting caveats.
+ // before minting a discharge for a publicKeyCaveat.
DischargeMintingCaveat []byte
// ValidationKey specifies the public key of the discharging-party.
ValidationKey wire.PublicKey
// ThirdPartyLocation specifies the object name of the discharging-party.
ThirdPartyLocation string
- // Information wanted in order to issue a discharge.
+ // ThirdPartyRequirements specify the information wanted in order to issue a discharge.
ThirdPartyRequirements security.ThirdPartyRequirements
}
@@ -54,11 +48,10 @@
//
// TODO(ataly, ashankar): A 256bit hash is probably much stronger that what we need
// here. Can we truncate the hash to 96bits?
-func (c *publicKeyCaveat) ID() security.ThirdPartyCaveatID {
- return security.ThirdPartyCaveatID(id(c.RandNonce, c.DischargeMintingCaveat))
+func (c *publicKeyCaveat) ID() string {
+ return id(c.RandNonce, c.DischargeMintingCaveat)
}
-// Location returns the third-party location embedded in the caveat.
func (c *publicKeyCaveat) Location() string {
return c.ThirdPartyLocation
}
@@ -71,67 +64,67 @@
return c.ThirdPartyRequirements
}
-// Validate verifies whether the caveat validates for the provided context.
func (c *publicKeyCaveat) Validate(ctx security.Context) error {
// Check whether the context has a dicharge matching the caveat's ID.
- dis, ok := ctx.CaveatDischarges()[c.ID()]
+ dis, ok := ctx.Discharges()[c.ID()]
if !ok {
- return errPublicKeyCaveat(c, fmt.Errorf("missing discharge"))
+ return fmt.Errorf("missing discharge")
}
pkDischarge, ok := dis.(*publicKeyDischarge)
if !ok {
- return errPublicKeyCaveat(c, fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis))
+ return fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis)
}
// Validate all caveats present on the discharge.
- for _, cav := range pkDischarge.Caveats {
- if err := cav.Validate(ctx); err != nil {
- return errPublicKeyCaveat(c, err)
+ validators, err := vsecurity.CaveatValidators(pkDischarge.Caveats...)
+ if err != nil {
+ return err
+ }
+ for _, v := range validators {
+ if err := v.Validate(ctx); err != nil {
+ return err
}
}
// Check the discharge signature with the validation key from the caveat.
key, err := c.ValidationKey.Decode()
if err != nil {
- return errPublicKeyCaveat(c, err)
+ return err
}
if !pkDischarge.Signature.Verify(key, pkDischarge.contentHash()) {
- return errPublicKeyCaveat(c, fmt.Errorf("discharge %v has invalid signature", dis))
+ return fmt.Errorf("discharge %v has invalid signature", dis)
}
return nil
}
-// publicKeyDischarge implements security.ThirdPartyDischarge. It specifies a
-// discharge for a publicKeyCaveat, and includes a signature that can be verified
-// with the validation key of the caveat. Additionally, the discharge may include
-// service caveats which must all be valid in order for the discharge to be
-// considered valid.
+// publicKeyDischarge implements security.Discharge. It specifies a discharge
+// for a publicKeyCaveat, and includes a signature that can be verified with
+// the validation key of the publicKeyCaveat. Additionally, the discharge may
+// also include caveats which must all validate in order for the discharge to
+// be considered valid.
type publicKeyDischarge struct {
- // ThirdPartyCaveatID is used to match a Discharge with the Caveat it is for.
- ThirdPartyCaveatID security.ThirdPartyCaveatID
+ // CaveatID is used to match this Discharge to the the ThirdPartyCaveat it is for.
+ CaveatID string
// Caveats under which this Discharge is valid.
- Caveats []wire.Caveat
+ Caveats [][]byte
- // Signature on the contents of the discharge obtained using the private key
- // corresponding to the validaton key in the caveat.
+ // Signature on the contents of the discharge that can be verified using the
+ // validaton key in the publicKeycaveat this discharge is for.
Signature security.Signature
}
-// CaveatID returns a unique identity for the discharge based on the random nonce and
-// restriction embedded in the discharge.
-func (d *publicKeyDischarge) CaveatID() security.ThirdPartyCaveatID {
- return d.ThirdPartyCaveatID
+func (d *publicKeyDischarge) ID() string {
+ return d.CaveatID
}
-func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ServiceCaveat {
- return wire.DecodeThirdPartyCaveats(d.Caveats)
+func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ThirdPartyCaveat {
+ return vsecurity.ThirdPartyCaveats(d.Caveats...)
}
-// sign uses the provided identity to sign the contents of the discharge.
-func (d *publicKeyDischarge) sign(discharger security.PrivateID) (err error) {
- d.Signature, err = discharger.Sign(d.contentHash())
+func (d *publicKeyDischarge) sign(signer security.Signer) (err error) {
+ d.Signature, err = signer.Sign(d.contentHash())
return
}
@@ -139,72 +132,74 @@
h := sha256.New()
tmp := make([]byte, binary.MaxVarintLen64)
- wire.WriteBytes(h, tmp, []byte(d.ThirdPartyCaveatID))
+ wire.WriteString(h, tmp, d.CaveatID)
for _, cav := range d.Caveats {
- wire.WriteString(h, tmp, string(cav.Service))
- wire.WriteBytes(h, tmp, cav.Bytes)
+ wire.WriteBytes(h, tmp, cav)
}
return h.Sum(nil)
}
-// NewPublicKeyCaveat returns a new third-party caveat from the provided restriction,
-// third-party identity, and third-party location.
-func NewPublicKeyCaveat(dischargeMintingCaveat security.Caveat, thirdParty security.PublicID, location string, requirements security.ThirdPartyRequirements) (security.ThirdPartyCaveat, error) {
+// NewPublicKeyCaveat returns a security.ThirdPartyCaveat which requires a
+// discharge from a principal identified by the public key 'key' and present
+// at the object name 'location'. This discharging principal is expected to
+// validate 'caveat' before issuing a discharge.
+func NewPublicKeyCaveat(caveat security.Caveat, key *ecdsa.PublicKey, location string, requirements security.ThirdPartyRequirements) (security.ThirdPartyCaveat, error) {
nonce := make([]uint8, nonceLength)
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
var validationKey wire.PublicKey
- if err := validationKey.Encode(thirdParty.PublicKey()); err != nil {
+ if err := validationKey.Encode(key); err != nil {
return nil, err
}
- var mintingCaveatEncoded bytes.Buffer
- vom.NewEncoder(&mintingCaveatEncoded).Encode(dischargeMintingCaveat)
return &publicKeyCaveat{
RandNonce: nonce,
- DischargeMintingCaveat: mintingCaveatEncoded.Bytes(),
+ DischargeMintingCaveat: caveat.Bytes(),
ValidationKey: validationKey,
ThirdPartyLocation: location,
ThirdPartyRequirements: requirements,
}, nil
}
-// NewPublicKeyDischarge returns a new discharge for the provided caveat
-// after verifying that the caveats for minting a discharge are met.
+// NewPublicKeyDischarge returns a new discharge for the provided 'tp'
+// after validating any restrictions specified in it under context 'ctx'.
//
-// The CaveatID of the discharge is the same as the ID of the caveat, and
-// the discharge includes the provided service caveats along with a universal
-// expiry caveat for the provided duration. The discharge also includes a
-// signature over its contents obtained from the provided private key.
-func NewPublicKeyDischarge(discharger security.PrivateID, caveat security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, caveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
- cav, ok := caveat.(*publicKeyCaveat)
+// The ID of the discharge is the same as the ID of 'caveat', and the discharge
+// will be usable for the provided 'duration' only if:
+// (1) 'caveats' are met when using the discharge.
+// (2) 'tp' was obtained from NewPublicKeyCaveat using a key that is the same as
+// the provided signer's PublicKey.
+func NewPublicKeyDischarge(signer security.Signer, tp security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, caveats []security.Caveat) (security.Discharge, error) {
+ pkCaveat, ok := tp.(*publicKeyCaveat)
if !ok {
- return nil, fmt.Errorf("cannot mint discharges for %T", caveat)
+ return nil, fmt.Errorf("cannot mint discharges for %T", tp)
}
- var mintingCaveat security.Caveat
- mcBuf := bytes.NewReader(cav.DischargeMintingCaveat)
- if err := vom.NewDecoder(mcBuf).Decode(&mintingCaveat); err != nil {
- return nil, fmt.Errorf("failed to decode DischargeMintingCaveat: %s", err)
- }
- if err := mintingCaveat.Validate(ctx); err != nil {
- return nil, fmt.Errorf("failed to validate DischargeMintingCaveat: %s", err)
- }
- now := time.Now()
- expiryCaveat := &Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}
- caveats = append(caveats, UniversalCaveat(expiryCaveat))
- encodedCaveats, err := wire.EncodeCaveats(caveats)
+
+ v, err := vsecurity.CaveatValidators(pkCaveat.DischargeMintingCaveat)
if err != nil {
- return nil, fmt.Errorf("failed to encode caveats in discharge: %v", err)
+ return nil, err
}
+ if err := v[0].Validate(ctx); err != nil {
+ return nil, err
+ }
+
+ now := time.Now()
+ expiryCaveat, err := security.NewCaveat(&Expiry{IssueTime: now, ExpiryTime: now.Add(duration)})
+ if err != nil {
+ return nil, err
+ }
+
+ caveats = append(caveats, expiryCaveat)
discharge := &publicKeyDischarge{
- ThirdPartyCaveatID: caveat.ID(),
- Caveats: encodedCaveats,
+ CaveatID: tp.ID(),
+ Caveats: vsecurity.CaveatBytes(caveats...),
}
- // TODO(ashankar,ataly): Should discharger necessarily be the same as ctx.LocalID()?
+
+ // TODO(ashankar,ataly): Should signer necessarily be the same as ctx.LocalID()?
// If so, need the PrivateID object corresponding to ctx.LocalID.
- if err := discharge.sign(discharger); err != nil {
+ if err := discharge.sign(signer); err != nil {
return nil, err
}
return discharge, nil
diff --git a/security/util.go b/security/util.go
index b709c31..31987a0 100644
--- a/security/util.go
+++ b/security/util.go
@@ -1,8 +1,10 @@
package security
import (
+ "bytes"
"encoding/base64"
"encoding/json"
+ "fmt"
"io"
"veyron2/security"
@@ -55,3 +57,46 @@
func SaveACL(w io.Writer, acl security.ACL) error {
return json.NewEncoder(w).Encode(acl)
}
+
+// CaveatBytes returns a slice containing the Bytes of the provided 'caveats'.
+func CaveatBytes(caveats ...security.Caveat) [][]byte {
+ b := make([][]byte, len(caveats))
+ for i, c := range caveats {
+ b[i] = c.Bytes()
+ }
+ return b
+}
+
+// CaveatValidators returns the set of security.CaveatValidators
+// obtained by decoding the provided caveat bytes.
+//
+// It is an error if any of the provided caveat bytes cannot
+// be decoded into a security.CaveatValidator.
+func CaveatValidators(caveats ...[]byte) ([]security.CaveatValidator, error) {
+ if len(caveats) == 0 {
+ return nil, nil
+ }
+ validators := make([]security.CaveatValidator, len(caveats))
+ for i, c := range caveats {
+ var v security.CaveatValidator
+ if err := vom.NewDecoder(bytes.NewReader(c)).Decode(&v); err != nil {
+ return nil, fmt.Errorf("caveat bytes could not be VOM-decoded: %s", err)
+ }
+ validators[i] = v
+ }
+ return validators, nil
+}
+
+// ThirdPartyCaveats returns the set of security.ThirdPartyCaveats
+// that could be successfully decoded from the provided caveat bytes.
+func ThirdPartyCaveats(caveats ...[]byte) []security.ThirdPartyCaveat {
+ var tpCaveats []security.ThirdPartyCaveat
+ for _, c := range caveats {
+ var t security.ThirdPartyCaveat
+ if err := vom.NewDecoder(bytes.NewReader(c)).Decode(&t); err != nil {
+ continue
+ }
+ tpCaveats = append(tpCaveats, t)
+ }
+ return tpCaveats
+}
diff --git a/security/util_test.go b/security/util_test.go
index d535039..2cf2da4 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -2,10 +2,12 @@
import (
"bytes"
+ "fmt"
"reflect"
"testing"
"veyron2/security"
+ "veyron2/vom"
)
func TestLoadSaveIdentity(t *testing.T) {
@@ -49,3 +51,76 @@
t.Fatalf("Got ACL %v, but want %v", loadedACL, acl)
}
}
+
+// fpCaveat implements security.CaveatValidator.
+type fpCaveat struct{}
+
+func (fpCaveat) Validate(security.Context) error { return nil }
+
+// tpCaveat implements security.ThirdPartyCaveat.
+type tpCaveat struct{}
+
+func (tpCaveat) Validate(security.Context) (err error) { return }
+func (tpCaveat) ID() (id string) { return }
+func (tpCaveat) Location() (loc string) { return }
+func (tpCaveat) Requirements() (r security.ThirdPartyRequirements) { return }
+
+func TestCaveatUtil(t *testing.T) {
+ type b [][]byte
+ type v []security.CaveatValidator
+ type tp []security.ThirdPartyCaveat
+
+ newCaveatBytes := func(v security.CaveatValidator) []byte {
+ cav, err := security.NewCaveat(v)
+ if err != nil {
+ t.Fatalf("NewCaveat failed: %s", err)
+ }
+ return cav.Bytes()
+ }
+ var (
+ fpCavBytes = newCaveatBytes(fpCaveat{})
+ tpCavBytes = newCaveatBytes(tpCaveat{})
+ invalid = []byte("fake")
+ )
+ testdata := []struct {
+ caveats [][]byte
+ validators []security.CaveatValidator
+ tpCaveats []security.ThirdPartyCaveat
+ }{
+ {nil, nil, nil},
+ {b{fpCavBytes}, v{fpCaveat{}}, nil},
+ {b{tpCavBytes}, v{tpCaveat{}}, tp{tpCaveat{}}},
+ {b{fpCavBytes, tpCavBytes}, v{fpCaveat{}, tpCaveat{}}, tp{tpCaveat{}}},
+ }
+ for i, d := range testdata {
+ // Test CaveatValidators.
+ got, err := CaveatValidators(d.caveats...)
+ if err != nil {
+ t.Errorf("CaveatValidators(%v) failed: %s", d.caveats, err)
+ continue
+ }
+ if !reflect.DeepEqual(got, d.validators) {
+ fmt.Println("TEST ", i)
+ t.Errorf("CaveatValidators(%v): got: %#v, want: %#v", d.caveats, got, d.validators)
+ continue
+ }
+ if _, err := CaveatValidators(append(d.caveats, invalid)...); err == nil {
+ t.Errorf("CaveatValidators(%v) succeeded unexpectedly", d.caveats)
+ continue
+ }
+ // Test ThirdPartyCaveats.
+ if got := ThirdPartyCaveats(d.caveats...); !reflect.DeepEqual(got, d.tpCaveats) {
+ t.Errorf("ThirdPartyCaveats(%v): got: %#v, want: %#v", d.caveats, got, d.tpCaveats)
+ continue
+ }
+ if got := ThirdPartyCaveats(append(d.caveats, invalid)...); !reflect.DeepEqual(got, d.tpCaveats) {
+ t.Errorf("ThirdPartyCaveats(%v): got: %#v, want: %#v", d.caveats, got, d.tpCaveats)
+ continue
+ }
+ }
+}
+
+func init() {
+ vom.Register(&fpCaveat{})
+ vom.Register(&tpCaveat{})
+}