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