blob: 76e8548ba2d57df1cc8f64ff9d8555a4c213aeed [file] [log] [blame]
// 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{})
}