]> Cypherpunks.ru repositories - govpn.git/blob - src/govpn/peer.go
Take into account user's MTU and encryptionless settings for CPR calculations
[govpn.git] / src / 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 int64 = 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         int64
100         BytesOut        int64
101         BytesPayloadIn  int64
102         BytesPayloadOut int64
103         FramesIn        int
104         FramesOut       int
105         FramesUnauth    int
106         FramesDup       int
107         HeartbeatRecv   int
108         HeartbeatSent   int
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 += int64(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.AddInt64(&p.BytesOut, int64(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         var out []byte
304         p.BusyR.Lock()
305         if p.Encless {
306                 var err error
307                 out, err = EnclessDecode(
308                         p.Key,
309                         data[len(data)-NonceSize:],
310                         data[:len(data)-NonceSize],
311                 )
312                 if err != nil {
313                         p.FramesUnauth++
314                         p.BusyR.Unlock()
315                         return false
316                 }
317         } else {
318                 for i := 0; i < SSize; i++ {
319                         p.bufR[i] = 0
320                 }
321                 copy(p.bufR[S20BS:], data[TagSize:])
322                 salsa20.XORKeyStream(
323                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
324                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
325                         data[len(data)-NonceSize:],
326                         p.Key,
327                 )
328                 copy(p.keyAuthR[:], p.bufR[:SSize])
329                 copy(p.tagR[:], data[:TagSize])
330                 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
331                         p.FramesUnauth++
332                         p.BusyR.Unlock()
333                         return false
334                 }
335                 out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
336         }
337
338         // Check if received nonce is known to us in either of two buckets.
339         // If yes, then this is ignored duplicate.
340         // Check from the oldest bucket, as in most cases this will result
341         // in constant time check.
342         // If Bucket0 is filled, then it becomes Bucket1.
343         p.NonceCipher.Decrypt(
344                 data[len(data)-NonceSize:],
345                 data[len(data)-NonceSize:],
346         )
347         p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
348         if reorderable {
349                 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
350                 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
351                 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
352                         p.FramesDup++
353                         p.BusyR.Unlock()
354                         return false
355                 }
356                 p.nonceBucket0[p.nonceRecv] = struct{}{}
357                 p.nonceBucketN++
358                 if p.nonceBucketN == NonceBucketSize {
359                         p.nonceBucket1 = p.nonceBucket0
360                         p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
361                         p.nonceBucketN = 0
362                 }
363         } else {
364                 if p.nonceRecv != p.NonceExpect {
365                         p.FramesDup++
366                         p.BusyR.Unlock()
367                         return false
368                 }
369                 p.NonceExpect += 2
370         }
371         if p.nonceRecv > p.nonceLatest {
372                 p.nonceLatest = p.nonceRecv
373         }
374
375         p.FramesIn++
376         atomic.AddInt64(&p.BytesIn, int64(len(data)))
377         p.LastPing = time.Now()
378         p.pktSizeR = bytes.LastIndexByte(out, PadByte)
379         if p.pktSizeR == -1 {
380                 p.BusyR.Unlock()
381                 return false
382         }
383         // Validate the pad
384         for i := p.pktSizeR + 1; i < len(out); i++ {
385                 if out[i] != 0 {
386                         p.BusyR.Unlock()
387                         return false
388                 }
389         }
390
391         if p.pktSizeR == 0 {
392                 p.HeartbeatRecv++
393                 p.BusyR.Unlock()
394                 return true
395         }
396         p.BytesPayloadIn += int64(p.pktSizeR)
397         tap.Write(out[:p.pktSizeR])
398         p.BusyR.Unlock()
399         return true
400 }