| 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/core/veyron2/context" |
| "v.io/core/veyron2/ipc/version" |
| "v.io/core/veyron2/security" |
| "v.io/core/veyron2/vom2" |
| ) |
| |
| 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, clientDischarges map[string]security.Discharge, err error) { |
| defer conn.Close() |
| if server == nil { |
| return nil, nil, 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, clientDischarges, 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 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(ctx *context.T, conn io.ReadWriteCloser, principal security.Principal, dc DischargeClient, crypter crypto.Crypter, 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 |
| } |
| serverB := server.ForContext(security.NewContext(&security.ContextParams{ |
| LocalPrincipal: principal, |
| RemoteBlessings: server, |
| RemoteDischarges: serverDischarges, |
| // 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, nil, fmt.Errorf("no blessing tagged for peer %v in the BlessingStore", serverB) |
| } |
| var discharges []security.Discharge |
| if dc != nil { |
| discharges = dc.PrepareDischarges(ctx, client.ThirdPartyCaveats(), security.DischargeImpetus{}) |
| } |
| if err = writeBlessings(conn, authClientContextTag, crypter, principal, client, discharges, 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 := vom2.NewBinaryEncoder(&buf) |
| if err != nil { |
| return err |
| } |
| if err := enc.Encode(signature); err != nil { |
| return err |
| } |
| if err := enc.Encode(security.MarshalBlessings(b)); err != nil { |
| return err |
| } |
| 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 = vom2.NewBinaryEncoder(w) |
| if err != nil { |
| return err |
| } |
| return enc.Encode(msg.Contents) |
| } |
| |
| func readBlessings(r io.Reader, tag []byte, crypter crypto.Crypter, v version.IPCVersion) (blessings security.Blessings, discharges map[string]security.Discharge, err error) { |
| var msg []byte |
| dec, err := vom2.NewDecoder(r) |
| if err != nil { |
| return nil, nil, fmt.Errorf("failed to create new decoder: %v", err) |
| } |
| if err = dec.Decode(&msg); err != nil { |
| return nil, nil, fmt.Errorf("failed to read handshake message: %v", err) |
| } |
| buf, err := crypter.Decrypt(iobuf.NewSlice(msg)) |
| if err != nil { |
| return |
| } |
| defer buf.Release() |
| dec, err = vom2.NewDecoder(bytes.NewReader(buf.Contents)) |
| if err != nil { |
| return nil, nil, fmt.Errorf("failed to create new decoder: %v", err) |
| } |
| |
| var ( |
| wireb security.WireBlessings |
| sig security.Signature |
| ) |
| if err = dec.Decode(&sig); err != nil { |
| return |
| } |
| if err = dec.Decode(&wireb); err != nil { |
| return |
| } |
| var dischargeSet []security.Discharge |
| if v >= version.IPCVersion5 { |
| if err = dec.Decode(&dischargeSet); err != nil { |
| return |
| } |
| } |
| if len(dischargeSet) > 0 { |
| discharges = make(map[string]security.Discharge) |
| for _, d := range dischargeSet { |
| discharges[d.ID()] = d |
| } |
| } |
| blessings, err = security.NewBlessings(wireb) |
| if err != nil { |
| return |
| } |
| if blessings == nil { |
| return nil, nil, errNoCertificatesReceived |
| } |
| if !sig.Verify(blessings.PublicKey(), append(tag, crypter.ChannelBinding()...)) { |
| return nil, nil, errInvalidSignatureInMessage |
| } |
| return |
| } |