blob: 8120cfff07d8333e1ecc3e6bf6b6fcb0ce3b9ce7 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This module implements a cache of data associated with the
// signatures, keyed by hash values of the data. The intent is that
// communicating devices will refer to the data using hashes, and transmit the
// data itself only if the device on the other side does not have the data in
// its cache.
package signing
import "crypto/sha256"
import "encoding/binary"
import "time"
import "v.io/x/ref/services/syncbase/signing/hashcache"
import "v.io/v23/context"
import "v.io/v23/security"
import "v.io/v23/vom"
// --------------------------------------------
// A BlessingsData contains information about a security.Blessings object. The
// object itself is referred to by UnmarshalledBlessings. The implementation
// constructs all instances; the client should not modify fields.
type BlessingsData struct {
UnmarshalledBlessings security.Blessings // The Blessings.
MarshalledBlessings []byte // VOM encoded Blessings.
MarshalledPublicKey []byte // Value from blessings.PublicKey().MarshalBinary().
}
// A ValidatorData is the extra data that a validator signs when validating and
// signing a DataWithSignature. Clients may construct instances to pass to
// AddValidatorData(), but should not modify the fields of a constructed
// ValidatorData.
type ValidatorData struct {
Names []string // Names of valid signing blessings in the Blessings referred to by BlessingsHash.
PublicKey security.PublicKey // The key used to create ValidatorSigned.
MarshalledPublicKey []byte // PublicKey, marshalled with MarshalBinary().
}
// hash() returns the hash of *vd. This hash should be used in the
// ValidatorDataHash field of DataWithSignature, and as the cache key of *vd
// in a ValidationCache.
func (vd *ValidatorData) hash() []byte {
hasher := sha256.New()
var buffer [256]byte
var buf []byte = buffer[:]
binary.LittleEndian.PutUint64(buf[:], uint64(len(vd.Names)))
hasher.Write(buf[:8])
for i := range vd.Names {
if len(vd.Names[i]) > len(buf) {
buf = make([]byte, len(vd.Names[i])+256)
}
hashByteVectorWithLength(hasher, []byte(vd.Names[i]))
}
hashByteVectorWithLength(hasher, vd.MarshalledPublicKey)
return hasher.Sum(nil)[:]
}
// A ValidationCache records recently-seen instances of BlessingsData and
// ValidatorData values, keys by hashes of the blessings and validator keys
// respectively. Values may expire from the cache if unused for a duration
// specified with NewValidationCache().
type ValidationCache struct {
blessingsCache *hashcache.Cache
validatorCache *hashcache.Cache
}
// NewValidationCache() returns a pointer to a new, empty ValidationCache with
// the specified expiry duration..
func NewValidationCache(expiry time.Duration) *ValidationCache {
return &ValidationCache{
blessingsCache: hashcache.New(expiry),
validatorCache: hashcache.New(expiry)}
}
// LookupBlessingsData() returns a pointer to the BlessingsData associated with
// blessingsHash in *vc. blessingsHash should have been returned by a previous
// call to AddBlessings() or AddWireBlessings() (possibly on another machine).
// nil is returned if the data is not present. The client should not modify
// *result, since it is shared with *vc.
func (vc *ValidationCache) LookupBlessingsData(ctx *context.T, blessingsHash []byte) (result *BlessingsData) {
value, found := vc.blessingsCache.Lookup(blessingsHash)
if found {
result = value.(*BlessingsData)
}
return result
}
// addBlessings() adds a BlessingsData for blessings to *vc, and returns a hash
// value, which if passed to LookupBlessingsData() will yield a pointer to the
// BlessingsData, or a non-nil error. The fields of BlessingsData other than
// MarshalledBlessings and UnmarshalledBlessings are constructed by this
// routine. Requires that blessings and marshalledBlessings represent the same
// data, or that marshalledBlessings be nil.
func (vc *ValidationCache) addBlessings(ctx *context.T, blessings security.Blessings,
marshalledBlessings []byte) (blessingsHash []byte, data *BlessingsData, err error) {
blessingsHash = blessings.UniqueID()
if value, found := vc.blessingsCache.Lookup(blessingsHash); found {
data = value.(*BlessingsData)
} else { // not found
var marshalledKey []byte
if marshalledBlessings == nil {
marshalledBlessings, err = vom.Encode(blessings)
}
if err == nil {
marshalledKey, err = blessings.PublicKey().MarshalBinary()
}
if err == nil {
data = &BlessingsData{
UnmarshalledBlessings: blessings,
MarshalledBlessings: marshalledBlessings,
MarshalledPublicKey: marshalledKey}
vc.blessingsCache.Add(blessingsHash, data)
}
}
return blessingsHash, data, err
}
// AddBlessings() adds a BlessingsData for blessings to *cv, and
// returns a hash value, which if passed to LookupBlessingsData() will yield a
// pointer to the BlessingsData, or a non-nil error. The fields of
// BlessingsData other than UnmarshalledBlessings are constructed by this
// routine.
func (vc *ValidationCache) AddBlessings(ctx *context.T, blessings security.Blessings) (blessingsHash []byte, data *BlessingsData, err error) {
return vc.addBlessings(ctx, blessings, nil)
}
// AddWireBlessings() adds a BlessingsData for blessings to *cv, and
// returns a hash value, which if passed to LookupBlessingsData() will yield a
// pointer to the BlessingsData, or a non-nil error. The fields of
// BlessingsData other than MarshalledBlessings are constructed by this
// routine.
func (vc *ValidationCache) AddWireBlessings(ctx *context.T,
marshalledBlessings []byte) (blessingsHash []byte, data *BlessingsData, err error) {
var blessings security.Blessings
err = vom.Decode(marshalledBlessings, &blessings)
if err == nil {
blessingsHash, data, err = vc.addBlessings(ctx, blessings, marshalledBlessings)
}
return blessingsHash, data, err
}
// LookupValidatorData() returns a pointer to the ValidatorData associated with
// hash validatorHash in *vc validatorHash should have been returned by a
// previous call to AddValidatorData() (possibly on another machine). nil is
// returned if the data is not present. The client should not modifiy *result,
// since it it shared with *vc.
func (vc *ValidationCache) LookupValidatorData(ctx *context.T, validatorHash []byte) (result *ValidatorData) {
value, found := vc.validatorCache.Lookup(validatorHash)
if found {
result = value.(*ValidatorData)
}
return result
}
// AddValidatorData() adds a ValidatorData *vd to cache *vc, and returns a hash
// value, which if passed to LookupValidatorData() will yield a pointer to the
// ValidatorData. The client should not modify *vd after the call, since it is
// shared with *vc.
func (vc *ValidationCache) AddValidatorData(ctx *context.T, vd *ValidatorData) (validatorDataHash []byte) {
validatorDataHash = vd.hash()
vc.validatorCache.Add(validatorDataHash, vd)
return validatorDataHash
}
// ToWireValidatorData() puts the wire form of ValidatorData *vd in *wvd.
func ToWireValidatorData(vd *ValidatorData) (wvd WireValidatorData) {
wvd.Names = make([]string, len(vd.Names))
copy(wvd.Names, vd.Names)
wvd.MarshalledPublicKey = make([]byte, len(vd.MarshalledPublicKey))
copy(wvd.MarshalledPublicKey, vd.MarshalledPublicKey)
return wvd
}
// FromWireValidatorData() puts the in-memory form of WireValidatorData *wvd in *vd.
func FromWireValidatorData(wvd *WireValidatorData) (vd ValidatorData, err error) {
vd.PublicKey, err = security.UnmarshalPublicKey(wvd.MarshalledPublicKey)
if err == nil {
vd.Names = make([]string, len(wvd.Names))
copy(vd.Names, wvd.Names)
vd.MarshalledPublicKey = make([]byte, len(wvd.MarshalledPublicKey))
copy(vd.MarshalledPublicKey, wvd.MarshalledPublicKey)
}
return vd, err
}