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