]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/peer.go
Merge branch 'develop'
[govpn.git] / src / cypherpunks.ru / govpn / peer.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2016 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         "bytes"
23         "encoding/binary"
24         "io"
25         "log"
26         "sync"
27         "sync/atomic"
28         "time"
29
30         "golang.org/x/crypto/poly1305"
31         "golang.org/x/crypto/salsa20"
32         "golang.org/x/crypto/xtea"
33 )
34
35 const (
36         NonceSize       = 8
37         NonceBucketSize = 128
38         TagSize         = poly1305.TagSize
39         // S20BS is Salsa20's internal blocksize in bytes
40         S20BS = 64
41         // Maximal amount of bytes transfered with single key (4 GiB)
42         MaxBytesPerKey uint64 = 1 << 32
43         // Heartbeat rate, relative to Timeout
44         TimeoutHeartbeat = 4
45         // Minimal valid packet length
46         MinPktLength = 1 + 16 + 8
47         // Padding byte
48         PadByte = byte(0x80)
49 )
50
51 func newNonceCipher(key *[32]byte) *xtea.Cipher {
52         nonceKey := make([]byte, 16)
53         salsa20.XORKeyStream(
54                 nonceKey,
55                 make([]byte, 32),
56                 make([]byte, xtea.BlockSize),
57                 key,
58         )
59         ciph, err := xtea.NewCipher(nonceKey)
60         if err != nil {
61                 panic(err)
62         }
63         return ciph
64 }
65
66 type Peer struct {
67         // Statistics (they are at the beginning for correct int64 alignment)
68         BytesIn         uint64
69         BytesOut        uint64
70         BytesPayloadIn  uint64
71         BytesPayloadOut uint64
72         FramesIn        uint64
73         FramesOut       uint64
74         FramesUnauth    uint64
75         FramesDup       uint64
76         HeartbeatRecv   uint64
77         HeartbeatSent   uint64
78
79         // Basic
80         Addr string
81         Id   *PeerId
82         Conn io.Writer
83
84         // Traffic behaviour
85         NoiseEnable bool
86         CPR         int
87         CPRCycle    time.Duration `json:"-"`
88         Encless     bool
89         MTU         int
90
91         // Cryptography related
92         Key          *[SSize]byte `json:"-"`
93         NonceCipher  *xtea.Cipher `json:"-"`
94         nonceRecv    uint64
95         nonceLatest  uint64
96         nonceOur     uint64
97         NonceExpect  uint64 `json:"-"`
98         nonceBucket0 map[uint64]struct{}
99         nonceBucket1 map[uint64]struct{}
100         nonceFound0  bool
101         nonceFound1  bool
102         nonceBucketN int32
103
104         // Timers
105         Timeout       time.Duration `json:"-"`
106         Established   time.Time
107         LastPing      time.Time
108         LastSent      time.Time
109         willSentCycle time.Time
110
111         // Receiver
112         BusyR    sync.Mutex `json:"-"`
113         bufR     []byte
114         tagR     *[TagSize]byte
115         keyAuthR *[SSize]byte
116         pktSizeR int
117
118         // Transmitter
119         BusyT    sync.Mutex `json:"-"`
120         bufT     []byte
121         tagT     *[TagSize]byte
122         keyAuthT *[SSize]byte
123         frameT   []byte
124         now      time.Time
125 }
126
127 func (p *Peer) String() string {
128         return p.Id.String() + ":" + p.Addr
129 }
130
131 // Zero peer's memory state.
132 func (p *Peer) Zero() {
133         p.BusyT.Lock()
134         p.BusyR.Lock()
135         SliceZero(p.Key[:])
136         SliceZero(p.bufR)
137         SliceZero(p.bufT)
138         SliceZero(p.keyAuthR[:])
139         SliceZero(p.keyAuthT[:])
140         p.BusyT.Unlock()
141         p.BusyR.Unlock()
142 }
143
144 func (p *Peer) NonceExpectation(buf []byte) {
145         binary.BigEndian.PutUint64(buf, p.NonceExpect)
146         p.NonceCipher.Encrypt(buf, buf)
147 }
148
149 func cprCycleCalculate(conf *PeerConf) time.Duration {
150         if conf.CPR == 0 {
151                 return time.Duration(0)
152         }
153         rate := conf.CPR * 1 << 10
154         if conf.Encless {
155                 rate /= EnclessEnlargeSize + conf.MTU
156         } else {
157                 rate /= conf.MTU
158         }
159         return time.Second / time.Duration(rate)
160 }
161
162 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
163         now := time.Now()
164         timeout := conf.Timeout
165
166         cprCycle := cprCycleCalculate(conf)
167         noiseEnable := conf.Noise
168         if conf.CPR > 0 {
169                 noiseEnable = true
170                 timeout = cprCycle
171         } else {
172                 timeout = timeout / TimeoutHeartbeat
173         }
174
175         bufSize := S20BS + 2*conf.MTU
176         if conf.Encless {
177                 bufSize += EnclessEnlargeSize
178                 noiseEnable = true
179         }
180         peer := Peer{
181                 Addr: addr,
182                 Id:   conf.Id,
183                 Conn: conn,
184
185                 NoiseEnable: noiseEnable,
186                 CPR:         conf.CPR,
187                 CPRCycle:    cprCycle,
188                 Encless:     conf.Encless,
189                 MTU:         conf.MTU,
190
191                 Key:          key,
192                 NonceCipher:  newNonceCipher(key),
193                 nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
194                 nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
195
196                 Timeout:     timeout,
197                 Established: now,
198                 LastPing:    now,
199
200                 bufR:     make([]byte, bufSize),
201                 bufT:     make([]byte, bufSize),
202                 tagR:     new([TagSize]byte),
203                 tagT:     new([TagSize]byte),
204                 keyAuthR: new([SSize]byte),
205                 keyAuthT: new([SSize]byte),
206         }
207         if isClient {
208                 peer.nonceOur = 1
209                 peer.NonceExpect = 0 + 2
210         } else {
211                 peer.nonceOur = 0
212                 peer.NonceExpect = 1 + 2
213         }
214         return &peer
215
216 }
217
218 // Process incoming Ethernet packet.
219 // ready channel is TAPListen's synchronization channel used to tell him
220 // that he is free to receive new packets. Encrypted and authenticated
221 // packets will be sent to remote Peer side immediately.
222 func (p *Peer) EthProcess(data []byte) {
223         if len(data) > p.MTU-1 { // 1 is for padding byte
224                 log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
225                 return
226         }
227         p.now = time.Now()
228         p.BusyT.Lock()
229
230         // Zero size is a heartbeat packet
231         SliceZero(p.bufT)
232         if len(data) == 0 {
233                 // If this heartbeat is necessary
234                 if !p.LastSent.Add(p.Timeout).Before(p.now) {
235                         p.BusyT.Unlock()
236                         return
237                 }
238                 p.bufT[S20BS+0] = PadByte
239                 p.HeartbeatSent++
240         } else {
241                 // Copy payload to our internal buffer and we are ready to
242                 // accept the next one
243                 copy(p.bufT[S20BS:], data)
244                 p.bufT[S20BS+len(data)] = PadByte
245                 p.BytesPayloadOut += uint64(len(data))
246         }
247
248         if p.NoiseEnable && !p.Encless {
249                 p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
250         } else if p.Encless {
251                 p.frameT = p.bufT[S20BS : S20BS+p.MTU]
252         } else {
253                 p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
254         }
255         p.nonceOur += 2
256         binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
257         p.NonceCipher.Encrypt(
258                 p.frameT[len(p.frameT)-NonceSize:],
259                 p.frameT[len(p.frameT)-NonceSize:],
260         )
261         var out []byte
262         if p.Encless {
263                 var err error
264                 out, err = EnclessEncode(
265                         p.Key,
266                         p.frameT[len(p.frameT)-NonceSize:],
267                         p.frameT[:len(p.frameT)-NonceSize],
268                 )
269                 if err != nil {
270                         panic(err)
271                 }
272                 out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
273         } else {
274                 salsa20.XORKeyStream(
275                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
276                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
277                         p.frameT[len(p.frameT)-NonceSize:],
278                         p.Key,
279                 )
280                 copy(p.keyAuthT[:], p.bufT[:SSize])
281                 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
282                 atomic.AddUint64(&p.BytesOut, uint64(len(p.frameT)+TagSize))
283                 out = append(p.tagT[:], p.frameT...)
284         }
285         p.FramesOut++
286
287         if p.CPRCycle != time.Duration(0) {
288                 p.willSentCycle = p.LastSent.Add(p.CPRCycle)
289                 if p.willSentCycle.After(p.now) {
290                         time.Sleep(p.willSentCycle.Sub(p.now))
291                         p.now = p.willSentCycle
292                 }
293         }
294
295         p.LastSent = p.now
296         p.Conn.Write(out)
297         p.BusyT.Unlock()
298 }
299
300 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
301         if len(data) < MinPktLength {
302                 return false
303         }
304         if !p.Encless && len(data) > len(p.bufR)-S20BS {
305                 return false
306         }
307         var out []byte
308         p.BusyR.Lock()
309         if p.Encless {
310                 var err error
311                 out, err = EnclessDecode(
312                         p.Key,
313                         data[len(data)-NonceSize:],
314                         data[:len(data)-NonceSize],
315                 )
316                 if err != nil {
317                         p.FramesUnauth++
318                         p.BusyR.Unlock()
319                         return false
320                 }
321         } else {
322                 for i := 0; i < SSize; i++ {
323                         p.bufR[i] = 0
324                 }
325                 copy(p.bufR[S20BS:], data[TagSize:])
326                 salsa20.XORKeyStream(
327                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
328                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
329                         data[len(data)-NonceSize:],
330                         p.Key,
331                 )
332                 copy(p.keyAuthR[:], p.bufR[:SSize])
333                 copy(p.tagR[:], data[:TagSize])
334                 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
335                         p.FramesUnauth++
336                         p.BusyR.Unlock()
337                         return false
338                 }
339                 out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
340         }
341
342         // Check if received nonce is known to us in either of two buckets.
343         // If yes, then this is ignored duplicate.
344         // Check from the oldest bucket, as in most cases this will result
345         // in constant time check.
346         // If Bucket0 is filled, then it becomes Bucket1.
347         p.NonceCipher.Decrypt(
348                 data[len(data)-NonceSize:],
349                 data[len(data)-NonceSize:],
350         )
351         p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
352         if reorderable {
353                 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
354                 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
355                 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
356                         p.FramesDup++
357                         p.BusyR.Unlock()
358                         return false
359                 }
360                 p.nonceBucket0[p.nonceRecv] = struct{}{}
361                 p.nonceBucketN++
362                 if p.nonceBucketN == NonceBucketSize {
363                         p.nonceBucket1 = p.nonceBucket0
364                         p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
365                         p.nonceBucketN = 0
366                 }
367         } else {
368                 if p.nonceRecv != p.NonceExpect {
369                         p.FramesDup++
370                         p.BusyR.Unlock()
371                         return false
372                 }
373                 p.NonceExpect += 2
374         }
375         if p.nonceRecv > p.nonceLatest {
376                 p.nonceLatest = p.nonceRecv
377         }
378
379         p.FramesIn++
380         atomic.AddUint64(&p.BytesIn, uint64(len(data)))
381         p.LastPing = time.Now()
382         p.pktSizeR = bytes.LastIndexByte(out, PadByte)
383         if p.pktSizeR == -1 {
384                 p.BusyR.Unlock()
385                 return false
386         }
387         // Validate the pad
388         for i := p.pktSizeR + 1; i < len(out); i++ {
389                 if out[i] != 0 {
390                         p.BusyR.Unlock()
391                         return false
392                 }
393         }
394
395         if p.pktSizeR == 0 {
396                 p.HeartbeatRecv++
397                 p.BusyR.Unlock()
398                 return true
399         }
400         p.BytesPayloadIn += uint64(p.pktSizeR)
401         tap.Write(out[:p.pktSizeR])
402         p.BusyR.Unlock()
403         return true
404 }