]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/peer.go
Fix an other misusage of .Sum()
[govpn.git] / src / cypherpunks.ru / govpn / peer.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2017 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         "crypto/subtle"
24         "encoding/binary"
25         "io"
26         "log"
27         "sync"
28         "sync/atomic"
29         "time"
30
31         "chacha20"
32         "golang.org/x/crypto/blake2b"
33         "golang.org/x/crypto/poly1305"
34 )
35
36 const (
37         NonceSize       = 8
38         NonceBucketSize = 256
39         TagSize         = poly1305.TagSize
40         // S20BS is ChaCha20's internal blocksize in bytes
41         S20BS = 64
42         // Maximal amount of bytes transfered with single key (4 GiB)
43         MaxBytesPerKey uint64 = 1 << 32
44         // Heartbeat rate, relative to Timeout
45         TimeoutHeartbeat = 4
46         // Minimal valid packet length
47         MinPktLength = 1 + 16 + 8
48         // Padding byte
49         PadByte = byte(0x80)
50 )
51
52 func newNonces(key *[32]byte, i uint64) chan *[NonceSize]byte {
53         macKey := make([]byte, 32)
54         chacha20.XORKeyStream(macKey, make([]byte, 32), new([16]byte), key)
55         mac, err := blake2b.New256(macKey)
56         if err != nil {
57                 panic(err)
58         }
59         sum := make([]byte, mac.Size())
60         nonces := make(chan *[NonceSize]byte, NonceBucketSize*3)
61         go func() {
62                 for {
63                         buf := new([NonceSize]byte)
64                         binary.BigEndian.PutUint64(buf[:], i)
65                         mac.Write(buf[:])
66                         sum = mac.Sum(nil)
67                         for index := 0; index < NonceSize; index++ {
68                                 buf[index] = sum[index]
69                         }
70                         nonces <- buf
71                         mac.Reset()
72                         i += 2
73                 }
74         }()
75         return nonces
76 }
77
78 type Peer struct {
79         // Statistics (they are at the beginning for correct int64 alignment)
80         BytesIn         uint64
81         BytesOut        uint64
82         BytesPayloadIn  uint64
83         BytesPayloadOut uint64
84         FramesIn        uint64
85         FramesOut       uint64
86         FramesUnauth    uint64
87         FramesDup       uint64
88         HeartbeatRecv   uint64
89         HeartbeatSent   uint64
90
91         // Basic
92         Addr string
93         Id   *PeerId
94         Conn io.Writer `json:"-"`
95
96         // Traffic behaviour
97         NoiseEnable bool
98         CPR         int
99         CPRCycle    time.Duration `json:"-"`
100         Encless     bool
101         MTU         int
102
103         key *[SSize]byte `json:"-"`
104
105         // Timers
106         Timeout     time.Duration `json:"-"`
107         Established time.Time
108         LastPing    time.Time
109
110         // Receiver
111         BusyR    sync.Mutex `json:"-"`
112         bufR     []byte
113         tagR     *[TagSize]byte
114         keyAuthR *[SSize]byte
115         nonceR   *[16]byte
116         pktSizeR int
117
118         // UDP-related
119         noncesR      chan *[NonceSize]byte
120         nonceRecv    [NonceSize]byte
121         nonceBucketL map[[NonceSize]byte]struct{}
122         nonceBucketM map[[NonceSize]byte]struct{}
123         nonceBucketH map[[NonceSize]byte]struct{}
124
125         // TCP-related
126         NonceExpect  []byte `json:"-"`
127         noncesExpect chan *[NonceSize]byte
128
129         // Transmitter
130         BusyT    sync.Mutex `json:"-"`
131         bufT     []byte
132         tagT     *[TagSize]byte
133         keyAuthT *[SSize]byte
134         nonceT   *[16]byte
135         frameT   []byte
136         noncesT  chan *[NonceSize]byte
137 }
138
139 func (p *Peer) String() string {
140         return p.Id.String() + ":" + p.Addr
141 }
142
143 // Zero peer's memory state.
144 func (p *Peer) Zero() {
145         p.BusyT.Lock()
146         p.BusyR.Lock()
147         SliceZero(p.key[:])
148         SliceZero(p.bufR)
149         SliceZero(p.bufT)
150         SliceZero(p.keyAuthR[:])
151         SliceZero(p.keyAuthT[:])
152         p.BusyT.Unlock()
153         p.BusyR.Unlock()
154 }
155
156 func cprCycleCalculate(conf *PeerConf) time.Duration {
157         if conf.CPR == 0 {
158                 return time.Duration(0)
159         }
160         rate := conf.CPR * 1 << 10
161         if conf.Encless {
162                 rate /= EnclessEnlargeSize + conf.MTU
163         } else {
164                 rate /= conf.MTU
165         }
166         return time.Second / time.Duration(rate)
167 }
168
169 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
170         now := time.Now()
171         timeout := conf.Timeout
172
173         cprCycle := cprCycleCalculate(conf)
174         noiseEnable := conf.Noise
175         if conf.CPR > 0 {
176                 noiseEnable = true
177                 timeout = cprCycle
178         } else {
179                 timeout = timeout / TimeoutHeartbeat
180         }
181
182         bufSize := S20BS + 2*conf.MTU
183         if conf.Encless {
184                 bufSize += EnclessEnlargeSize
185                 noiseEnable = true
186         }
187
188         peer := Peer{
189                 Addr: addr,
190                 Id:   conf.Id,
191                 Conn: conn,
192
193                 NoiseEnable: noiseEnable,
194                 CPR:         conf.CPR,
195                 CPRCycle:    cprCycle,
196                 Encless:     conf.Encless,
197                 MTU:         conf.MTU,
198
199                 key: key,
200
201                 Timeout:     timeout,
202                 Established: now,
203                 LastPing:    now,
204
205                 bufR:     make([]byte, bufSize),
206                 bufT:     make([]byte, bufSize),
207                 tagR:     new([TagSize]byte),
208                 tagT:     new([TagSize]byte),
209                 keyAuthR: new([SSize]byte),
210                 nonceR:   new([16]byte),
211                 keyAuthT: new([SSize]byte),
212                 nonceT:   new([16]byte),
213         }
214
215         if isClient {
216                 peer.noncesT = newNonces(peer.key, 1+2)
217                 peer.noncesR = newNonces(peer.key, 0+2)
218                 peer.noncesExpect = newNonces(peer.key, 0+2)
219         } else {
220                 peer.noncesT = newNonces(peer.key, 0+2)
221                 peer.noncesR = newNonces(peer.key, 1+2)
222                 peer.noncesExpect = newNonces(peer.key, 1+2)
223         }
224
225         peer.NonceExpect = make([]byte, NonceSize)
226         nonce := <-peer.noncesExpect
227         copy(peer.NonceExpect, nonce[:])
228
229         var i int
230         peer.nonceBucketL = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
231         for i = 0; i < NonceBucketSize; i++ {
232                 nonce = <-peer.noncesR
233                 peer.nonceBucketL[*nonce] = struct{}{}
234         }
235         peer.nonceBucketM = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
236         for i = 0; i < NonceBucketSize; i++ {
237                 nonce = <-peer.noncesR
238                 peer.nonceBucketM[*nonce] = struct{}{}
239         }
240         peer.nonceBucketH = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
241         for i = 0; i < NonceBucketSize; i++ {
242                 nonce = <-peer.noncesR
243                 peer.nonceBucketH[*nonce] = struct{}{}
244         }
245
246         return &peer
247 }
248
249 // Process incoming Ethernet packet.
250 // ready channel is TAPListen's synchronization channel used to tell him
251 // that he is free to receive new packets. Encrypted and authenticated
252 // packets will be sent to remote Peer side immediately.
253 func (p *Peer) EthProcess(data []byte) {
254         if len(data) > p.MTU-1 { // 1 is for padding byte
255                 log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
256                 return
257         }
258         p.BusyT.Lock()
259
260         // Zero size is a heartbeat packet
261         SliceZero(p.bufT)
262         if len(data) == 0 {
263                 p.bufT[S20BS+0] = PadByte
264                 p.HeartbeatSent++
265         } else {
266                 // Copy payload to our internal buffer and we are ready to
267                 // accept the next one
268                 copy(p.bufT[S20BS:], data)
269                 p.bufT[S20BS+len(data)] = PadByte
270                 p.BytesPayloadOut += uint64(len(data))
271         }
272
273         if p.NoiseEnable && !p.Encless {
274                 p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
275         } else if p.Encless {
276                 p.frameT = p.bufT[S20BS : S20BS+p.MTU]
277         } else {
278                 p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
279         }
280         copy(p.frameT[len(p.frameT)-NonceSize:], (<-p.noncesT)[:])
281         var out []byte
282         copy(p.nonceT[8:], p.frameT[len(p.frameT)-NonceSize:])
283         if p.Encless {
284                 var err error
285                 out, err = EnclessEncode(p.key, p.nonceT, p.frameT[:len(p.frameT)-NonceSize])
286                 if err != nil {
287                         panic(err)
288                 }
289                 out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
290         } else {
291                 chacha20.XORKeyStream(
292                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
293                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
294                         p.nonceT,
295                         p.key,
296                 )
297                 copy(p.keyAuthT[:], p.bufT[:SSize])
298                 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
299                 atomic.AddUint64(&p.BytesOut, uint64(len(p.frameT)+TagSize))
300                 out = append(p.tagT[:], p.frameT...)
301         }
302         p.FramesOut++
303         p.Conn.Write(out)
304         p.BusyT.Unlock()
305 }
306
307 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
308         if len(data) < MinPktLength {
309                 return false
310         }
311         if !p.Encless && len(data) > len(p.bufR)-S20BS {
312                 return false
313         }
314         var out []byte
315         p.BusyR.Lock()
316         copy(p.nonceR[8:], data[len(data)-NonceSize:])
317         if p.Encless {
318                 var err error
319                 out, err = EnclessDecode(p.key, p.nonceR, data[:len(data)-NonceSize])
320                 if err != nil {
321                         p.FramesUnauth++
322                         p.BusyR.Unlock()
323                         return false
324                 }
325         } else {
326                 for i := 0; i < SSize; i++ {
327                         p.bufR[i] = 0
328                 }
329                 copy(p.bufR[S20BS:], data[TagSize:])
330                 chacha20.XORKeyStream(
331                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
332                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
333                         p.nonceR,
334                         p.key,
335                 )
336                 copy(p.keyAuthR[:], p.bufR[:SSize])
337                 copy(p.tagR[:], data[:TagSize])
338                 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
339                         p.FramesUnauth++
340                         p.BusyR.Unlock()
341                         return false
342                 }
343                 out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
344         }
345
346         if reorderable {
347                 copy(p.nonceRecv[:], data[len(data)-NonceSize:])
348                 _, foundL := p.nonceBucketL[p.nonceRecv]
349                 _, foundM := p.nonceBucketM[p.nonceRecv]
350                 _, foundH := p.nonceBucketH[p.nonceRecv]
351                 // If found is none of buckets: either it is too old,
352                 // or too new (many packets were lost)
353                 if !(foundL || foundM || foundH) {
354                         p.FramesDup++
355                         p.BusyR.Unlock()
356                         return false
357                 }
358                 // Delete seen nonce
359                 if foundL {
360                         delete(p.nonceBucketL, p.nonceRecv)
361                 }
362                 if foundM {
363                         delete(p.nonceBucketM, p.nonceRecv)
364                 }
365                 if foundH {
366                         delete(p.nonceBucketH, p.nonceRecv)
367                 }
368                 // If we are dealing with the latest bucket, create the new one
369                 if foundH {
370                         p.nonceBucketL, p.nonceBucketM = p.nonceBucketM, p.nonceBucketH
371                         p.nonceBucketH = make(map[[NonceSize]byte]struct{})
372                         var nonce *[NonceSize]byte
373                         for i := 0; i < NonceBucketSize; i++ {
374                                 nonce = <-p.noncesR
375                                 p.nonceBucketH[*nonce] = struct{}{}
376                         }
377                 }
378         } else {
379                 if subtle.ConstantTimeCompare(data[len(data)-NonceSize:], p.NonceExpect) != 1 {
380                         p.FramesDup++
381                         p.BusyR.Unlock()
382                         return false
383                 }
384                 copy(p.NonceExpect, (<-p.noncesExpect)[:])
385         }
386
387         p.FramesIn++
388         atomic.AddUint64(&p.BytesIn, uint64(len(data)))
389         p.LastPing = time.Now()
390         p.pktSizeR = bytes.LastIndexByte(out, PadByte)
391         if p.pktSizeR == -1 {
392                 p.BusyR.Unlock()
393                 return false
394         }
395         // Validate the pad
396         for i := p.pktSizeR + 1; i < len(out); i++ {
397                 if out[i] != 0 {
398                         p.BusyR.Unlock()
399                         return false
400                 }
401         }
402
403         if p.pktSizeR == 0 {
404                 p.HeartbeatRecv++
405                 p.BusyR.Unlock()
406                 return true
407         }
408         p.BytesPayloadIn += uint64(p.pktSizeR)
409         tap.Write(out[:p.pktSizeR])
410         p.BusyR.Unlock()
411         return true
412 }
413
414 func PeerTapProcessor(peer *Peer, tap *TAP, terminator chan struct{}) {
415         var data []byte
416         var now time.Time
417         lastSent := time.Now()
418         heartbeat := time.NewTicker(peer.Timeout)
419         if peer.CPRCycle == time.Duration(0) {
420         RawProcessor:
421                 for {
422                         select {
423                         case <-terminator:
424                                 break RawProcessor
425                         case <-heartbeat.C:
426                                 now = time.Now()
427                                 if lastSent.Add(peer.Timeout).Before(now) {
428                                         peer.EthProcess(nil)
429                                         lastSent = now
430                                 }
431                         case data = <-tap.Sink:
432                                 peer.EthProcess(data)
433                                 lastSent = time.Now()
434                         }
435                 }
436         } else {
437         CPRProcessor:
438                 for {
439                         data = nil
440                         select {
441                         case <-terminator:
442                                 break CPRProcessor
443                         case data = <-tap.Sink:
444                                 peer.EthProcess(data)
445                         default:
446                         }
447                         if data == nil {
448                                 peer.EthProcess(nil)
449                         }
450                         time.Sleep(peer.CPRCycle)
451                 }
452         }
453         close(terminator)
454         peer.Zero()
455         heartbeat.Stop()
456 }