]> Cypherpunks.ru repositories - govpn.git/blob - src/govpn/peer.go
Add missing copyright docstrings
[govpn.git] / src / govpn / peer.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2015 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         p.BusyR.Lock()
264         for i := 0; i < SSize; i++ {
265                 p.bufR[i] = byte(0)
266         }
267         copy(p.bufR[S20BS:], data[TagSize:])
268         salsa20.XORKeyStream(
269                 p.bufR[:S20BS+len(data)-TagSize-NonceSize],
270                 p.bufR[:S20BS+len(data)-TagSize-NonceSize],
271                 data[len(data)-NonceSize:],
272                 p.Key,
273         )
274
275         copy(p.keyAuthR[:], p.bufR[:SSize])
276         copy(p.tagR[:], data[:TagSize])
277         if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
278                 p.FramesUnauth++
279                 p.BusyR.Unlock()
280                 return false
281         }
282
283         // Check if received nonce is known to us in either of two buckets.
284         // If yes, then this is ignored duplicate.
285         // Check from the oldest bucket, as in most cases this will result
286         // in constant time check.
287         // If Bucket0 is filled, then it becomes Bucket1.
288         p.NonceCipher.Decrypt(
289                 data[len(data)-NonceSize:],
290                 data[len(data)-NonceSize:],
291         )
292         p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
293         if reorderable {
294                 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
295                 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
296                 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
297                         p.FramesDup++
298                         p.BusyR.Unlock()
299                         return false
300                 }
301                 p.nonceBucket0[p.nonceRecv] = struct{}{}
302                 p.nonceBucketN++
303                 if p.nonceBucketN == NonceBucketSize {
304                         p.nonceBucket1 = p.nonceBucket0
305                         p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
306                         p.nonceBucketN = 0
307                 }
308         } else {
309                 if p.nonceRecv != p.NonceExpect {
310                         p.FramesDup++
311                         p.BusyR.Unlock()
312                         return false
313                 }
314                 p.NonceExpect += 2
315         }
316         if p.nonceRecv > p.nonceLatest {
317                 p.nonceLatest = p.nonceRecv
318         }
319
320         p.FramesIn++
321         atomic.AddInt64(&p.BytesIn, int64(len(data)))
322         p.LastPing = time.Now()
323         p.pktSizeR = binary.BigEndian.Uint16(p.bufR[S20BS : S20BS+PktSizeSize])
324
325         if p.pktSizeR == 0 {
326                 p.HeartbeatRecv++
327                 p.BusyR.Unlock()
328                 return true
329         }
330         p.BytesPayloadIn += int64(p.pktSizeR)
331         tap.Write(p.bufR[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSizeR])
332         p.BusyR.Unlock()
333         return true
334 }