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