]> Cypherpunks.ru repositories - govpn.git/blob - src/govpn/handshake.go
0aa65bd624e4e9366db2275b3acff5d6fc04ad1d
[govpn.git] / src / govpn / handshake.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package govpn
20
21 import (
22         "crypto/rand"
23         "crypto/subtle"
24         "encoding/binary"
25         "io"
26         "log"
27         "time"
28
29         "github.com/agl/ed25519"
30         "github.com/agl/ed25519/extra25519"
31         "golang.org/x/crypto/curve25519"
32         "golang.org/x/crypto/salsa20"
33         "golang.org/x/crypto/salsa20/salsa"
34         "golang.org/x/crypto/xtea"
35 )
36
37 const (
38         RSize = 8
39         SSize = 32
40 )
41
42 type Handshake struct {
43         addr     string
44         conn     io.Writer
45         LastPing time.Time
46         Conf     *PeerConf
47         dsaPubH  *[ed25519.PublicKeySize]byte
48         key      *[32]byte
49         rNonce   *[RSize]byte
50         dhPriv   *[32]byte    // own private DH key
51         rServer  *[RSize]byte // random string for authentication
52         rClient  *[RSize]byte
53         sServer  *[SSize]byte // secret string for main key calculation
54         sClient  *[SSize]byte
55 }
56
57 func keyFromSecrets(server, client []byte) *[SSize]byte {
58         k := new([SSize]byte)
59         for i := 0; i < SSize; i++ {
60                 k[i] = server[i] ^ client[i]
61         }
62         return k
63 }
64
65 // Apply HSalsa20 function for data. Used to hash public keys.
66 func HApply(data *[32]byte) {
67         salsa.HSalsa20(data, new([16]byte), data, &salsa.Sigma)
68 }
69
70 // Zero handshake's memory state
71 func (h *Handshake) Zero() {
72         if h.rNonce != nil {
73                 sliceZero(h.rNonce[:])
74         }
75         if h.dhPriv != nil {
76                 sliceZero(h.dhPriv[:])
77         }
78         if h.key != nil {
79                 sliceZero(h.key[:])
80         }
81         if h.dsaPubH != nil {
82                 sliceZero(h.dsaPubH[:])
83         }
84         if h.rServer != nil {
85                 sliceZero(h.rServer[:])
86         }
87         if h.rClient != nil {
88                 sliceZero(h.rClient[:])
89         }
90         if h.sServer != nil {
91                 sliceZero(h.sServer[:])
92         }
93         if h.sClient != nil {
94                 sliceZero(h.sClient[:])
95         }
96 }
97
98 func (h *Handshake) rNonceNext(count uint64) []byte {
99         nonce := make([]byte, RSize)
100         nonceCurrent, _ := binary.Uvarint(h.rNonce[:])
101         binary.PutUvarint(nonce, nonceCurrent+count)
102         return nonce
103 }
104
105 func randRead(b []byte) error {
106         var err error
107         if egdPath == "" {
108                 _, err = rand.Read(b)
109         } else {
110                 err = EGDRead(b)
111         }
112         return err
113 }
114
115 func dhKeypairGen() (*[32]byte, *[32]byte) {
116         priv := new([32]byte)
117         pub := new([32]byte)
118         repr := new([32]byte)
119         reprFound := false
120         for !reprFound {
121                 if err := randRead(priv[:]); err != nil {
122                         log.Fatalln("Error reading random for DH private key:", err)
123                 }
124                 reprFound = extra25519.ScalarBaseMult(pub, repr, priv)
125         }
126         return priv, repr
127 }
128
129 func dhKeyGen(priv, pub *[32]byte) *[32]byte {
130         key := new([32]byte)
131         curve25519.ScalarMult(key, priv, pub)
132         HApply(key)
133         return key
134 }
135
136 // Create new handshake state.
137 func NewHandshake(addr string, conn io.Writer, conf *PeerConf) *Handshake {
138         state := Handshake{
139                 addr:     addr,
140                 conn:     conn,
141                 LastPing: time.Now(),
142                 Conf:     conf,
143         }
144         state.dsaPubH = new([ed25519.PublicKeySize]byte)
145         copy(state.dsaPubH[:], state.Conf.DSAPub[:])
146         HApply(state.dsaPubH)
147         return &state
148 }
149
150 // Generate ID tag from client identification and data.
151 func idTag(id *PeerId, data []byte) []byte {
152         ciph, err := xtea.NewCipher(id[:])
153         if err != nil {
154                 panic(err)
155         }
156         enc := make([]byte, xtea.BlockSize)
157         ciph.Encrypt(enc, data[:xtea.BlockSize])
158         return enc
159 }
160
161 // Start handshake's procedure from the client. It is the entry point
162 // for starting the handshake procedure. // First handshake packet
163 // will be sent immediately.
164 func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake {
165         state := NewHandshake(addr, conn, conf)
166         var dhPubRepr *[32]byte
167         state.dhPriv, dhPubRepr = dhKeypairGen()
168
169         state.rNonce = new([RSize]byte)
170         if err := randRead(state.rNonce[:]); err != nil {
171                 log.Fatalln("Error reading random for nonce:", err)
172         }
173         var enc []byte
174         if conf.Noise {
175                 enc = make([]byte, MTU-xtea.BlockSize-RSize)
176         } else {
177                 enc = make([]byte, 32)
178         }
179         copy(enc, dhPubRepr[:])
180         salsa20.XORKeyStream(enc, enc, state.rNonce[:], state.dsaPubH)
181         data := append(state.rNonce[:], enc...)
182         data = append(data, idTag(state.Conf.Id, state.rNonce[:])...)
183         state.conn.Write(data)
184         return state
185 }
186
187 // Process handshake message on the server side.
188 // This function is intended to be called on server's side.
189 // If this is the final handshake message, then new Peer object
190 // will be created and used as a transport. If no mutually
191 // authenticated Peer is ready, then return nil.
192 func (h *Handshake) Server(data []byte) *Peer {
193         // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag
194         if h.rNonce == nil {
195                 // Generate DH keypair
196                 var dhPubRepr *[32]byte
197                 h.dhPriv, dhPubRepr = dhKeypairGen()
198
199                 h.rNonce = new([RSize]byte)
200                 copy(h.rNonce[:], data[:RSize])
201
202                 // Decrypt remote public key and compute shared key
203                 cDHRepr := new([32]byte)
204                 salsa20.XORKeyStream(
205                         cDHRepr[:],
206                         data[RSize:RSize+32],
207                         h.rNonce[:],
208                         h.dsaPubH,
209                 )
210                 cDH := new([32]byte)
211                 extra25519.RepresentativeToPublicKey(cDH, cDHRepr)
212                 h.key = dhKeyGen(h.dhPriv, cDH)
213
214                 encPub := make([]byte, 32)
215                 salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
216
217                 // Generate R* and encrypt them
218                 h.rServer = new([RSize]byte)
219                 if err := randRead(h.rServer[:]); err != nil {
220                         log.Fatalln("Error reading random for R:", err)
221                 }
222                 h.sServer = new([SSize]byte)
223                 if err := randRead(h.sServer[:]); err != nil {
224                         log.Fatalln("Error reading random for S:", err)
225                 }
226                 var encRs []byte
227                 if h.Conf.Noise {
228                         encRs = make([]byte, MTU-len(encPub)-xtea.BlockSize)
229                 } else {
230                         encRs = make([]byte, RSize+SSize)
231                 }
232                 copy(encRs, append(h.rServer[:], h.sServer[:]...))
233                 salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key)
234
235                 // Send that to client
236                 h.conn.Write(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...))
237                 h.LastPing = time.Now()
238         } else
239         // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag
240         if h.rClient == nil {
241                 // Decrypted Rs compare rServer
242                 dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
243                 salsa20.XORKeyStream(
244                         dec,
245                         data[:RSize+RSize+SSize+ed25519.SignatureSize],
246                         h.rNonceNext(1),
247                         h.key,
248                 )
249                 if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 {
250                         log.Println("Invalid server's random number with", h.addr)
251                         return nil
252                 }
253                 sign := new([ed25519.SignatureSize]byte)
254                 copy(sign[:], dec[RSize+RSize+SSize:])
255                 if !ed25519.Verify(h.Conf.DSAPub, h.key[:], sign) {
256                         log.Println("Invalid signature from", h.addr)
257                         return nil
258                 }
259
260                 // Send final answer to client
261                 var enc []byte
262                 if h.Conf.Noise {
263                         enc = make([]byte, MTU-xtea.BlockSize)
264                 } else {
265                         enc = make([]byte, RSize)
266                 }
267                 copy(enc, dec[RSize:RSize+RSize])
268                 salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key)
269                 h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
270
271                 // Switch peer
272                 peer := newPeer(
273                         false,
274                         h.addr,
275                         h.conn,
276                         h.Conf,
277                         keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize]))
278                 h.LastPing = time.Now()
279                 return peer
280         } else {
281                 log.Println("Invalid handshake message from", h.addr)
282         }
283         return nil
284 }
285
286 // Process handshake message on the client side.
287 // This function is intended to be called on client's side.
288 // If this is the final handshake message, then new Peer object
289 // will be created and used as a transport. If no mutually
290 // authenticated Peer is ready, then return nil.
291 func (h *Handshake) Client(data []byte) *Peer {
292         // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag
293         if h.rServer == nil && h.key == nil {
294                 // Decrypt remote public key and compute shared key
295                 sDHRepr := new([32]byte)
296                 salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
297                 sDH := new([32]byte)
298                 extra25519.RepresentativeToPublicKey(sDH, sDHRepr)
299                 h.key = dhKeyGen(h.dhPriv, sDH)
300
301                 // Decrypt Rs
302                 decRs := make([]byte, RSize+SSize)
303                 salsa20.XORKeyStream(decRs, data[SSize:32+RSize+SSize], h.rNonce[:], h.key)
304                 h.rServer = new([RSize]byte)
305                 copy(h.rServer[:], decRs[:RSize])
306                 h.sServer = new([SSize]byte)
307                 copy(h.sServer[:], decRs[RSize:])
308
309                 // Generate R* and signature and encrypt them
310                 h.rClient = new([RSize]byte)
311                 if err := randRead(h.rClient[:]); err != nil {
312                         log.Fatalln("Error reading random for R:", err)
313                 }
314                 h.sClient = new([SSize]byte)
315                 if err := randRead(h.sClient[:]); err != nil {
316                         log.Fatalln("Error reading random for S:", err)
317                 }
318                 sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:])
319
320                 var enc []byte
321                 if h.Conf.Noise {
322                         enc = make([]byte, MTU-xtea.BlockSize)
323                 } else {
324                         enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
325                 }
326                 copy(enc,
327                         append(h.rServer[:],
328                                 append(h.rClient[:],
329                                         append(h.sClient[:], sign[:]...)...)...))
330                 salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key)
331
332                 // Send that to server
333                 h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
334                 h.LastPing = time.Now()
335         } else
336         // ENC(K, R+2, RC) + IDtag
337         if h.key != nil {
338                 // Decrypt rClient
339                 dec := make([]byte, RSize)
340                 salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key)
341                 if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 {
342                         log.Println("Invalid client's random number with", h.addr)
343                         return nil
344                 }
345
346                 // Switch peer
347                 peer := newPeer(
348                         true,
349                         h.addr,
350                         h.conn,
351                         h.Conf,
352                         keyFromSecrets(h.sServer[:], h.sClient[:]),
353                 )
354                 h.LastPing = time.Now()
355                 return peer
356         } else {
357                 log.Println("Invalid handshake stage from", h.addr)
358         }
359         return nil
360 }