blob: 495fb5d96cd6d235e6e3c9a1926961b46a43016b [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.
// Package revocation provides tools to create and manage revocation caveats.
package revocation
import (
"crypto/rand"
"database/sql"
"fmt"
"sync"
"time"
"v.io/v23/context"
"v.io/v23/security"
)
// RevocationManager persists information for revocation caveats to provided discharges and allow for future revocations.
type RevocationManager interface {
NewCaveat(discharger security.PublicKey, dischargerLocation string) (security.Caveat, error)
Revoke(caveatID string) error
GetRevocationTime(caveatID string) *time.Time
}
// revocationManager persists information for revocation caveats to provided discharges and allow for future revocations.
type revocationManager struct{}
// NewRevocationManager returns a RevocationManager that persists information about
// revocationCaveats in a SQL database and allows for revocation and caveat creation.
// This function can only be called once because of the use of global variables.
func NewRevocationManager(sqlDB *sql.DB) (RevocationManager, error) {
revocationLock.Lock()
defer revocationLock.Unlock()
if revocationDB != nil {
return nil, fmt.Errorf("NewRevocationManager can only be called once")
}
var err error
revocationDB, err = newSQLDatabase(sqlDB, "RevocationCaveatInfo")
if err != nil {
return nil, err
}
return &revocationManager{}, nil
}
var revocationDB database
var revocationLock sync.RWMutex
// NewCaveat returns a security.Caveat constructed with a ThirdPartyCaveat for which discharges will be
// issued iff Revoke has not been called for the returned caveat.
func (r *revocationManager) NewCaveat(discharger security.PublicKey, dischargerLocation string) (security.Caveat, error) {
var empty security.Caveat
var revocation [16]byte
if _, err := rand.Read(revocation[:]); err != nil {
return empty, err
}
notRevoked, err := security.NewCaveat(NotRevokedCaveat, revocation[:])
if err != nil {
return empty, err
}
cav, err := security.NewPublicKeyCaveat(discharger, dischargerLocation, security.ThirdPartyRequirements{}, notRevoked)
if err != nil {
return empty, err
}
if err = revocationDB.InsertCaveat(cav.ThirdPartyDetails().ID(), revocation[:]); err != nil {
return empty, err
}
return cav, nil
}
// Revoke disables discharges from being issued for the provided third-party caveat.
func (r *revocationManager) Revoke(caveatID string) error {
return revocationDB.Revoke(caveatID)
}
// GetRevocationTimestamp returns the timestamp at which a caveat was revoked.
// If the caveat wasn't revoked returns nil
func (r *revocationManager) GetRevocationTime(caveatID string) *time.Time {
timestamp, err := revocationDB.RevocationTime(caveatID)
if err != nil {
return nil
}
return timestamp
}
func isRevoked(_ *context.T, _ security.Call, key []byte) error {
revocationLock.RLock()
if revocationDB == nil {
revocationLock.RUnlock()
return fmt.Errorf("missing call to NewRevocationManager")
}
revocationLock.RUnlock()
revoked, err := revocationDB.IsRevoked(key)
if revoked {
return fmt.Errorf("revoked")
}
return err
}
func init() {
security.RegisterCaveatValidator(NotRevokedCaveat, isRevoked)
}