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