veyron2/security: Implementation of a public-key signature based ThirdPartyCaveat.
This commit includes:
- VDL specification of the ThirdPartyCaveat and corresponding discharge
- Factory function for creating ThirdPartyCaveats
- Principal.MintDischarge for creating Discharges
Miscellaneous:
- veyron/security/caveat package is now removed
- Added a "String" method to the Caveat type to print out useful information
- Renamed "UnconstrainedDelegation" to "UnconstrainedUse" since this "Caveat"
can apply to both blessings and discharges.
- Renamed "contentHash" to "digest" to comply with more commonly used
terminology ("message digest" or "digest")
Change-Id: I0ccce2557b4c3c5cba0c3cc7acba62b8dc118339
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index bb9f3b6..be1655c 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -23,7 +23,6 @@
isecurity "veyron.io/veyron/veyron/runtimes/google/security"
tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
vsecurity "veyron.io/veyron/veyron/security"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc"
@@ -569,7 +568,7 @@
}
func mkThirdPartyCaveat(discharger security.PublicID, location string, c security.Caveat) security.Caveat {
- tpc, err := caveat.NewPublicKeyCaveat(c, discharger.PublicKey(), location, security.ThirdPartyRequirements{})
+ tpc, err := security.NewPublicKeyCaveat(discharger.PublicKey(), location, security.ThirdPartyRequirements{}, c)
if err != nil {
panic(err)
}
@@ -605,7 +604,7 @@
dischargerID = serverID.PublicID()
mkClientID = func(req security.ThirdPartyRequirements) security.PrivateID {
- tpc, err := caveat.NewPublicKeyCaveat(newCaveat(alwaysValidCaveat{}), dischargerID.PublicKey(), "mountpoint/discharger", req)
+ tpc, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), "mountpoint/discharger", req, newCaveat(alwaysValidCaveat{}))
if err != nil {
t.Fatalf("Failed to create ThirdPartyCaveat: %v", err)
}
diff --git a/runtimes/google/security/identity_chain.go b/runtimes/google/security/identity_chain.go
index d21aea6..2855a36 100644
--- a/runtimes/google/security/identity_chain.go
+++ b/runtimes/google/security/identity_chain.go
@@ -13,7 +13,6 @@
"veyron.io/veyron/veyron/runtimes/google/security/keys"
vsecurity "veyron.io/veyron/veyron/security"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/security/wire"
@@ -232,7 +231,16 @@
}
func (id *chainPrivateID) MintDischarge(cav security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, dischargeCaveats []security.Caveat) (security.Discharge, error) {
- return caveat.NewPublicKeyDischarge(id, cav, ctx, duration, dischargeCaveats)
+ // TODO(ashankar): HACK using new API to fit into old. This will go away anyway when we get rid of PrivateID and PublicID before release.
+ principal, err := security.CreatePrincipal(id.signer)
+ if err != nil {
+ return nil, err
+ }
+ expiry, err := security.ExpiryCaveat(time.Now().Add(duration))
+ if err != nil {
+ return nil, err
+ }
+ return principal.MintDischarge(cav, ctx, expiry, dischargeCaveats...)
}
func (id *chainPrivateID) Sign(message []byte) (security.Signature, error) {
diff --git a/runtimes/google/security/identity_test.go b/runtimes/google/security/identity_test.go
index 33b9726..c10538f 100644
--- a/runtimes/google/security/identity_test.go
+++ b/runtimes/google/security/identity_test.go
@@ -11,7 +11,6 @@
"time"
"veyron.io/veyron/veyron/lib/testutil/blackbox"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/security/wire"
"veyron.io/veyron/veyron2/vlog"
@@ -473,7 +472,7 @@
func TestThirdPartyCaveatMinting(t *testing.T) {
minter := newChain("minter")
- cav, err := caveat.NewPublicKeyCaveat(newCaveat(proximityCaveat{}), minter.PublicID().PublicKey(), "location", security.ThirdPartyRequirements{})
+ cav, err := security.NewPublicKeyCaveat(minter.PublicID().PublicKey(), "location", security.ThirdPartyRequirements{}, newCaveat(proximityCaveat{}))
if err != nil {
t.Fatalf("security.NewPublicKeyCaveat failed: %s", err)
}
@@ -505,7 +504,7 @@
return derive(bless(id.PublicID(), googleChain, name), id)
}
mkTPCaveat := func(id security.PrivateID) security.ThirdPartyCaveat {
- c, err := caveat.NewPublicKeyCaveat(newCaveat(alwaysValidCaveat{}), id.PublicID().PublicKey(), fmt.Sprintf("%v location", id.PublicID()), security.ThirdPartyRequirements{})
+ c, err := security.NewPublicKeyCaveat(id.PublicID().PublicKey(), fmt.Sprintf("%v location", id.PublicID()), security.ThirdPartyRequirements{}, newCaveat(alwaysValidCaveat{}))
if err != nil {
t.Fatalf("NewPublicKeyCaveat with PublicKey of: %v failed: %s", id, err)
}
@@ -567,7 +566,7 @@
ctxEmpty: "missing discharge",
ctxGoogleAtOther: "security.peerBlessingsCaveat=[google] fails validation",
ctxExpired: "security.unixTimeExpiryCaveat",
- ctxInvalid: "invalid signature",
+ ctxInvalid: "signature verification on discharge",
}
)
@@ -626,7 +625,7 @@
func TestThirdPartyCaveatAccessors(t *testing.T) {
mkTPCaveat := func(id security.PublicID) security.ThirdPartyCaveat {
- tpCav, err := caveat.NewPublicKeyCaveat(newCaveat(alwaysValidCaveat{}), id.PublicKey(), "someLocation", security.ThirdPartyRequirements{})
+ tpCav, err := security.NewPublicKeyCaveat(id.PublicKey(), "someLocation", security.ThirdPartyRequirements{}, newCaveat(alwaysValidCaveat{}))
if err != nil {
t.Fatalf("NewPublicKeyCaveat with PublicKey of: %v failed: %s", id, err)
}
diff --git a/security/caveat/public_key_caveat.go b/security/caveat/public_key_caveat.go
deleted file mode 100644
index 76e8548..0000000
--- a/security/caveat/public_key_caveat.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// Package caveat will be removed as the new security API takes shape before November 1, 2014.
-package caveat
-
-import (
- "crypto/rand"
- "crypto/sha256"
- "encoding/binary"
- "fmt"
- "time"
-
- vsecurity "veyron.io/veyron/veyron/security"
-
- "veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/security/wire"
- "veyron.io/veyron/veyron2/vom"
-)
-
-// nonceLength specifies the length in bytes of the random nonce used
-// in publicKeyCaveat and publicKeyDischarge.
-//
-// TODO(ataly, ashankar): Nonce length of 16 bytes was chosen very conservatively.
-// The purpose of the random nonce is essentially to separate caveats that have
-// the same restriction and validation key (in order to prevent discharge replay).
-// Can we use 4bytes instead?.
-const nonceLength = 16
-
-// publicKeyCaveat implements security.ThirdPartyCaveat. It specifies a restriction,
-// 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.
-//
-// TODO(ataly): This should be a VDL defined type.
-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.
- DischargeMintingCaveat security.Caveat
- // ValidationKey specifies the public key of the discharging-party.
- ValidationKey wire.PublicKey
- // ThirdPartyLocation specifies the object name of the discharging-party.
- ThirdPartyLocation string
- // ThirdPartyRequirements specify the information wanted in order to issue a discharge.
- ThirdPartyRequirements security.ThirdPartyRequirements
-}
-
-// ID returns a unique 32bytes long identity for the caveat based on the random nonce
-// and SHA-256 hash of the restriction embedded in the caveat.
-//
-// 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() string {
- return id(c.RandNonce, c.DischargeMintingCaveat.ValidatorVOM)
-}
-
-func (c *publicKeyCaveat) Location() string {
- return c.ThirdPartyLocation
-}
-
-func (c *publicKeyCaveat) String() string {
- return fmt.Sprintf("publicKeyCaveat{DischargeMintingCaveat: (%v bytes), ThirdPartyLocation: %q}", len(c.DischargeMintingCaveat.ValidatorVOM), c.ThirdPartyLocation)
-}
-
-func (c *publicKeyCaveat) Requirements() security.ThirdPartyRequirements {
- return c.ThirdPartyRequirements
-}
-
-func (c *publicKeyCaveat) Validate(ctx security.Context) error {
- // Check whether the context has a dicharge matching the caveat's ID.
- dis, ok := ctx.Discharges()[c.ID()]
- if !ok {
- return fmt.Errorf("missing discharge")
- }
-
- pkDischarge, ok := dis.(*publicKeyDischarge)
- if !ok {
- return fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis)
- }
-
- // Validate all caveats present on the discharge.
- 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 err
- }
- if !pkDischarge.Signature.Verify(key, pkDischarge.contentHash()) {
- return fmt.Errorf("discharge %v has invalid signature", dis)
- }
- return nil
-}
-
-// 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.
-//
-// TODO(ataly): This should be a VDL-defined type?
-type publicKeyDischarge struct {
- // CaveatID is used to match this Discharge to the the ThirdPartyCaveat it is for.
- CaveatID string
-
- // Caveats under which this Discharge is valid.
- Caveats []security.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
-}
-
-func (d *publicKeyDischarge) ID() string {
- return d.CaveatID
-}
-
-func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ThirdPartyCaveat {
- return vsecurity.ThirdPartyCaveats(d.Caveats...)
-}
-
-func (d *publicKeyDischarge) sign(signer security.PrivateID) (err error) {
- d.Signature, err = signer.Sign(d.contentHash())
- return
-}
-
-func (d *publicKeyDischarge) contentHash() []byte {
- h := sha256.New()
- tmp := make([]byte, binary.MaxVarintLen64)
-
- wire.WriteString(h, tmp, d.CaveatID)
- for _, cav := range d.Caveats {
- wire.WriteBytes(h, tmp, cav.ValidatorVOM)
- }
- return h.Sum(nil)
-}
-
-// 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 security.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(key); err != nil {
- return nil, err
- }
-
- return &publicKeyCaveat{
- RandNonce: nonce,
- DischargeMintingCaveat: caveat,
- ValidationKey: validationKey,
- ThirdPartyLocation: location,
- ThirdPartyRequirements: requirements,
- }, nil
-}
-
-// NewPublicKeyDischarge returns a new discharge for the provided 'tp'
-// after validating any restrictions specified in it under context 'ctx'.
-//
-// 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.PrivateID, 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", tp)
- }
-
- v, err := vsecurity.CaveatValidators(pkCaveat.DischargeMintingCaveat)
- if err != nil {
- return nil, err
- }
- if err := v[0].Validate(ctx); err != nil {
- return nil, err
- }
-
- expiryCaveat, err := security.ExpiryCaveat(time.Now().Add(duration))
- if err != nil {
- return nil, err
- }
-
- caveats = append(caveats, expiryCaveat)
- discharge := &publicKeyDischarge{CaveatID: tp.ID(), Caveats: caveats}
-
- // 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(signer); err != nil {
- return nil, err
- }
- return discharge, nil
-}
-
-// id calculates the sha256 hash of length-delimited byte slices
-func id(slices ...[]byte) string {
- h := sha256.New()
- tmp := make([]byte, binary.MaxVarintLen64)
-
- for _, slice := range slices {
- wire.WriteBytes(h, tmp, slice)
- }
- return string(h.Sum(nil))
-}
-
-func init() {
- vom.Register(publicKeyCaveat{})
- vom.Register(publicKeyDischarge{})
-}
diff --git a/services/identity/revocation/revocation_manager.go b/services/identity/revocation/revocation_manager.go
index c05bd5e..f79e740 100644
--- a/services/identity/revocation/revocation_manager.go
+++ b/services/identity/revocation/revocation_manager.go
@@ -10,7 +10,6 @@
"sync"
"time"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron/services/identity/util"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
@@ -35,7 +34,7 @@
if err != nil {
return nil, err
}
- cav, err := caveat.NewPublicKeyCaveat(restriction, dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{})
+ cav, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{}, restriction)
if err != nil {
return nil, err
}
diff --git a/services/security/discharger/revoker.go b/services/security/discharger/revoker.go
index e7c23a1..8ccea7b 100644
--- a/services/security/discharger/revoker.go
+++ b/services/security/discharger/revoker.go
@@ -9,7 +9,6 @@
"os"
"path/filepath"
"sync"
- "veyron.io/veyron/veyron/security/caveat"
ssecurity "veyron.io/veyron/veyron/services/security"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/security"
@@ -73,7 +72,7 @@
if err != nil {
return revocation, nil, err
}
- cav, err := caveat.NewPublicKeyCaveat(restriction, dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{})
+ cav, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{}, restriction)
return revocation, cav, err
}