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