blob: 312c3a6bbf25ab39b8bb3178f391fdcf236a6c5c [file] [log] [blame]
Jason Hickey96d30e82014-11-13 07:40:00 -08001package vif
2
3import (
4 "crypto/rand"
5 "errors"
6 "fmt"
7 "io"
Jason Hickey96d30e82014-11-13 07:40:00 -08008
Cosmos Nicolaoud412cb22014-12-15 22:06:32 -08009 "golang.org/x/crypto/nacl/box"
Jason Hickey96d30e82014-11-13 07:40:00 -080010
Jiri Simsa764efb72014-12-25 20:57:03 -080011 "v.io/core/veyron/runtimes/google/ipc/stream/crypto"
12 "v.io/core/veyron/runtimes/google/ipc/stream/message"
13 "v.io/core/veyron/runtimes/google/ipc/stream/vc"
14 "v.io/core/veyron/runtimes/google/ipc/version"
15 "v.io/core/veyron/runtimes/google/lib/iobuf"
16 "v.io/core/veyron2/context"
17 "v.io/core/veyron2/ipc/stream"
18 ipcversion "v.io/core/veyron2/ipc/version"
19 "v.io/core/veyron2/options"
20 "v.io/core/veyron2/security"
Jason Hickey96d30e82014-11-13 07:40:00 -080021)
22
23var (
24 errUnsupportedEncryptVersion = errors.New("unsupported encryption version")
25 errVersionNegotiationFailed = errors.New("encryption version negotiation failed")
26 nullCipher crypto.NullControlCipher
27)
28
29// privateData includes secret data we need for encryption.
30type privateData struct {
31 naclBoxPrivateKey [32]byte
32}
33
34// AuthenticateAsClient sends a HopSetup message if possible. If so, it chooses
35// encryption based on the max supported version.
36//
37// The sequence is initiated by the client.
38//
39// - If the versions include IPCVersion6 or greater, the client sends a
40// HopSetup message to the server, containing the client's supported
41// versions, and the client's crypto options. The HopSetup message
42// is sent in the clear.
43//
44// - When the server receives the HopSetup message, it calls
45// AuthenticateAsServer, which constructs a response HopSetup containing
46// the server's version range, and any crypto options.
47//
48// - For IPCVersion6, the client and server generate fresh public/private key
49// pairs, sending the public key to the peer as a crypto option. The
50// remainder of the communication is encrypted as HopSetupStream messages
51// using NewControlCipherIPC6, which is based on
Matt Rosencrantz7c88fb12014-11-18 01:12:45 +000052// code.google.com/p/go.crypto/nacl/box.
Jason Hickey96d30e82014-11-13 07:40:00 -080053//
54// - Once the encrypted HopSetupStream channel is setup, the client and
55// server authenticate using the vc.AuthenticateAs{Client,Server} protocol.
56//
57// Note that the HopSetup messages are sent in the clear, so they are subject to
58// modification by a man-in-the-middle, which can currently force a downgrade by
59// modifying the acceptable version ranges downward. This can be addressed by
60// including a hash of the HopSetup message in the encrypted stream. It is
61// likely that this will be addressed in subsequent protocol versions (or it may
62// not be addressed at all if IPCVersion6 becomes the only supported version).
Jungho Ahn4b9a5192015-02-02 13:11:08 -080063func AuthenticateAsClient(ctx *context.T, writer io.Writer, reader *iobuf.Reader, versions *version.Range, principal security.Principal, dc vc.DischargeClient) (crypto.ControlCipher, error) {
Jason Hickey96d30e82014-11-13 07:40:00 -080064 if versions == nil {
65 versions = version.SupportedRange
66 }
67 if principal == nil {
68 // If there is no principal, we do not support encryption/authentication.
69 var err error
70 versions, err = versions.Intersect(&version.Range{Min: 0, Max: ipcversion.IPCVersion5})
71 if err != nil {
72 return nil, err
73 }
74 }
75 if versions.Max < ipcversion.IPCVersion6 {
76 return nullCipher, nil
77 }
78
79 // The client has not yet sent its public data. Construct it and send it.
80 pvt, pub, err := makeHopSetup(versions)
81 if err != nil {
82 return nil, err
83 }
Jungho Ahn4b9a5192015-02-02 13:11:08 -080084 if err := message.WriteTo(writer, &pub, nullCipher); err != nil {
Jason Hickey96d30e82014-11-13 07:40:00 -080085 return nil, err
86 }
87
88 // Read the server's public data.
Jason Hickey96d30e82014-11-13 07:40:00 -080089 pmsg, err := message.ReadFrom(reader, nullCipher)
90 if err != nil {
91 return nil, err
92 }
93 ppub, ok := pmsg.(*message.HopSetup)
94 if !ok {
95 return nil, errVersionNegotiationFailed
96 }
97
98 // Choose the max version in the intersection.
99 vrange, err := pub.Versions.Intersect(&ppub.Versions)
100 if err != nil {
101 return nil, err
102 }
103 v := vrange.Max
104 if v < ipcversion.IPCVersion6 {
105 return nullCipher, nil
106 }
107
108 // Perform the authentication.
109 switch v {
110 case ipcversion.IPCVersion6:
Jungho Ahn4b9a5192015-02-02 13:11:08 -0800111 return authenticateAsClientIPC6(ctx, writer, reader, principal, dc, &pvt, &pub, ppub)
Jason Hickey96d30e82014-11-13 07:40:00 -0800112 default:
113 return nil, errUnsupportedEncryptVersion
114 }
115}
116
Matt Rosencrantz4f8ac602014-12-29 14:42:48 -0800117func authenticateAsClientIPC6(ctx *context.T, writer io.Writer, reader *iobuf.Reader, principal security.Principal, dc vc.DischargeClient,
Jason Hickey96d30e82014-11-13 07:40:00 -0800118 pvt *privateData, pub, ppub *message.HopSetup) (crypto.ControlCipher, error) {
119 pbox := ppub.NaclBox()
120 if pbox == nil {
121 return nil, errVersionNegotiationFailed
122 }
123 c := crypto.NewControlCipherIPC6(&pbox.PublicKey, &pvt.naclBoxPrivateKey, false)
124 sconn := newSetupConn(writer, reader, c)
125 // TODO(jyh): act upon the authentication results.
Asim Shankarf4864f42014-11-25 18:53:05 -0800126 _, _, _, err := vc.AuthenticateAsClient(ctx, sconn, principal, dc, crypto.NewNullCrypter(), ipcversion.IPCVersion6)
Jason Hickey96d30e82014-11-13 07:40:00 -0800127 if err != nil {
128 return nil, err
129 }
130 return c, nil
131}
132
133// AuthenticateAsServer handles a HopSetup message, choosing authentication
134// based on the max common version.
135//
136// See AuthenticateAsClient for a description of the negotiation.
Jungho Ahn4b9a5192015-02-02 13:11:08 -0800137func AuthenticateAsServer(writer io.Writer, reader *iobuf.Reader, versions *version.Range, principal security.Principal, lBlessings security.Blessings,
Jason Hickey96d30e82014-11-13 07:40:00 -0800138 dc vc.DischargeClient, ppub *message.HopSetup) (crypto.ControlCipher, error) {
139 var err error
140 if versions == nil {
141 versions = version.SupportedRange
142 }
143 if principal == nil {
144 // If there is no principal, we don't support encryption/authentication.
145 versions, err = versions.Intersect(&version.Range{Min: 0, Max: ipcversion.IPCVersion5})
146 if err != nil {
147 return nil, err
148 }
149 }
150
151 // Create our public data and send it to the client.
152 pvt, pub, err := makeHopSetup(versions)
153 if err != nil {
154 return nil, err
155 }
Jungho Ahn4b9a5192015-02-02 13:11:08 -0800156 if err := message.WriteTo(writer, &pub, nullCipher); err != nil {
Jason Hickey96d30e82014-11-13 07:40:00 -0800157 return nil, err
158 }
159
160 // Choose the max version in common.
161 vrange, err := pub.Versions.Intersect(&ppub.Versions)
162 if err != nil {
163 return nil, err
164 }
165 v := vrange.Max
166 if v < ipcversion.IPCVersion6 {
167 return nullCipher, nil
168 }
169
170 // Perform authentication.
171 switch v {
172 case ipcversion.IPCVersion6:
Jungho Ahn4b9a5192015-02-02 13:11:08 -0800173 return authenticateAsServerIPC6(writer, reader, principal, lBlessings, dc, &pvt, &pub, ppub)
Jason Hickey96d30e82014-11-13 07:40:00 -0800174 default:
175 return nil, errUnsupportedEncryptVersion
176 }
177}
178
Jungho Ahn4b9a5192015-02-02 13:11:08 -0800179func authenticateAsServerIPC6(writer io.Writer, reader *iobuf.Reader, principal security.Principal, lBlessings security.Blessings, dc vc.DischargeClient,
Jason Hickey96d30e82014-11-13 07:40:00 -0800180 pvt *privateData, pub, ppub *message.HopSetup) (crypto.ControlCipher, error) {
181 box := ppub.NaclBox()
182 if box == nil {
183 return nil, errVersionNegotiationFailed
184 }
185 c := crypto.NewControlCipherIPC6(&box.PublicKey, &pvt.naclBoxPrivateKey, true)
Jungho Ahn4b9a5192015-02-02 13:11:08 -0800186 sconn := newSetupConn(writer, reader, c)
Jason Hickey96d30e82014-11-13 07:40:00 -0800187 // TODO(jyh): act upon authentication results.
188 _, _, err := vc.AuthenticateAsServer(sconn, principal, lBlessings, dc, crypto.NewNullCrypter(), ipcversion.IPCVersion6)
189 if err != nil {
190 return nil, err
191 }
192 return c, nil
193}
194
195// serverAuthOptions extracts the Principal from the options list.
196func serverAuthOptions(lopts []stream.ListenerOpt) (principal security.Principal, lBlessings security.Blessings, dischargeClient vc.DischargeClient, err error) {
197 var securityLevel options.VCSecurityLevel
198 for _, o := range lopts {
199 switch v := o.(type) {
200 case vc.DischargeClient:
201 dischargeClient = v
202 case vc.LocalPrincipal:
203 principal = v.Principal
204 case options.VCSecurityLevel:
205 securityLevel = v
206 case options.ServerBlessings:
207 lBlessings = v.Blessings
208 }
209 }
210 switch securityLevel {
211 case options.VCSecurityConfidential:
212 if principal == nil {
213 principal = vc.AnonymousPrincipal
214 }
215 if lBlessings == nil {
216 lBlessings = principal.BlessingStore().Default()
217 }
218 case options.VCSecurityNone:
219 principal = nil
220 default:
221 err = fmt.Errorf("unrecognized VC security level: %v", securityLevel)
222 }
223 return
224}
225
226// clientAuthOptions extracts the client authentication options from the options
227// list.
Matt Rosencrantz4f8ac602014-12-29 14:42:48 -0800228func clientAuthOptions(lopts []stream.VCOpt) (ctx *context.T, principal security.Principal, dischargeClient vc.DischargeClient, err error) {
Jason Hickey96d30e82014-11-13 07:40:00 -0800229 var securityLevel options.VCSecurityLevel
Suharsh Sivakumar11316872014-11-25 15:57:00 -0800230 var noDischarges bool
Jason Hickey96d30e82014-11-13 07:40:00 -0800231 for _, o := range lopts {
232 switch v := o.(type) {
Asim Shankarf4864f42014-11-25 18:53:05 -0800233 case vc.DialContext:
234 ctx = v.T
Jason Hickey96d30e82014-11-13 07:40:00 -0800235 case vc.DischargeClient:
236 dischargeClient = v
237 case vc.LocalPrincipal:
238 principal = v.Principal
239 case options.VCSecurityLevel:
240 securityLevel = v
Suharsh Sivakumar11316872014-11-25 15:57:00 -0800241 case vc.NoDischarges:
242 noDischarges = true
Jason Hickey96d30e82014-11-13 07:40:00 -0800243 }
244 }
Suharsh Sivakumar11316872014-11-25 15:57:00 -0800245 if noDischarges {
246 dischargeClient = nil
247 }
Jason Hickey96d30e82014-11-13 07:40:00 -0800248 switch securityLevel {
249 case options.VCSecurityConfidential:
250 if principal == nil {
251 principal = vc.AnonymousPrincipal
252 }
253 case options.VCSecurityNone:
254 principal = nil
255 default:
256 err = fmt.Errorf("unrecognized VC security level: %v", securityLevel)
257 }
258 return
259}
260
261// makeHopSetup constructs the options that this process can support.
262func makeHopSetup(versions *version.Range) (pvt privateData, pub message.HopSetup, err error) {
263 pub.Versions = *versions
264 var pubKey, pvtKey *[32]byte
265 pubKey, pvtKey, err = box.GenerateKey(rand.Reader)
266 pub.Options = append(pub.Options, &message.NaclBox{PublicKey: *pubKey})
267 pvt.naclBoxPrivateKey = *pvtKey
268 return
269}