]> Cypherpunks.ru repositories - govpn.git/blob - src/govpn/handshake.go
Make transport less dependent on UDP nature
[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 HandshakeNew(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 := HandshakeNew(addr, conn, conf)
166
167         var dhPubRepr *[32]byte
168         state.dhPriv, dhPubRepr = dhKeypairGen()
169
170         state.rNonce = new([RSize]byte)
171         if err := randRead(state.rNonce[:]); err != nil {
172                 log.Fatalln("Error reading random for nonce:", err)
173         }
174         enc := make([]byte, 32)
175         salsa20.XORKeyStream(enc, dhPubRepr[:], state.rNonce[:], state.dsaPubH)
176         data := append(state.rNonce[:], enc...)
177         data = append(data, idTag(state.Conf.Id, state.rNonce[:])...)
178         state.conn.Write(data)
179         return state
180 }
181
182 // Process handshake message on the server side.
183 // This function is intended to be called on server's side.
184 // If this is the final handshake message, then new Peer object
185 // will be created and used as a transport. If no mutually
186 // authenticated Peer is ready, then return nil.
187 func (h *Handshake) Server(data []byte) *Peer {
188         // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag
189         if len(data) == 48 && h.rNonce == nil {
190                 // Generate DH keypair
191                 var dhPubRepr *[32]byte
192                 h.dhPriv, dhPubRepr = dhKeypairGen()
193
194                 h.rNonce = new([RSize]byte)
195                 copy(h.rNonce[:], data[:RSize])
196
197                 // Decrypt remote public key and compute shared key
198                 cDHRepr := new([32]byte)
199                 salsa20.XORKeyStream(
200                         cDHRepr[:],
201                         data[RSize:RSize+32],
202                         h.rNonce[:],
203                         h.dsaPubH,
204                 )
205                 cDH := new([32]byte)
206                 extra25519.RepresentativeToPublicKey(cDH, cDHRepr)
207                 h.key = dhKeyGen(h.dhPriv, cDH)
208
209                 encPub := make([]byte, 32)
210                 salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH)
211
212                 // Generate R* and encrypt them
213                 h.rServer = new([RSize]byte)
214                 if err := randRead(h.rServer[:]); err != nil {
215                         log.Fatalln("Error reading random for R:", err)
216                 }
217                 h.sServer = new([SSize]byte)
218                 if err := randRead(h.sServer[:]); err != nil {
219                         log.Fatalln("Error reading random for S:", err)
220                 }
221                 encRs := make([]byte, RSize+SSize)
222                 salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key)
223
224                 // Send that to client
225                 h.conn.Write(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...))
226                 h.LastPing = time.Now()
227         } else
228         // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag
229         if len(data) == 120 && h.rClient == nil {
230                 // Decrypted Rs compare rServer
231                 dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
232                 salsa20.XORKeyStream(
233                         dec,
234                         data[:RSize+RSize+SSize+ed25519.SignatureSize],
235                         h.rNonceNext(1),
236                         h.key,
237                 )
238                 if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 {
239                         log.Println("Invalid server's random number with", h.addr)
240                         return nil
241                 }
242                 sign := new([ed25519.SignatureSize]byte)
243                 copy(sign[:], dec[RSize+RSize+SSize:])
244                 if !ed25519.Verify(h.Conf.DSAPub, h.key[:], sign) {
245                         log.Println("Invalid signature from", h.addr)
246                         return nil
247                 }
248
249                 // Send final answer to client
250                 enc := make([]byte, RSize)
251                 salsa20.XORKeyStream(enc, dec[RSize:RSize+RSize], h.rNonceNext(2), h.key)
252                 h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
253
254                 // Switch peer
255                 peer := newPeer(
256                         h.addr,
257                         h.conn,
258                         h.Conf,
259                         0,
260                         keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize]))
261                 h.LastPing = time.Now()
262                 return peer
263         } else {
264                 log.Println("Invalid handshake message from", h.addr)
265         }
266         return nil
267 }
268
269 // Process handshake message on the client side.
270 // This function is intended to be called on client's side.
271 // If this is the final handshake message, then new Peer object
272 // will be created and used as a transport. If no mutually
273 // authenticated Peer is ready, then return nil.
274 func (h *Handshake) Client(data []byte) *Peer {
275         switch len(data) {
276         case 80: // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag
277                 if h.key != nil {
278                         log.Println("Invalid handshake stage from", h.addr)
279                         return nil
280                 }
281
282                 // Decrypt remote public key and compute shared key
283                 sDHRepr := new([32]byte)
284                 salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH)
285                 sDH := new([32]byte)
286                 extra25519.RepresentativeToPublicKey(sDH, sDHRepr)
287                 h.key = dhKeyGen(h.dhPriv, sDH)
288
289                 // Decrypt Rs
290                 decRs := make([]byte, RSize+SSize)
291                 salsa20.XORKeyStream(decRs, data[SSize:32+RSize+SSize], h.rNonce[:], h.key)
292                 h.rServer = new([RSize]byte)
293                 copy(h.rServer[:], decRs[:RSize])
294                 h.sServer = new([SSize]byte)
295                 copy(h.sServer[:], decRs[RSize:])
296
297                 // Generate R* and signature and encrypt them
298                 h.rClient = new([RSize]byte)
299                 if err := randRead(h.rClient[:]); err != nil {
300                         log.Fatalln("Error reading random for R:", err)
301                 }
302                 h.sClient = new([SSize]byte)
303                 if err := randRead(h.sClient[:]); err != nil {
304                         log.Fatalln("Error reading random for S:", err)
305                 }
306                 sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:])
307
308                 enc := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
309                 salsa20.XORKeyStream(enc,
310                         append(h.rServer[:],
311                                 append(h.rClient[:],
312                                         append(h.sClient[:], sign[:]...)...)...), h.rNonceNext(1), h.key)
313
314                 // Send that to server
315                 h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...))
316                 h.LastPing = time.Now()
317         case 16: // ENC(K, R+2, RC) + IDtag
318                 if h.key == nil {
319                         log.Println("Invalid handshake stage from", h.addr)
320                         return nil
321                 }
322
323                 // Decrypt rClient
324                 dec := make([]byte, RSize)
325                 salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key)
326                 if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 {
327                         log.Println("Invalid client's random number with", h.addr)
328                         return nil
329                 }
330
331                 // Switch peer
332                 peer := newPeer(h.addr, h.conn, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:]))
333                 h.LastPing = time.Now()
334                 return peer
335         default:
336                 log.Println("Invalid handshake message from", h.addr)
337         }
338         return nil
339 }