blob: b85a4f10e9e727731d9518e9c43e93d9a887862e [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Jiri Simsa5293dcb2014-05-10 09:56:38 -07005package vc
6
7import (
8 "bytes"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07009 "io"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070010
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070011 "v.io/v23/rpc/version"
Jiri Simsa6ac95222015-02-23 16:11:49 -080012 "v.io/v23/security"
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070013 "v.io/v23/verror"
Jiri Simsa6ac95222015-02-23 16:11:49 -080014 "v.io/v23/vom"
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070015
Suharsh Sivakumardcc11d72015-05-11 12:19:20 -070016 "v.io/x/ref/runtime/internal/lib/iobuf"
17 "v.io/x/ref/runtime/internal/rpc/stream"
18 "v.io/x/ref/runtime/internal/rpc/stream/crypto"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070019)
20
Andres Erbsenffa45742014-08-13 10:13:11 -070021var (
22 authServerContextTag = []byte("VCauthS\x00")
23 authClientContextTag = []byte("VCauthC\x00")
24)
25
Jiri Simsa5293dcb2014-05-10 09:56:38 -070026var (
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070027 // These errors are intended to be used as arguments to higher
28 // level errors and hence {1}{2} is omitted from their format
29 // strings to avoid repeating these n-times in the final error
30 // message visible to the user.
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070031 errVomEncodeBlessing = reg(".errVomEncodeRequest", "failed to encode blessing{:3}")
32 errHandshakeMessage = reg(".errHandshakeMessage", "failed to read hanshake message{:3}")
33 errInvalidSignatureInMessage = reg(".errInvalidSignatureInMessage", "signature does not verify in authentication handshake message")
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070034 errFailedToCreateSelfBlessing = reg(".errFailedToCreateSelfBlessing", "failed to create self blessing{:3}")
35 errNoBlessingsToPresentToServer = reg(".errerrNoBlessingsToPresentToServer ", "no blessings to present as a server")
Jiri Simsa5293dcb2014-05-10 09:56:38 -070036)
37
Jungho Ahn19e84b22015-05-18 13:22:27 -070038// TODO(jhahn): Add len(ChannelBinding) > 0 check in writeBlessing/readBlessings
39// to make sure that the auth protocol only works when len(ChannelBinding) > 0
40// once we deprecate RPCv10.
41
Ankuredd74ee2015-03-04 16:38:45 -080042// AuthenticateAsServer executes the authentication protocol at the server.
43// It returns the blessings shared by the client, and the discharges shared
44// by the server.
Jungho Ahn19e84b22015-05-18 13:22:27 -070045func AuthenticateAsServer(conn io.ReadWriteCloser, crypter crypto.Crypter, v version.RPCVersion, principal security.Principal, server security.Blessings, dc DischargeClient) (security.Blessings, map[string]security.Discharge, error) {
Asim Shankar2bf7b1e2015-02-27 00:45:12 -080046 if server.IsZero() {
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070047 return security.Blessings{}, nil, verror.New(stream.ErrSecurity, nil, verror.New(errNoBlessingsToPresentToServer, nil))
Asim Shankar6b0510a2014-10-01 12:05:06 -070048 }
Ankuredd74ee2015-03-04 16:38:45 -080049 var serverDischarges []security.Discharge
Ankure49a86a2014-11-11 18:52:43 -080050 if tpcavs := server.ThirdPartyCaveats(); len(tpcavs) > 0 && dc != nil {
Ankuredd74ee2015-03-04 16:38:45 -080051 serverDischarges = dc.PrepareDischarges(nil, tpcavs, security.DischargeImpetus{})
Asim Shankar6b0510a2014-10-01 12:05:06 -070052 }
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070053 if err := writeBlessings(conn, authServerContextTag, crypter, principal, server, serverDischarges, v); err != nil {
54 return security.Blessings{}, nil, err
Asim Shankar6b0510a2014-10-01 12:05:06 -070055 }
Ankuredd74ee2015-03-04 16:38:45 -080056 // Note that since the client uses a self-signed blessing to authenticate
57 // during VC setup, it does not share any discharges.
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070058 client, _, err := readBlessings(conn, authClientContextTag, crypter, v)
Ankuredd74ee2015-03-04 16:38:45 -080059 if err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070060 return security.Blessings{}, nil, err
Ankure49a86a2014-11-11 18:52:43 -080061 }
Ankuredd74ee2015-03-04 16:38:45 -080062 return client, mkDischargeMap(serverDischarges), nil
Asim Shankar6b0510a2014-10-01 12:05:06 -070063}
64
Ankuredd74ee2015-03-04 16:38:45 -080065// AuthenticateAsClient executes the authentication protocol at the client.
Jungho Ahn19e84b22015-05-18 13:22:27 -070066// It returns the blessing shared by the client, and the blessings and any
67// discharges shared by the server.
Asim Shankar6b0510a2014-10-01 12:05:06 -070068//
Ankuredd74ee2015-03-04 16:38:45 -080069// The client will only share its blessings if the server (who shares its
70// blessings first) is authorized as per the authorizer for this RPC.
Jungho Ahn19e84b22015-05-18 13:22:27 -070071func AuthenticateAsClient(conn io.ReadWriteCloser, crypter crypto.Crypter, v version.RPCVersion, params security.CallParams, auth *ServerAuthorizer) (security.Blessings, security.Blessings, map[string]security.Discharge, error) {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070072 server, serverDischarges, err := readBlessings(conn, authServerContextTag, crypter, v)
Ankuredd74ee2015-03-04 16:38:45 -080073 if err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070074 return security.Blessings{}, security.Blessings{}, nil, err
Asim Shankar6b0510a2014-10-01 12:05:06 -070075 }
Ankur50a5f392015-02-27 18:46:30 -080076 // Authorize the server based on the provided authorizer.
77 if auth != nil {
78 params.RemoteBlessings = server
79 params.RemoteDischarges = serverDischarges
Ankuredd74ee2015-03-04 16:38:45 -080080 if err := auth.Authorize(params); err != nil {
Jungho Ahn19e84b22015-05-18 13:22:27 -070081 // Note this error type should match with the one in HandshakeDialedVCPreAuthenticated().
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070082 return security.Blessings{}, security.Blessings{}, nil, verror.New(stream.ErrNotTrusted, nil, err)
Ankur50a5f392015-02-27 18:46:30 -080083 }
Asim Shankar6b0510a2014-10-01 12:05:06 -070084 }
Ankur50a5f392015-02-27 18:46:30 -080085
Ankuredd74ee2015-03-04 16:38:45 -080086 // The client shares its blessings at RPC time (as the blessings may vary
87 // across RPCs). During VC handshake, the client simply sends a self-signed
88 // blessing in order to reveal its public key to the server.
Ankur50a5f392015-02-27 18:46:30 -080089 principal := params.LocalPrincipal
Ankuredd74ee2015-03-04 16:38:45 -080090 client, err := principal.BlessSelf("vcauth")
Ankur50a5f392015-02-27 18:46:30 -080091 if err != nil {
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -070092 return security.Blessings{}, security.Blessings{}, nil, verror.New(stream.ErrSecurity, nil, verror.New(errFailedToCreateSelfBlessing, nil, err))
Asim Shankar6b0510a2014-10-01 12:05:06 -070093 }
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -070094 if err := writeBlessings(conn, authClientContextTag, crypter, principal, client, nil, v); err != nil {
95 return security.Blessings{}, security.Blessings{}, nil, err
Ankure49a86a2014-11-11 18:52:43 -080096 }
Jungho Ahn19e84b22015-05-18 13:22:27 -070097 return client, server, serverDischarges, nil
Asim Shankar6b0510a2014-10-01 12:05:06 -070098}
99
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700100func writeBlessings(w io.Writer, tag []byte, crypter crypto.Crypter, p security.Principal, b security.Blessings, discharges []security.Discharge, v version.RPCVersion) error {
Asim Shankar6b0510a2014-10-01 12:05:06 -0700101 signature, err := p.Sign(append(tag, crypter.ChannelBinding()...))
102 if err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700103 return err
Asim Shankar6b0510a2014-10-01 12:05:06 -0700104 }
105 var buf bytes.Buffer
Jungho Ahn5d1fe972015-04-27 17:51:32 -0700106 enc := vom.NewEncoder(&buf)
Asim Shankar6b0510a2014-10-01 12:05:06 -0700107 if err := enc.Encode(signature); err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700108 return verror.New(stream.ErrNetwork, nil, verror.New(errVomEncodeBlessing, nil, err))
Asim Shankar6b0510a2014-10-01 12:05:06 -0700109 }
Asim Shankarb07ec692015-02-27 23:40:44 -0800110 if err := enc.Encode(b); err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700111 return verror.New(stream.ErrNetwork, nil, verror.New(errVomEncodeBlessing, nil, err))
Asim Shankar6b0510a2014-10-01 12:05:06 -0700112 }
Matt Rosencrantz1ca47182015-04-22 17:13:28 -0700113 if err := enc.Encode(discharges); err != nil {
114 return verror.New(stream.ErrNetwork, nil, verror.New(errVomEncodeBlessing, nil, err))
Ankure49a86a2014-11-11 18:52:43 -0800115 }
Asim Shankar6b0510a2014-10-01 12:05:06 -0700116 msg, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
117 if err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700118 return err
Asim Shankar6b0510a2014-10-01 12:05:06 -0700119 }
120 defer msg.Release()
Jungho Ahn5d1fe972015-04-27 17:51:32 -0700121 enc = vom.NewEncoder(w)
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -0700122 if err := enc.Encode(msg.Contents); err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700123 return verror.New(stream.ErrNetwork, nil, verror.New(errVomEncodeBlessing, nil, err))
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -0700124 }
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700125 return nil
Asim Shankar6b0510a2014-10-01 12:05:06 -0700126}
127
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700128func readBlessings(r io.Reader, tag []byte, crypter crypto.Crypter, v version.RPCVersion) (security.Blessings, map[string]security.Discharge, error) {
Asim Shankar6b0510a2014-10-01 12:05:06 -0700129 var msg []byte
Asim Shankar2bf7b1e2015-02-27 00:45:12 -0800130 var noBlessings security.Blessings
Jungho Ahn5d1fe972015-04-27 17:51:32 -0700131 dec := vom.NewDecoder(r)
Asim Shankardae12eb2015-02-23 12:09:30 -0800132 if err := dec.Decode(&msg); err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700133 return noBlessings, nil, verror.New(stream.ErrNetwork, nil, verror.New(errHandshakeMessage, nil, err))
Asim Shankar6b0510a2014-10-01 12:05:06 -0700134 }
135 buf, err := crypter.Decrypt(iobuf.NewSlice(msg))
136 if err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700137 return noBlessings, nil, err
Asim Shankar6b0510a2014-10-01 12:05:06 -0700138 }
139 defer buf.Release()
Jungho Ahn5d1fe972015-04-27 17:51:32 -0700140 dec = vom.NewDecoder(bytes.NewReader(buf.Contents))
Ankure49a86a2014-11-11 18:52:43 -0800141 var (
Asim Shankarb07ec692015-02-27 23:40:44 -0800142 blessings security.Blessings
143 sig security.Signature
Ankure49a86a2014-11-11 18:52:43 -0800144 )
145 if err = dec.Decode(&sig); err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700146 return noBlessings, nil, verror.New(stream.ErrNetwork, nil, err)
Asim Shankar6b0510a2014-10-01 12:05:06 -0700147 }
Asim Shankarb07ec692015-02-27 23:40:44 -0800148 if err = dec.Decode(&blessings); err != nil {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700149 return noBlessings, nil, verror.New(stream.ErrNetwork, nil, err)
Asim Shankar6b0510a2014-10-01 12:05:06 -0700150 }
Ankuredd74ee2015-03-04 16:38:45 -0800151 var discharges []security.Discharge
Matt Rosencrantz1ca47182015-04-22 17:13:28 -0700152 if err := dec.Decode(&discharges); err != nil {
153 return noBlessings, nil, verror.New(stream.ErrNetwork, nil, err)
Ankure49a86a2014-11-11 18:52:43 -0800154 }
Ankure49a86a2014-11-11 18:52:43 -0800155 if !sig.Verify(blessings.PublicKey(), append(tag, crypter.ChannelBinding()...)) {
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700156 return noBlessings, nil, verror.New(stream.ErrSecurity, nil, verror.New(errInvalidSignatureInMessage, nil))
Asim Shankar6b0510a2014-10-01 12:05:06 -0700157 }
Cosmos Nicolaou185c0c62015-04-13 21:22:43 -0700158 return blessings, mkDischargeMap(discharges), nil
Ankuredd74ee2015-03-04 16:38:45 -0800159}
160
161func mkDischargeMap(discharges []security.Discharge) map[string]security.Discharge {
162 if len(discharges) == 0 {
163 return nil
164 }
165 m := make(map[string]security.Discharge, len(discharges))
166 for _, d := range discharges {
167 m[d.ID()] = d
168 }
169 return m
Asim Shankar6b0510a2014-10-01 12:05:06 -0700170}
Jungho Ahn19e84b22015-05-18 13:22:27 -0700171
172func bindClientPrincipalToChannel(crypter crypto.Crypter, p security.Principal) ([]byte, error) {
173 sig, err := p.Sign(append(authClientContextTag, crypter.ChannelBinding()...))
174 if err != nil {
175 return nil, err
176 }
177 var buf bytes.Buffer
178 enc := vom.NewEncoder(&buf)
179 if err := enc.Encode(sig); err != nil {
180 return nil, verror.New(errVomEncodeBlessing, nil, err)
181 }
182 msg, err := crypter.Encrypt(iobuf.NewSlice(buf.Bytes()))
183 if err != nil {
184 return nil, err
185 }
186 defer msg.Release()
187 signature := make([]byte, len(msg.Contents))
188 copy(signature, msg.Contents)
189 return signature, nil
190}
191
192func verifyClientPrincipalBoundToChannel(signature []byte, crypter crypto.Crypter, publicKey security.PublicKey) error {
193 msg, err := crypter.Decrypt(iobuf.NewSlice(signature))
194 if err != nil {
195 return err
196 }
197 defer msg.Release()
198 dec := vom.NewDecoder(bytes.NewReader(msg.Contents))
199 var sig security.Signature
200 if err = dec.Decode(&sig); err != nil {
201 return verror.New(errHandshakeMessage, nil, err)
202 }
203 if !sig.Verify(publicKey, append(authClientContextTag, crypter.ChannelBinding()...)) {
204 return verror.New(errInvalidSignatureInMessage, nil)
205 }
206 return nil
207}