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