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