]> Cypherpunks.ru repositories - govpn.git/blob - handshake.go
Use A-EKE instead of EKE. Doc refactoring. Preparing for 3.0 release
[govpn.git] / 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         "log"
26         "net"
27         "time"
28
29         "github.com/agl/ed25519"
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     *net.UDPAddr
43         LastPing time.Time
44         Conf     *PeerConf
45         dsaPubH  *[ed25519.PublicKeySize]byte
46         key      *[32]byte
47         rNonce   *[RSize]byte
48         dhPriv   *[32]byte    // own private DH key
49         rServer  *[RSize]byte // random string for authentication
50         rClient  *[RSize]byte
51         sServer  *[SSize]byte // secret string for main key calculation
52         sClient  *[SSize]byte
53 }
54
55 func keyFromSecrets(server, client []byte) *[SSize]byte {
56         k := new([SSize]byte)
57         for i := 0; i < SSize; i++ {
58                 k[i] = server[i] ^ client[i]
59         }
60         return k
61 }
62
63 // Apply HSalsa20 function for data. Used to hash public keys.
64 func HApply(data *[32]byte) {
65         salsa.HSalsa20(data, new([16]byte), data, &salsa.Sigma)
66 }
67
68 // Zero handshake's memory state
69 func (h *Handshake) Zero() {
70         if h.rNonce != nil {
71                 sliceZero(h.rNonce[:])
72         }
73         if h.dhPriv != nil {
74                 sliceZero(h.dhPriv[:])
75         }
76         if h.key != nil {
77                 sliceZero(h.key[:])
78         }
79         if h.dsaPubH != nil {
80                 sliceZero(h.dsaPubH[:])
81         }
82         if h.rServer != nil {
83                 sliceZero(h.rServer[:])
84         }
85         if h.rClient != nil {
86                 sliceZero(h.rClient[:])
87         }
88         if h.sServer != nil {
89                 sliceZero(h.sServer[:])
90         }
91         if h.sClient != nil {
92                 sliceZero(h.sClient[:])
93         }
94 }
95
96 func (h *Handshake) rNonceNext(count uint64) []byte {
97         nonce := make([]byte, RSize)
98         nonceCurrent, _ := binary.Uvarint(h.rNonce[:])
99         binary.PutUvarint(nonce, nonceCurrent+count)
100         return nonce
101 }
102
103 func dhPrivGen() *[32]byte {
104         dh := new([32]byte)
105         if _, err := rand.Read(dh[:]); err != nil {
106                 panic("Can not read random for DH private key")
107         }
108         return dh
109 }
110
111 func dhKeyGen(priv, pub *[32]byte) *[32]byte {
112         key := new([32]byte)
113         curve25519.ScalarMult(key, priv, pub)
114         HApply(key)
115         return key
116 }
117
118 // Create new handshake state.
119 func HandshakeNew(addr *net.UDPAddr, conf *PeerConf) *Handshake {
120         state := Handshake{
121                 addr:     addr,
122                 LastPing: time.Now(),
123                 Conf:     conf,
124         }
125         state.dsaPubH = new([ed25519.PublicKeySize]byte)
126         copy(state.dsaPubH[:], state.Conf.DSAPub[:])
127         HApply(state.dsaPubH)
128         return &state
129 }
130
131 // Generate ID tag from client identification and data.
132 func idTag(id *PeerId, data []byte) []byte {
133         ciph, err := xtea.NewCipher(id[:])
134         if err != nil {
135                 panic(err)
136         }
137         enc := make([]byte, xtea.BlockSize)
138         ciph.Encrypt(enc, data[:xtea.BlockSize])
139         return enc
140 }
141
142 // Start handshake's procedure from the client. It is the entry point
143 // for starting the handshake procedure. You have to specify outgoing
144 // conn address, remote's addr address, our own peer configuration.
145 // First handshake packet will be sent immediately.
146 func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Handshake {
147         state := HandshakeNew(addr, conf)
148
149         state.dhPriv = dhPrivGen()
150         dhPub := new([32]byte)
151         curve25519.ScalarBaseMult(dhPub, state.dhPriv)
152
153         state.rNonce = new([RSize]byte)
154         if _, err := rand.Read(state.rNonce[:]); err != nil {
155                 panic("Can not read random for handshake nonce")
156         }
157         enc := make([]byte, 32)
158         salsa20.XORKeyStream(enc, dhPub[:], state.rNonce[:], state.dsaPubH)
159         data := append(state.rNonce[:], enc...)
160         data = append(data, idTag(state.Conf.Id, state.rNonce[:])...)
161         if _, err := conn.WriteTo(data, addr); err != nil {
162                 panic(err)
163         }
164         return state
165 }
166
167 // Process handshake message on the server side.
168 // This function is intended to be called on server's side.
169 // Our outgoing conn connection and received data are required.
170 // If this is the final handshake message, then new Peer object
171 // will be created and used as a transport. If no mutually
172 // authenticated Peer is ready, then return nil.
173 func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer {
174         // R + ENC(H(DSAPub), R, CDHPub) + IDtag
175         if len(data) == 48 && h.rNonce == nil {
176                 // Generate private DH key
177                 h.dhPriv = dhPrivGen()
178                 dhPub := new([32]byte)
179                 curve25519.ScalarBaseMult(dhPub, h.dhPriv)
180
181                 h.rNonce = new([RSize]byte)
182                 copy(h.rNonce[:], data[:RSize])
183
184                 // Decrypt remote public key and compute shared key
185                 dec := new([32]byte)
186                 salsa20.XORKeyStream(dec[:], data[RSize:RSize+32], h.rNonce[:], h.dsaPubH)
187                 h.key = dhKeyGen(h.dhPriv, dec)
188
189                 encPub := make([]byte, 32)
190                 salsa20.XORKeyStream(encPub, dhPub[:], h.rNonceNext(1), h.dsaPubH)
191
192                 // Generate R* and encrypt them
193                 h.rServer = new([RSize]byte)
194                 if _, err := rand.Read(h.rServer[:]); err != nil {
195                         panic("Can not read random for handshake random key")
196                 }
197                 h.sServer = new([SSize]byte)
198                 if _, err := rand.Read(h.sServer[:]); err != nil {
199                         panic("Can not read random for handshake shared key")
200                 }
201                 encRs := make([]byte, RSize+SSize)
202                 salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key)
203
204                 // Send that to client
205                 if _, err := conn.WriteTo(
206                         append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...), h.addr); err != nil {
207                         panic(err)
208                 }
209                 h.LastPing = time.Now()
210         } else
211         // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag
212         if len(data) == 120 && h.rClient == nil {
213                 // Decrypted Rs compare rServer
214                 dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
215                 salsa20.XORKeyStream(
216                         dec,
217                         data[:RSize+RSize+SSize+ed25519.SignatureSize],
218                         h.rNonceNext(1),
219                         h.key,
220                 )
221                 if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 {
222                         log.Println("Invalid server's random number with", h.addr)
223                         return nil
224                 }
225                 sign := new([ed25519.SignatureSize]byte)
226                 copy(sign[:], dec[RSize+RSize+SSize:])
227                 if !ed25519.Verify(h.Conf.DSAPub, h.key[:], sign) {
228                         log.Println("Invalid signature from", h.addr)
229                         return nil
230                 }
231
232                 // Send final answer to client
233                 enc := make([]byte, RSize)
234                 salsa20.XORKeyStream(enc, dec[RSize:RSize+RSize], h.rNonceNext(2), h.key)
235                 if _, err := conn.WriteTo(append(enc, idTag(h.Conf.Id, enc)...), h.addr); err != nil {
236                         panic(err)
237                 }
238
239                 // Switch peer
240                 peer := newPeer(
241                         h.addr,
242                         h.Conf,
243                         0,
244                         keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize]))
245                 h.LastPing = time.Now()
246                 return peer
247         } else {
248                 log.Println("Invalid handshake message from", h.addr)
249         }
250         return nil
251 }
252
253 // Process handshake message on the client side.
254 // This function is intended to be called on client's side.
255 // Our outgoing conn connection, authentication
256 // key and received data are required.
257 // If this is the final handshake message, then new Peer object
258 // will be created and used as a transport. If no mutually
259 // authenticated Peer is ready, then return nil.
260 func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer {
261         switch len(data) {
262         case 80: // ENC(H(DSAPub), R+1, SDHPub) + ENC(K, R, RS + SS) + IDtag
263                 if h.key != nil {
264                         log.Println("Invalid handshake stage from", h.addr)
265                         return nil
266                 }
267
268                 // Decrypt remote public key and compute shared key
269                 dec := new([32]byte)
270                 salsa20.XORKeyStream(dec[:], data[:32], h.rNonceNext(1), h.dsaPubH)
271                 h.key = dhKeyGen(h.dhPriv, dec)
272
273                 // Decrypt Rs
274                 decRs := make([]byte, RSize+SSize)
275                 salsa20.XORKeyStream(decRs, data[SSize:32+RSize+SSize], h.rNonce[:], h.key)
276                 h.rServer = new([RSize]byte)
277                 copy(h.rServer[:], decRs[:RSize])
278                 h.sServer = new([SSize]byte)
279                 copy(h.sServer[:], decRs[RSize:])
280
281                 // Generate R* and signature and encrypt them
282                 h.rClient = new([RSize]byte)
283                 if _, err := rand.Read(h.rClient[:]); err != nil {
284                         panic("Can not read random for handshake random key")
285                 }
286                 h.sClient = new([SSize]byte)
287                 if _, err := rand.Read(h.sClient[:]); err != nil {
288                         panic("Can not read random for handshake shared key")
289                 }
290                 sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:])
291
292                 enc := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize)
293                 salsa20.XORKeyStream(enc,
294                         append(h.rServer[:],
295                                 append(h.rClient[:],
296                                         append(h.sClient[:], sign[:]...)...)...), h.rNonceNext(1), h.key)
297
298                 // Send that to server
299                 if _, err := conn.WriteTo(append(enc, idTag(h.Conf.Id, enc)...), h.addr); err != nil {
300                         panic(err)
301                 }
302                 h.LastPing = time.Now()
303         case 16: // ENC(K, R+2, RC) + IDtag
304                 if h.key == nil {
305                         log.Println("Invalid handshake stage from", h.addr)
306                         return nil
307                 }
308
309                 // Decrypt rClient
310                 dec := make([]byte, RSize)
311                 salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key)
312                 if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 {
313                         log.Println("Invalid client's random number with", h.addr)
314                         return nil
315                 }
316
317                 // Switch peer
318                 peer := newPeer(h.addr, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:]))
319                 h.LastPing = time.Now()
320                 return peer
321         default:
322                 log.Println("Invalid handshake message from", h.addr)
323         }
324         return nil
325 }