blob: af2be258459475d8f220fceeab950262d69d17b7 [file] [log] [blame]
package vc
import (
"bytes"
"errors"
"fmt"
"io"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/crypto"
"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
"veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
)
var (
authServerContextTag = []byte("VCauthS\x00")
authClientContextTag = []byte("VCauthC\x00")
)
var (
errSameChannelPublicKey = errors.New("same public keys for both ends of the channel")
errChannelIDMismatch = errors.New("channel id does not match expectation")
errInvalidSignatureInMessage = errors.New("signature does not verify in authentication handshake message")
errNoCertificatesReceived = errors.New("no certificates received")
errSingleCertificateRequired = errors.New("exactly one X.509 certificate chain with exactly one certificate is required")
)
// authenticateAsServer executes the authentication protocol at the server and
// returns the blessings used to authenticate both ends.
func authenticateAsServer(conn io.ReadWriteCloser, principal security.Principal, crypter crypto.Crypter, v version.IPCVersion) (client, server security.Blessings, err error) {
defer conn.Close()
server = principal.BlessingStore().Default()
if server == nil {
return nil, nil, fmt.Errorf("BlessingStore does not contain a default set of blessings, cannot act as a server")
}
if err := writeBlessings(conn, authServerContextTag, crypter, principal, server, v); err != nil {
return nil, nil, err
}
if client, err = readBlessings(conn, authClientContextTag, crypter, v); err != nil {
return nil, nil, err
}
return client, server, nil
}
// authenticateAsClient executes the authentication protocol at the client and
// returns the blessings used to authenticate both ends.
//
// The client will only share its identity if its blessing store has one marked
// for the server (who shares its blessings first).
//
// TODO(ashankar): Seems like there is no way the blessing store
// can say that it does NOT want to share the default blessing with the server?
func authenticateAsClient(conn io.ReadWriteCloser, principal security.Principal, crypter crypto.Crypter, v version.IPCVersion) (server, client security.Blessings, err error) {
defer conn.Close()
if server, err = readBlessings(conn, authServerContextTag, crypter, v); err != nil {
return nil, nil, err
}
serverB := server.ForContext(&serverAuthContext{
self: principal,
remote: server,
// TODO(ashankar): Get the local and remote endpoint here?
// There is also a bootstrapping problem here. For example, let's say
// (1) server has the blessing "provider/server" with a PeerIdentity caveat of "provider/client"
// (2) Client has a blessing "provider/client" tagged for "provider/server" in its BlessingStore
// How do we get that working?
// One option is to have a UnionOfBlessings of all blessings of the client in the BlessingStore
// made available to serverAuthContext.LocalBlessings for this call.
})
client = principal.BlessingStore().ForPeer(serverB...)
if client == nil {
return nil, nil, fmt.Errorf("no blessing tagged for peer %v in the BlessingStore", serverB)
}
if err = writeBlessings(conn, authClientContextTag, crypter, principal, client, v); err != nil {
return nil, nil, err
}
return server, client, nil
}
func writeBlessings(w io.Writer, tag []byte, crypter crypto.Crypter, p security.Principal, b security.Blessings, v version.IPCVersion) error {
signature, err := p.Sign(append(tag, crypter.ChannelBinding()...))
if err != nil {
return err
}
var buf bytes.Buffer
enc := vom.NewEncoder(&buf)
if err := enc.Encode(signature); err != nil {
return err
}
if err := enc.Encode(b); err != nil {
return err
}
msg, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
if err != nil {
return err
}
defer msg.Release()
return vom.NewEncoder(w).Encode(msg.Contents)
}
func readBlessings(r io.Reader, tag []byte, crypter crypto.Crypter, v version.IPCVersion) (security.Blessings, error) {
var msg []byte
if err := vom.NewDecoder(r).Decode(&msg); err != nil {
return nil, fmt.Errorf("failed to read handshake message: %v", err)
}
buf, err := crypter.Decrypt(iobuf.NewSlice(msg))
if err != nil {
return nil, err
}
defer buf.Release()
dec := vom.NewDecoder(bytes.NewReader(buf.Contents))
var wireb security.WireBlessings
var sig security.Signature
if err := dec.Decode(&sig); err != nil {
return nil, err
}
if err := dec.Decode(&wireb); err != nil {
return nil, err
}
b, err := security.NewBlessings(wireb)
if err != nil {
return nil, err
}
if b == nil {
return nil, errNoCertificatesReceived
}
if !sig.Verify(b.PublicKey(), append(tag, crypter.ChannelBinding()...)) {
return nil, errInvalidSignatureInMessage
}
return b, nil
}
// security.Context implementation used when extracting blessings from what the
// server presents during authentication.
type serverAuthContext struct {
self security.Principal
remote security.Blessings
}
func (*serverAuthContext) Method() string { return "" }
func (*serverAuthContext) Name() string { return "" }
func (*serverAuthContext) Suffix() string { return "" }
func (*serverAuthContext) Label() (l security.Label) { return l }
func (c *serverAuthContext) Discharges() map[string]security.Discharge { return nil }
func (c *serverAuthContext) LocalPrincipal() security.Principal { return c.self }
func (c *serverAuthContext) LocalBlessings() security.Blessings { return nil }
func (c *serverAuthContext) RemoteBlessings() security.Blessings { return c.remote }
func (c *serverAuthContext) LocalEndpoint() naming.Endpoint { return nil }
func (c *serverAuthContext) RemoteEndpoint() naming.Endpoint { return nil }