blob: b6ad17b79d1c3fcc383d037348b1e54a69f810b3 [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"
"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
}
// Match determines if the PublicID's chained name can be extended to match the
// provided PrincipalPattern. An extension of a chained name is any name obtained
// by joining additional strings to the name using wire.ChainSeparator. Ex: extensions
// of the name "foo/bar" are the names "foo/bar", "foo/bar/baz", "foo/bar/baz/car", and
// so on.
func (id *chainPublicID) Match(pattern security.PrincipalPattern) bool {
return matchPrincipalPattern(id.Names(), pattern)
}
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 {
publicID *chainPublicID
privateKey *ecdsa.PrivateKey
}
func (id *chainPrivateID) PublicID() security.PublicID { return id.publicID }
func (id *chainPrivateID) Sign(message []byte) (signature security.Signature, err error) {
signature.R, signature.S, err = ecdsa.Sign(rand.Reader, id.privateKey, message)
return
}
func (id *chainPrivateID) String() string { return fmt.Sprintf("PrivateID:%v", id.publicID) }
func (id *chainPrivateID) VomEncode() (*wire.ChainPrivateID, error) {
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),
}
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, security.UniversalCaveat(&caveat.Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}))
var err error
if cert.Caveats, err = wire.EncodeCaveats(caveats); err != nil {
return nil, err
}
vomID, err := id.VomEncode()
if err != nil {
return nil, err
}
if err := cert.Sign(vomID); 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{
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 containing a freshly generated
// private key, and a single self-signed certificate specifying the provided
// name and the public key corresponding to the generated private key.
func newChainPrivateID(name string) (security.PrivateID, error) {
if err := wire.ValidateBlessingName(name); err != nil {
return nil, err
}
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
id := &chainPrivateID{
publicID: &chainPublicID{
certificates: []wire.Certificate{{Name: name}},
name: name,
publicKey: &key.PublicKey,
rootKey: &key.PublicKey,
},
privateKey: key,
}
cert := &id.publicID.certificates[0]
if err := cert.PublicKey.Encode(&key.PublicKey); err != nil {
return nil, err
}
vomID, err := id.VomEncode()
if err != nil {
return nil, err
}
if err := cert.Sign(vomID); err != nil {
return nil, err
}
return id, nil
}
func init() {
vom.Register(chainPublicID{})
vom.Register(chainPrivateID{})
}