blob: 43442b36696b665816a6970d790cedbc0ccadd2e [file] [log] [blame]
package vc
import (
"bytes"
"errors"
"fmt"
"io"
"v.io/core/veyron/runtimes/google/ipc/stream/crypto"
"v.io/core/veyron/runtimes/google/lib/iobuf"
"v.io/v23/ipc/version"
"v.io/v23/security"
"v.io/v23/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")
errChecksumMismatch = errors.New("checksum mismatch")
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 the client.
func AuthenticateAsServer(conn io.ReadWriteCloser, principal security.Principal, server security.Blessings, dc DischargeClient, crypter crypto.Crypter, v version.IPCVersion) (client security.Blessings, err error) {
defer conn.Close()
if server.IsZero() {
return security.Blessings{}, errors.New("no blessings to present as a server")
}
var discharges []security.Discharge
if tpcavs := server.ThirdPartyCaveats(); len(tpcavs) > 0 && dc != nil {
discharges = dc.PrepareDischarges(nil, tpcavs, security.DischargeImpetus{})
}
if err = writeBlessings(conn, authServerContextTag, crypter, principal, server, discharges, v); err != nil {
return
}
if client, _, err = readBlessings(conn, authClientContextTag, crypter, v); err != nil {
return
}
return
}
// AuthenticateAsClient executes the authentication protocol at the client and
// returns the blessings used to authenticate both ends.
//
// The client will only share its blessings if the server (who shares its blessings first)
// is authorized as per the authorizer for this RPC.
func AuthenticateAsClient(conn io.ReadWriteCloser, crypter crypto.Crypter, params security.CallParams, auth *ServerAuthorizer, v version.IPCVersion) (server, client security.Blessings, serverDischarges map[string]security.Discharge, err error) {
defer conn.Close()
if server, serverDischarges, err = readBlessings(conn, authServerContextTag, crypter, v); err != nil {
return
}
// Authorize the server based on the provided authorizer.
if auth != nil {
params.RemoteBlessings = server
params.RemoteDischarges = serverDischarges
if err = auth.Authorize(params); err != nil {
return
}
}
// The client shares its blessings at RPC time (as the blessings may vary across
// RPCs). During VC handshake, the client simply sends a self-signed blessing
// in order to reveal its public key to the server.
principal := params.LocalPrincipal
client, err = principal.BlessSelf("vcauth")
if err != nil {
return security.Blessings{}, security.Blessings{}, nil, fmt.Errorf("failed to created self blessing: %v", err)
}
if err = writeBlessings(conn, authClientContextTag, crypter, principal, client, nil, v); err != nil {
return
}
return
}
func writeBlessings(w io.Writer, tag []byte, crypter crypto.Crypter, p security.Principal, b security.Blessings, discharges []security.Discharge, v version.IPCVersion) error {
signature, err := p.Sign(append(tag, crypter.ChannelBinding()...))
if err != nil {
return err
}
var buf bytes.Buffer
enc, err := vom.NewEncoder(&buf)
if err != nil {
return err
}
if err := enc.Encode(signature); err != nil {
return err
}
if err := enc.Encode(b); err != nil {
return err
}
if v >= version.IPCVersion7 {
wired := make([]security.WireDischarge, len(discharges))
for i, d := range discharges {
wired[i] = security.MarshalDischarge(d)
}
if err := enc.Encode(wired); err != nil {
return err
}
} else if v >= version.IPCVersion5 {
if err := enc.Encode(discharges); err != nil {
return err
}
}
msg, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
if err != nil {
return err
}
defer msg.Release()
enc, err = vom.NewEncoder(w)
if err != nil {
return err
}
return enc.Encode(msg.Contents)
}
func readBlessings(r io.Reader, tag []byte, crypter crypto.Crypter, v version.IPCVersion) (security.Blessings, map[string]security.Discharge, error) {
var msg []byte
var noBlessings security.Blessings
dec, err := vom.NewDecoder(r)
if err != nil {
return noBlessings, nil, fmt.Errorf("failed to create new decoder: %v", err)
}
if err := dec.Decode(&msg); err != nil {
return noBlessings, nil, fmt.Errorf("failed to read handshake message: %v", err)
}
buf, err := crypter.Decrypt(iobuf.NewSlice(msg))
if err != nil {
return noBlessings, nil, err
}
defer buf.Release()
dec, err = vom.NewDecoder(bytes.NewReader(buf.Contents))
if err != nil {
return noBlessings, nil, fmt.Errorf("failed to create new decoder: %v", err)
}
var (
blessings security.Blessings
sig security.Signature
)
if err = dec.Decode(&sig); err != nil {
return noBlessings, nil, err
}
if err = dec.Decode(&blessings); err != nil {
return noBlessings, nil, err
}
var discharges map[string]security.Discharge
if v >= version.IPCVersion7 {
var wired []security.WireDischarge
if err = dec.Decode(&wired); err != nil {
return noBlessings, nil, err
}
if len(wired) > 0 {
discharges = make(map[string]security.Discharge)
for _, w := range wired {
d := security.NewDischarge(w)
discharges[d.ID()] = d
}
}
} else if v >= version.IPCVersion5 {
var list []security.Discharge
if err = dec.Decode(&list); err != nil {
return noBlessings, nil, err
}
if len(list) > 0 {
discharges = make(map[string]security.Discharge)
for _, d := range list {
discharges[d.ID()] = d
}
}
}
if !sig.Verify(blessings.PublicKey(), append(tag, crypter.ChannelBinding()...)) {
return noBlessings, nil, errInvalidSignatureInMessage
}
return blessings, discharges, nil
}