blob: 8ccea7b53036726333b90d20ea2942318c328862 [file] [log] [blame]
package discharger
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
ssecurity "veyron.io/veyron/veyron/services/security"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
)
// The revocation caveats will be stored in a directory with a file for each.
type revocationDir string
// Returns whether the caveat exists in the recovation file.
func (dir revocationDir) exists(cav string) (bool, error) {
if len(dir) == 0 {
return false, fmt.Errorf("missing call to NewRecocationCaveat")
}
_, err := os.Stat(filepath.Join(string(dir), cav))
return !os.IsNotExist(err), nil
}
func (dir revocationDir) put(caveatNonce string, caveatPreimage []byte) error {
if len(dir) == 0 {
return fmt.Errorf("missing call to NewRevoker")
}
return ioutil.WriteFile(filepath.Join(string(dir), caveatNonce), caveatPreimage, 0600)
}
func (dir revocationDir) Revoke(ctx ipc.ServerContext, caveatPreimage ssecurity.RevocationToken) error {
if len(dir) == 0 {
return fmt.Errorf("missing call to NewRevoker")
}
caveatNonce := sha256.Sum256(caveatPreimage[:])
return revocationService.put(hex.EncodeToString(caveatNonce[:]), caveatPreimage[:])
}
var revocationService struct {
revocationDir
sync.Mutex
}
type revocationCaveat [32]byte
func (cav revocationCaveat) Validate(security.Context) error {
exists, err := revocationService.exists(hex.EncodeToString(cav[:]))
if err != nil {
return err
}
if exists {
return fmt.Errorf("revoked")
}
return nil
}
// NewRevocationCaveat returns a security.ThirdPartyCaveat that discharger will
// mint discharges until explicitly told not to by calling Revoke on it
// (using the returned revocation token)
func NewRevocationCaveat(dischargerID security.PublicID, dischargerLocation string) (ssecurity.RevocationToken, security.ThirdPartyCaveat, error) {
var revocation ssecurity.RevocationToken
if _, err := rand.Read(revocation[:]); err != nil {
return revocation, nil, err
}
restriction, err := security.NewCaveat(revocationCaveat(sha256.Sum256(revocation[:])))
if err != nil {
return revocation, nil, err
}
cav, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{}, restriction)
return revocation, cav, err
}
// NewRevoker returns a revoker service implementation that persists information about revocations on the filesystem in dir.
// Can only be called once due to global variables.
func NewRevoker(dir string) (ssecurity.RevokerService, error) {
revocationService.Lock()
defer revocationService.Unlock()
if len(revocationService.revocationDir) > 0 {
return nil, fmt.Errorf("revoker.Revoker called more than once.")
}
revocationService.revocationDir = revocationDir(dir)
// create the directory if not already there.
os.MkdirAll(dir, 0700)
return revocationService.revocationDir, nil
}
func init() {
vom.Register(revocationCaveat{})
}