]> Cypherpunks.ru repositories - govpn.git/blob - handshake.go
Official repositories moved to another URL
[govpn.git] / handshake.go
1 /*
2 govpn -- high-performance secure virtual private network daemon
3 Copyright (C) 2014 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 main
20
21 import (
22         "crypto/rand"
23         "crypto/subtle"
24         "encoding/binary"
25         "fmt"
26         "net"
27         "time"
28
29         "golang.org/x/crypto/curve25519"
30         "golang.org/x/crypto/poly1305"
31         "golang.org/x/crypto/salsa20"
32         "golang.org/x/crypto/salsa20/salsa"
33 )
34
35 type Handshake struct {
36         addr     *net.UDPAddr
37         lastPing time.Time
38         rNonce   *[8]byte
39         dhPriv   *[32]byte // own private DH key
40         key      *[32]byte // handshake encryption key
41         rServer  *[8]byte  // random string for authentication
42         rClient  *[8]byte
43         sServer  *[32]byte // secret string for main key calculation
44         sClient  *[32]byte
45 }
46
47 func KeyFromSecrets(server, client []byte) *[32]byte {
48         k := new([32]byte)
49         for i := 0; i < 32; i++ {
50                 k[i] = server[i] ^ client[i]
51         }
52         return k
53 }
54
55 // Check if it is valid handshake-related message
56 // Minimal size and last 16 zero bytes
57 func isValidHandshakePkt(pkt []byte) bool {
58         if len(pkt) < 24 {
59                 return false
60         }
61         for i := len(pkt) - poly1305.TagSize; i < len(pkt); i++ {
62                 if pkt[i] != '\x00' {
63                         return false
64                 }
65         }
66         return true
67 }
68
69 func (h *Handshake) rNonceNext() []byte {
70         nonce := make([]byte, 8)
71         nonceCurrent, _ := binary.Uvarint(h.rNonce[:])
72         binary.PutUvarint(nonce, nonceCurrent+1)
73         return nonce
74 }
75
76 func dhPrivGen() *[32]byte {
77         dh := new([32]byte)
78         if _, err := rand.Read(dh[:]); err != nil {
79                 panic("Can not read random for DH private key")
80         }
81         return dh
82 }
83
84 func dhKeyGen(priv, pub *[32]byte) *[32]byte {
85         key := new([32]byte)
86         curve25519.ScalarMult(key, priv, pub)
87         salsa.HSalsa20(key, new([16]byte), key, &salsa.Sigma)
88         return key
89 }
90
91 func HandshakeStart(conn *net.UDPConn, addr *net.UDPAddr, key *[32]byte) *Handshake {
92         state := Handshake{}
93         state.addr = addr
94         state.lastPing = time.Now()
95
96         state.dhPriv = dhPrivGen()
97         dhPub := new([32]byte)
98         curve25519.ScalarBaseMult(dhPub, state.dhPriv)
99
100         state.rNonce = new([8]byte)
101         if _, err := rand.Read(state.rNonce[:]); err != nil {
102                 panic("Can not read random for handshake nonce")
103         }
104         enc := make([]byte, 32)
105         salsa20.XORKeyStream(enc, dhPub[:], state.rNonce[:], key)
106
107         if _, err := conn.WriteTo(
108                 append(state.rNonce[:],
109                         append(enc, make([]byte, poly1305.TagSize)...)...), addr); err != nil {
110                 panic(err)
111         }
112         return &state
113 }
114
115 func (h *Handshake) Server(conn *net.UDPConn, key *[32]byte, data []byte) *Peer {
116         switch len(data) {
117         case 56: // R + ENC(PSK, dh_client_pub) + NULLs
118                 fmt.Print("[HS1]")
119                 if h.rNonce != nil {
120                         fmt.Print("[S?]")
121                         return nil
122                 }
123
124                 // Generate private DH key
125                 h.dhPriv = dhPrivGen()
126                 dhPub := new([32]byte)
127                 curve25519.ScalarBaseMult(dhPub, h.dhPriv)
128
129                 // Decrypt remote public key and compute shared key
130                 dec := new([32]byte)
131                 salsa20.XORKeyStream(dec[:], data[8:8+32], data[:8], key)
132                 h.key = dhKeyGen(h.dhPriv, dec)
133
134                 // Compute nonce and encrypt our public key
135                 h.rNonce = new([8]byte)
136                 copy(h.rNonce[:], data[:8])
137
138                 encPub := make([]byte, 32)
139                 salsa20.XORKeyStream(encPub, dhPub[:], h.rNonceNext(), key)
140
141                 // Generate R* and encrypt them
142                 h.rServer = new([8]byte)
143                 if _, err := rand.Read(h.rServer[:]); err != nil {
144                         panic("Can not read random for handshake random key")
145                 }
146                 h.sServer = new([32]byte)
147                 if _, err := rand.Read(h.sServer[:]); err != nil {
148                         panic("Can not read random for handshake shared key")
149                 }
150                 encRs := make([]byte, 8+32)
151                 salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key)
152
153                 // Send that to client
154                 if _, err := conn.WriteTo(
155                         append(encPub,
156                                 append(encRs, make([]byte, poly1305.TagSize)...)...), h.addr); err != nil {
157                         panic(err)
158                 }
159                 fmt.Print("[OK]")
160         case 64: // ENC(K, RS + RC + SC) + NULLs
161                 fmt.Print("[HS3]")
162                 if (h.rNonce == nil) || (h.rClient != nil) {
163                         fmt.Print("[S?]")
164                         return nil
165                 }
166
167                 // Decrypt Rs compare rServer
168                 decRs := make([]byte, 8+8+32)
169                 salsa20.XORKeyStream(decRs, data[:8+8+32], h.rNonceNext(), h.key)
170                 if res := subtle.ConstantTimeCompare(decRs[:8], h.rServer[:]); res != 1 {
171                         fmt.Print("[rS?]")
172                         return nil
173                 }
174
175                 // Send final answer to client
176                 enc := make([]byte, 8)
177                 salsa20.XORKeyStream(enc, decRs[8:8+8], make([]byte, 8), h.key)
178                 if _, err := conn.WriteTo(append(enc, make([]byte, poly1305.TagSize)...), h.addr); err != nil {
179                         panic(err)
180                 }
181
182                 // Switch peer
183                 peer := Peer{addr: h.addr, nonceOur: 0, nonceRecv: 0}
184                 peer.key = KeyFromSecrets(h.sServer[:], decRs[8+8:])
185                 fmt.Print("[OK]")
186                 return &peer
187         default:
188                 fmt.Print("[HS?]")
189         }
190         return nil
191 }
192
193 func (h *Handshake) Client(conn *net.UDPConn, key *[32]byte, data []byte) *Peer {
194         switch len(data) {
195         case 88: // ENC(PSK, dh_server_pub) + ENC(K, RS + SS) + NULLs
196                 fmt.Print("[HS2]")
197                 if h.key != nil {
198                         fmt.Print("[S?]")
199                         return nil
200                 }
201
202                 // Decrypt remote public key and compute shared key
203                 dec := new([32]byte)
204                 salsa20.XORKeyStream(dec[:], data[:32], h.rNonceNext(), key)
205                 h.key = dhKeyGen(h.dhPriv, dec)
206
207                 // Decrypt Rs
208                 decRs := make([]byte, 8+32)
209                 salsa20.XORKeyStream(decRs, data[32:32+8+32], h.rNonce[:], h.key)
210                 h.rServer = new([8]byte)
211                 copy(h.rServer[:], decRs[:8])
212                 h.sServer = new([32]byte)
213                 copy(h.sServer[:], decRs[8:])
214
215                 // Generate R* and encrypt them
216                 h.rClient = new([8]byte)
217                 if _, err := rand.Read(h.rClient[:]); err != nil {
218                         panic("Can not read random for handshake random key")
219                 }
220                 h.sClient = new([32]byte)
221                 if _, err := rand.Read(h.sClient[:]); err != nil {
222                         panic("Can not read random for handshake shared key")
223                 }
224                 encRs := make([]byte, 8+8+32)
225                 salsa20.XORKeyStream(encRs,
226                         append(h.rServer[:],
227                                 append(h.rClient[:], h.sClient[:]...)...), h.rNonceNext(), h.key)
228
229                 // Send that to server
230                 if _, err := conn.WriteTo(append(encRs, make([]byte, poly1305.TagSize)...), h.addr); err != nil {
231                         panic(err)
232                 }
233                 fmt.Print("[OK]")
234         case 24: // ENC(K, RC) + NULLs
235                 fmt.Print("[HS4]")
236                 if h.key == nil {
237                         fmt.Print("[S?]")
238                         return nil
239                 }
240
241                 // Decrypt rClient
242                 dec := make([]byte, 8)
243                 salsa20.XORKeyStream(dec, data[:8], make([]byte, 8), h.key)
244                 if res := subtle.ConstantTimeCompare(dec, h.rClient[:]); res != 1 {
245                         fmt.Print("[rC?]")
246                         return nil
247                 }
248
249                 // Switch peer
250                 peer := Peer{addr: h.addr, nonceOur: 1, nonceRecv: 0}
251                 peer.key = KeyFromSecrets(h.sServer[:], h.sClient[:])
252                 fmt.Print("[OK]")
253                 return &peer
254         default:
255                 fmt.Print("[HS?]")
256         }
257         return nil
258 }