2 GoVPN -- simple secure free software 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/>.
28 "golang.org/x/crypto/poly1305"
29 "golang.org/x/crypto/salsa20"
30 "golang.org/x/crypto/xtea"
36 TagSize = poly1305.TagSize
37 // S20BS is Salsa20's internal blocksize in bytes
39 // Maximal amount of bytes transfered with single key (4 GiB)
40 MaxBytesPerKey int64 = 1 << 32
41 // Size of packet's size mark in bytes
43 // Heartbeat rate, relative to Timeout
45 // Minimal valid packet length
46 MinPktLength = 2 + 16 + 8
49 func newNonceCipher(key *[32]byte) *xtea.Cipher {
50 nonceKey := make([]byte, 16)
54 make([]byte, xtea.BlockSize),
57 ciph, err := xtea.NewCipher(nonceKey)
72 CPRCycle time.Duration `json:"-"`
74 // Cryptography related
75 Key *[SSize]byte `json:"-"`
76 NonceCipher *xtea.Cipher `json:"-"`
80 NonceExpect uint64 `json:"-"`
81 nonceBucket0 map[uint64]struct{}
82 nonceBucket1 map[uint64]struct{}
88 Timeout time.Duration `json:"-"`
92 willSentCycle time.Time
107 BusyR sync.Mutex `json:"-"`
110 keyAuthR *[SSize]byte
114 BusyT sync.Mutex `json:"-"`
117 keyAuthT *[SSize]byte
122 func (p *Peer) String() string {
123 return p.Id.String() + ":" + p.Addr
126 // Zero peer's memory state.
127 func (p *Peer) Zero() {
133 sliceZero(p.keyAuthR[:])
134 sliceZero(p.keyAuthT[:])
139 func (p *Peer) NonceExpectation(buf []byte) {
140 binary.BigEndian.PutUint64(buf, p.NonceExpect)
141 p.NonceCipher.Encrypt(buf, buf)
144 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
146 timeout := conf.Timeout
148 cprCycle := cprCycleCalculate(conf.CPR)
149 noiseEnable := conf.Noise
154 timeout = timeout / TimeoutHeartbeat
162 NoiseEnable: noiseEnable,
167 NonceCipher: newNonceCipher(key),
168 nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
169 nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
175 bufR: make([]byte, S20BS+MTU+NonceSize),
176 bufT: make([]byte, S20BS+MTU+NonceSize),
177 tagR: new([TagSize]byte),
178 tagT: new([TagSize]byte),
179 keyAuthR: new([SSize]byte),
180 keyAuthT: new([SSize]byte),
184 peer.NonceExpect = 0 + 2
187 peer.NonceExpect = 1 + 2
193 // Process incoming Ethernet packet.
194 // ready channel is TAPListen's synchronization channel used to tell him
195 // that he is free to receive new packets. Encrypted and authenticated
196 // packets will be sent to remote Peer side immediately.
197 func (p *Peer) EthProcess(data []byte) {
201 // Zero size is a heartbeat packet
203 // If this heartbeat is necessary
204 if !p.LastSent.Add(p.Timeout).Before(p.now) {
208 p.bufT[S20BS+0] = byte(0)
209 p.bufT[S20BS+1] = byte(0)
212 // Copy payload to our internal buffer and we are ready to
213 // accept the next one
214 binary.BigEndian.PutUint16(
215 p.bufT[S20BS:S20BS+PktSizeSize],
218 copy(p.bufT[S20BS+PktSizeSize:], data)
219 p.BytesPayloadOut += int64(len(data))
223 p.frameT = p.bufT[S20BS : S20BS+MTU-TagSize]
225 p.frameT = p.bufT[S20BS : S20BS+PktSizeSize+len(data)+NonceSize]
228 binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
229 p.NonceCipher.Encrypt(
230 p.frameT[len(p.frameT)-NonceSize:],
231 p.frameT[len(p.frameT)-NonceSize:],
233 for i := 0; i < SSize; i++ {
236 salsa20.XORKeyStream(
237 p.bufT[:S20BS+len(p.frameT)-NonceSize],
238 p.bufT[:S20BS+len(p.frameT)-NonceSize],
239 p.frameT[len(p.frameT)-NonceSize:],
243 copy(p.keyAuthT[:], p.bufT[:SSize])
244 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
246 atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize))
249 if p.CPRCycle != time.Duration(0) {
250 p.willSentCycle = p.LastSent.Add(p.CPRCycle)
251 if p.willSentCycle.After(p.now) {
252 time.Sleep(p.willSentCycle.Sub(p.now))
253 p.now = p.willSentCycle
258 p.Conn.Write(append(p.tagT[:], p.frameT...))
262 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
264 for i := 0; i < SSize; i++ {
267 copy(p.bufR[S20BS:], data[TagSize:])
268 salsa20.XORKeyStream(
269 p.bufR[:S20BS+len(data)-TagSize-NonceSize],
270 p.bufR[:S20BS+len(data)-TagSize-NonceSize],
271 data[len(data)-NonceSize:],
275 copy(p.keyAuthR[:], p.bufR[:SSize])
276 copy(p.tagR[:], data[:TagSize])
277 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
283 // Check if received nonce is known to us in either of two buckets.
284 // If yes, then this is ignored duplicate.
285 // Check from the oldest bucket, as in most cases this will result
286 // in constant time check.
287 // If Bucket0 is filled, then it becomes Bucket1.
288 p.NonceCipher.Decrypt(
289 data[len(data)-NonceSize:],
290 data[len(data)-NonceSize:],
292 p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
294 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
295 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
296 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
301 p.nonceBucket0[p.nonceRecv] = struct{}{}
303 if p.nonceBucketN == NonceBucketSize {
304 p.nonceBucket1 = p.nonceBucket0
305 p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
309 if p.nonceRecv != p.NonceExpect {
316 if p.nonceRecv > p.nonceLatest {
317 p.nonceLatest = p.nonceRecv
321 atomic.AddInt64(&p.BytesIn, int64(len(data)))
322 p.LastPing = time.Now()
323 p.pktSizeR = binary.BigEndian.Uint16(p.bufR[S20BS : S20BS+PktSizeSize])
330 p.BytesPayloadIn += int64(p.pktSizeR)
331 tap.Write(p.bufR[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSizeR])