blob: e1300580f5251148e49d43591c02616c46764b17 [file] [log] [blame]
package security
// This file describes a certificate chain based implementation of security.PublicID.
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"math/big"
"reflect"
"time"
"veyron/runtimes/google/security/keys"
"veyron/security/caveat"
"veyron/security/signing"
"veyron2/security"
"veyron2/security/wire"
"veyron2/vom"
)
const (
// unknownIDProviderPrefix is the prefix added when stringifying
// an identity for which there is no entry for the root certificate
// in the trusted keys set.
unknownIDProviderPrefix = "unknown/"
// mistrustedIDProviderPrefix is the prefix added when stringifying
// an identity whose root certificate has a public key that does
// not exist in the (non-empty) set of trusted keys for that root.
mistrustedIDProviderPrefix = "mistrusted/"
)
// chainPublicID implements security.PublicID.
type chainPublicID struct {
certificates []wire.Certificate
// Fields derived from certificates in VomDecode
publicKey *ecdsa.PublicKey
rootKey *ecdsa.PublicKey
name string
}
func (id *chainPublicID) Names() []string {
// Return a name only if the identity provider is trusted.
if keys.LevelOfTrust(id.rootKey, id.certificates[0].Name) == keys.Trusted {
return []string{id.name}
}
return nil
}
func (id *chainPublicID) PublicKey() *ecdsa.PublicKey { return id.publicKey }
func (id *chainPublicID) String() string {
// Add a prefix if the identity provider is not trusted.
switch keys.LevelOfTrust(id.rootKey, id.certificates[0].Name) {
case keys.Trusted:
return id.name
case keys.Mistrusted:
return mistrustedIDProviderPrefix + id.name
default:
return unknownIDProviderPrefix + id.name
}
}
func (id *chainPublicID) VomEncode() (*wire.ChainPublicID, error) {
return &wire.ChainPublicID{Certificates: id.certificates}, nil
}
func (id *chainPublicID) VomDecode(w *wire.ChainPublicID) error {
if err := w.VerifyIntegrity(); err != nil {
return err
}
firstKey, err := w.Certificates[0].PublicKey.Decode()
if err != nil {
return err
}
lastKey, err := w.Certificates[len(w.Certificates)-1].PublicKey.Decode()
if err != nil {
return err
}
id.name = w.Name()
id.certificates = w.Certificates
id.publicKey = lastKey
id.rootKey = firstKey
return err
}
// Authorize checks if all caveats on the PublicID validate with respect to the
// provided context and if so returns the original PublicID. This method assumes that
// the existing PublicID was obtained after successfully decoding a serialized
// PublicID and hence has integrity.
func (id *chainPublicID) Authorize(context security.Context) (security.PublicID, error) {
for _, c := range id.certificates {
if err := c.ValidateCaveats(context); err != nil {
return nil, fmt.Errorf("not authorized because %v", err)
}
}
return id, nil
}
func (id *chainPublicID) ThirdPartyCaveats() (thirdPartyCaveats []security.ServiceCaveat) {
for _, c := range id.certificates {
thirdPartyCaveats = append(thirdPartyCaveats, wire.DecodeThirdPartyCaveats(c.Caveats)...)
}
return
}
// chainPrivateID implements security.PrivateID
type chainPrivateID struct {
security.Signer
publicID *chainPublicID
privateKey *ecdsa.PrivateKey // can be nil
}
func (id *chainPrivateID) PublicID() security.PublicID { return id.publicID }
func (id *chainPrivateID) String() string { return fmt.Sprintf("PrivateID:%v", id.publicID) }
func (id *chainPrivateID) VomEncode() (*wire.ChainPrivateID, error) {
if id.privateKey == nil {
// TODO(ataly): Figure out a clean way to serialize Signers.
return nil, fmt.Errorf("cannot vom-encode a chainPrivateID that doesn't have access to a private key")
}
pub, err := id.publicID.VomEncode()
if err != nil {
return nil, err
}
return &wire.ChainPrivateID{Secret: id.privateKey.D.Bytes(), PublicID: *pub}, nil
}
func (id *chainPrivateID) VomDecode(w *wire.ChainPrivateID) error {
id.publicID = new(chainPublicID)
if err := id.publicID.VomDecode(&w.PublicID); err != nil {
return err
}
id.privateKey = &ecdsa.PrivateKey{
PublicKey: *id.publicID.publicKey,
D: new(big.Int).SetBytes(w.Secret),
}
id.Signer = signing.NewClearSigner(id.privateKey)
return nil
}
// Bless returns a new PublicID by extending the ceritificate chain of the PrivateID's
// PublicID with a new certificate that has the provided blessingName, caveats, and an
// additional expiry caveat for the given duration.
func (id *chainPrivateID) Bless(blessee security.PublicID, blessingName string, duration time.Duration, caveats []security.ServiceCaveat) (security.PublicID, error) {
// The integrity of the PublicID blessee is assumed to have been verified
// (typically by a Vom decode).
if err := wire.ValidateBlessingName(blessingName); err != nil {
return nil, err
}
cert := wire.Certificate{Name: blessingName}
if err := cert.PublicKey.Encode(blessee.PublicKey()); err != nil {
return nil, err
}
now := time.Now()
caveats = append(caveats, caveat.UniversalCaveat(&caveat.Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}))
var err error
if cert.Caveats, err = wire.EncodeCaveats(caveats); err != nil {
return nil, err
}
vomPubID, err := id.publicID.VomEncode()
if err != nil {
return nil, err
}
if err := cert.Sign(id, vomPubID); err != nil {
return nil, err
}
w := &wire.ChainPublicID{
Certificates: append(id.publicID.certificates, cert),
}
return &chainPublicID{
certificates: w.Certificates,
publicKey: blessee.PublicKey(),
rootKey: id.publicID.rootKey,
name: w.Name(),
}, nil
}
func (id *chainPrivateID) Derive(pub security.PublicID) (security.PrivateID, error) {
if !reflect.DeepEqual(pub.PublicKey(), id.publicID.publicKey) {
return nil, errDeriveMismatch
}
switch p := pub.(type) {
case *chainPublicID:
return &chainPrivateID{
Signer: id.Signer,
publicID: p,
privateKey: id.privateKey,
}, nil
case *setPublicID:
privs := make([]security.PrivateID, len(*p))
var err error
for ix, ip := range *p {
if privs[ix], err = id.Derive(ip); err != nil {
return nil, fmt.Errorf("Derive failed for public id %d of %d in set: %v", ix, len(*p), err)
}
}
return setPrivateID(privs), nil
default:
return nil, fmt.Errorf("PrivateID of type %T cannot be used to Derive from PublicID of type %T", id, pub)
}
}
func (id *chainPrivateID) MintDischarge(cav security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
return caveat.NewPublicKeyDischarge(id, cav, ctx, duration, dischargeCaveats)
}
// newChainPrivateID returns a new PrivateID that uses the provided Signer to generate
// signatures. The returned PrivateID additionaly contains a single self-signed
// certificate with the given name.
//
// If a nil signer is provided, this method will generate a new public/private key pair
// and use a system-default signer, which stores the private key in the clear in the memory
// of the running process.
func newChainPrivateID(name string, signer security.Signer) (security.PrivateID, error) {
if err := wire.ValidateBlessingName(name); err != nil {
return nil, err
}
var privKey *ecdsa.PrivateKey
if signer == nil {
var err error
privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
signer = signing.NewClearSigner(privKey)
}
id := &chainPrivateID{
Signer: signer,
publicID: &chainPublicID{
certificates: []wire.Certificate{{Name: name}},
name: name,
publicKey: signer.PublicKey(),
rootKey: signer.PublicKey(),
},
privateKey: privKey,
}
// Self-sign the (single) certificate.
cert := &id.publicID.certificates[0]
if err := cert.PublicKey.Encode(signer.PublicKey()); err != nil {
return nil, err
}
vomPubID, err := id.publicID.VomEncode()
if err != nil {
return nil, err
}
if err := cert.Sign(id, vomPubID); err != nil {
return nil, err
}
return id, nil
}
func init() {
vom.Register(chainPublicID{})
vom.Register(chainPrivateID{})
}