2 govpn -- Simple secure virtual private network daemon
3 Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
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 "golang.org/x/crypto/xtea"
36 type Handshake struct {
40 dhPriv *[32]byte // own private DH key
41 key *[32]byte // handshake encryption key
42 rServer *[8]byte // random string for authentication
44 sServer *[32]byte // secret string for main key calculation
48 func KeyFromSecrets(server, client []byte) *[32]byte {
50 for i := 0; i < 32; i++ {
51 k[i] = server[i] ^ client[i]
56 func NewNonceCipher(key *[32]byte) *xtea.Cipher {
57 nonceKey := make([]byte, 16)
58 salsa20.XORKeyStream(nonceKey, make([]byte, 32), make([]byte, 8), key)
59 ciph, err := xtea.NewCipher(nonceKey)
66 // Check if it is valid handshake-related message
67 // Minimal size and last 16 zero bytes
68 func isValidHandshakePkt(pkt []byte) bool {
72 for i := len(pkt) - poly1305.TagSize; i < len(pkt); i++ {
80 func (h *Handshake) rNonceNext() []byte {
81 nonce := make([]byte, 8)
82 nonceCurrent, _ := binary.Uvarint(h.rNonce[:])
83 binary.PutUvarint(nonce, nonceCurrent+1)
87 func dhPrivGen() *[32]byte {
89 if _, err := rand.Read(dh[:]); err != nil {
90 panic("Can not read random for DH private key")
95 func dhKeyGen(priv, pub *[32]byte) *[32]byte {
97 curve25519.ScalarMult(key, priv, pub)
98 salsa.HSalsa20(key, new([16]byte), key, &salsa.Sigma)
102 func HandshakeStart(conn *net.UDPConn, addr *net.UDPAddr, key *[32]byte) *Handshake {
105 state.lastPing = time.Now()
107 state.dhPriv = dhPrivGen()
108 dhPub := new([32]byte)
109 curve25519.ScalarBaseMult(dhPub, state.dhPriv)
111 state.rNonce = new([8]byte)
112 if _, err := rand.Read(state.rNonce[:]); err != nil {
113 panic("Can not read random for handshake nonce")
115 enc := make([]byte, 32)
116 salsa20.XORKeyStream(enc, dhPub[:], state.rNonce[:], key)
118 if _, err := conn.WriteTo(
119 append(state.rNonce[:],
120 append(enc, make([]byte, poly1305.TagSize)...)...), addr); err != nil {
126 func (h *Handshake) Server(noncediff uint64, conn *net.UDPConn, key *[32]byte, data []byte) *Peer {
128 case 56: // R + ENC(PSK, dh_client_pub) + NULLs
135 // Generate private DH key
136 h.dhPriv = dhPrivGen()
137 dhPub := new([32]byte)
138 curve25519.ScalarBaseMult(dhPub, h.dhPriv)
140 // Decrypt remote public key and compute shared key
142 salsa20.XORKeyStream(dec[:], data[8:8+32], data[:8], key)
143 h.key = dhKeyGen(h.dhPriv, dec)
145 // Compute nonce and encrypt our public key
146 h.rNonce = new([8]byte)
147 copy(h.rNonce[:], data[:8])
149 encPub := make([]byte, 32)
150 salsa20.XORKeyStream(encPub, dhPub[:], h.rNonceNext(), key)
152 // Generate R* and encrypt them
153 h.rServer = new([8]byte)
154 if _, err := rand.Read(h.rServer[:]); err != nil {
155 panic("Can not read random for handshake random key")
157 h.sServer = new([32]byte)
158 if _, err := rand.Read(h.sServer[:]); err != nil {
159 panic("Can not read random for handshake shared key")
161 encRs := make([]byte, 8+32)
162 salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key)
164 // Send that to client
165 if _, err := conn.WriteTo(
167 append(encRs, make([]byte, poly1305.TagSize)...)...), h.addr); err != nil {
171 case 64: // ENC(K, RS + RC + SC) + NULLs
173 if (h.rNonce == nil) || (h.rClient != nil) {
178 // Decrypt Rs compare rServer
179 decRs := make([]byte, 8+8+32)
180 salsa20.XORKeyStream(decRs, data[:8+8+32], h.rNonceNext(), h.key)
181 if res := subtle.ConstantTimeCompare(decRs[:8], h.rServer[:]); res != 1 {
186 // Send final answer to client
187 enc := make([]byte, 8)
188 salsa20.XORKeyStream(enc, decRs[8:8+8], make([]byte, 8), h.key)
189 if _, err := conn.WriteTo(append(enc, make([]byte, poly1305.TagSize)...), h.addr); err != nil {
196 nonceOur: noncediff + 0,
197 nonceRecv: noncediff + 0,
198 key: KeyFromSecrets(h.sServer[:], decRs[8+8:]),
200 peer.nonceCipher = NewNonceCipher(peer.key)
209 func (h *Handshake) Client(noncediff uint64, conn *net.UDPConn, key *[32]byte, data []byte) *Peer {
211 case 88: // ENC(PSK, dh_server_pub) + ENC(K, RS + SS) + NULLs
218 // Decrypt remote public key and compute shared key
220 salsa20.XORKeyStream(dec[:], data[:32], h.rNonceNext(), key)
221 h.key = dhKeyGen(h.dhPriv, dec)
224 decRs := make([]byte, 8+32)
225 salsa20.XORKeyStream(decRs, data[32:32+8+32], h.rNonce[:], h.key)
226 h.rServer = new([8]byte)
227 copy(h.rServer[:], decRs[:8])
228 h.sServer = new([32]byte)
229 copy(h.sServer[:], decRs[8:])
231 // Generate R* and encrypt them
232 h.rClient = new([8]byte)
233 if _, err := rand.Read(h.rClient[:]); err != nil {
234 panic("Can not read random for handshake random key")
236 h.sClient = new([32]byte)
237 if _, err := rand.Read(h.sClient[:]); err != nil {
238 panic("Can not read random for handshake shared key")
240 encRs := make([]byte, 8+8+32)
241 salsa20.XORKeyStream(encRs,
243 append(h.rClient[:], h.sClient[:]...)...), h.rNonceNext(), h.key)
245 // Send that to server
246 if _, err := conn.WriteTo(append(encRs, make([]byte, poly1305.TagSize)...), h.addr); err != nil {
250 case 24: // ENC(K, RC) + NULLs
258 dec := make([]byte, 8)
259 salsa20.XORKeyStream(dec, data[:8], make([]byte, 8), h.key)
260 if res := subtle.ConstantTimeCompare(dec, h.rClient[:]); res != 1 {
268 nonceOur: noncediff + 1,
269 nonceRecv: noncediff + 0,
270 key: KeyFromSecrets(h.sServer[:], h.sClient[:]),
272 peer.nonceCipher = NewNonceCipher(peer.key)