blob: a15d1de1b3a512d28ef9b5b80c22be11832ea026 [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"
icaveat "veyron/runtimes/google/security/caveat"
"veyron/runtimes/google/security/keys"
"veyron/runtimes/google/security/wire"
"veyron/security/caveat"
"veyron2/security"
"veyron2/vom"
)
// 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.
if keys.LevelOfTrust(id.rootKey, id.certificates[0].Name) != keys.Trusted {
return wire.UntrustedIDProviderPrefix + id.name
}
return 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 that the identity provider (root public key) is not
// mistrusted. 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) {
rootCert := id.certificates[0]
rootKey, err := rootCert.PublicKey.Decode()
if err != nil {
// unlikely to hit this case, as chainPublicID would have integrity.
return nil, err
}
// Implicit "caveat": The identity provider should not be mistrusted.
switch tl := keys.LevelOfTrust(rootKey, rootCert.Name); tl {
case keys.Unknown, keys.Trusted:
// No-op
default:
return nil, fmt.Errorf("%v public key(%v) for identity provider %q", tl, rootKey, rootCert.Name)
}
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
}
// PublicID returns the PublicID associated with the PrivateID.
func (id *chainPrivateID) PublicID() security.PublicID { return id.publicID }
// PrivateKey returns the private key associated with the PrivateID.
func (id *chainPrivateID) PrivateKey() *ecdsa.PrivateKey { return id.privateKey }
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, duration time.Duration, dischargeCaveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
switch c := cav.(type) {
case *icaveat.PublicKeyCaveat:
return icaveat.NewPublicKeyDischarge(c, id.privateKey, duration, dischargeCaveats)
}
return nil, fmt.Errorf("discharge cannot be constructed for ThirdPartyCaveat of type %T from PrivateID of type %T", cav, id)
}
// 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) {
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{})
}