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