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