10 "golang.org/x/crypto/poly1305"
11 "golang.org/x/crypto/salsa20"
12 "golang.org/x/crypto/xtea"
18 TagSize = poly1305.TagSize
19 // S20BS is Salsa20's internal blocksize in bytes
21 // Maximal amount of bytes transfered with single key (4 GiB)
22 MaxBytesPerKey int64 = 1 << 32
23 // Size of packet's size mark in bytes
25 // Heartbeat rate, relative to Timeout
27 // Minimal valid packet length
28 MinPktLength = 2 + 16 + 8
31 func newNonceCipher(key *[32]byte) *xtea.Cipher {
32 nonceKey := make([]byte, 16)
36 make([]byte, xtea.BlockSize),
39 ciph, err := xtea.NewCipher(nonceKey)
54 CPRCycle time.Duration `json:"-"`
56 // Cryptography related
57 Key *[SSize]byte `json:"-"`
58 NonceCipher *xtea.Cipher `json:"-"`
62 NonceExpect uint64 `json:"-"`
63 nonceBucket0 map[uint64]struct{}
64 nonceBucket1 map[uint64]struct{}
70 Timeout time.Duration `json:"-"`
74 willSentCycle time.Time
89 BusyR sync.Mutex `json:"-"`
96 BusyT sync.Mutex `json:"-"`
104 func (p *Peer) String() string {
105 return p.Id.String() + ":" + p.Addr
108 // Zero peer's memory state.
109 func (p *Peer) Zero() {
115 sliceZero(p.keyAuthR[:])
116 sliceZero(p.keyAuthT[:])
121 func (p *Peer) NonceExpectation(buf []byte) {
122 binary.BigEndian.PutUint64(buf, p.NonceExpect)
123 p.NonceCipher.Encrypt(buf, buf)
126 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
128 timeout := conf.Timeout
130 cprCycle := cprCycleCalculate(conf.CPR)
131 noiseEnable := conf.Noise
136 timeout = timeout / TimeoutHeartbeat
144 NoiseEnable: noiseEnable,
149 NonceCipher: newNonceCipher(key),
150 nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
151 nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
157 bufR: make([]byte, S20BS+MTU+NonceSize),
158 bufT: make([]byte, S20BS+MTU+NonceSize),
159 tagR: new([TagSize]byte),
160 tagT: new([TagSize]byte),
161 keyAuthR: new([SSize]byte),
162 keyAuthT: new([SSize]byte),
166 peer.NonceExpect = 0 + 2
169 peer.NonceExpect = 1 + 2
175 // Process incoming Ethernet packet.
176 // ready channel is TAPListen's synchronization channel used to tell him
177 // that he is free to receive new packets. Encrypted and authenticated
178 // packets will be sent to remote Peer side immediately.
179 func (p *Peer) EthProcess(data []byte) {
183 // Zero size is a heartbeat packet
185 // If this heartbeat is necessary
186 if !p.LastSent.Add(p.Timeout).Before(p.now) {
190 p.bufT[S20BS+0] = byte(0)
191 p.bufT[S20BS+1] = byte(0)
194 // Copy payload to our internal buffer and we are ready to
195 // accept the next one
196 binary.BigEndian.PutUint16(
197 p.bufT[S20BS:S20BS+PktSizeSize],
200 copy(p.bufT[S20BS+PktSizeSize:], data)
201 p.BytesPayloadOut += int64(len(data))
205 p.frameT = p.bufT[S20BS : S20BS+MTU-TagSize]
207 p.frameT = p.bufT[S20BS : S20BS+PktSizeSize+len(data)+NonceSize]
210 binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
211 p.NonceCipher.Encrypt(
212 p.frameT[len(p.frameT)-NonceSize:],
213 p.frameT[len(p.frameT)-NonceSize:],
215 for i := 0; i < SSize; i++ {
218 salsa20.XORKeyStream(
219 p.bufT[:S20BS+len(p.frameT)-NonceSize],
220 p.bufT[:S20BS+len(p.frameT)-NonceSize],
221 p.frameT[len(p.frameT)-NonceSize:],
225 copy(p.keyAuthT[:], p.bufT[:SSize])
226 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
228 atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize))
231 if p.CPRCycle != time.Duration(0) {
232 p.willSentCycle = p.LastSent.Add(p.CPRCycle)
233 if p.willSentCycle.After(p.now) {
234 time.Sleep(p.willSentCycle.Sub(p.now))
235 p.now = p.willSentCycle
240 p.Conn.Write(append(p.tagT[:], p.frameT...))
244 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
246 for i := 0; i < SSize; i++ {
249 copy(p.bufR[S20BS:], data[TagSize:])
250 salsa20.XORKeyStream(
251 p.bufR[:S20BS+len(data)-TagSize-NonceSize],
252 p.bufR[:S20BS+len(data)-TagSize-NonceSize],
253 data[len(data)-NonceSize:],
257 copy(p.keyAuthR[:], p.bufR[:SSize])
258 copy(p.tagR[:], data[:TagSize])
259 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
265 // Check if received nonce is known to us in either of two buckets.
266 // If yes, then this is ignored duplicate.
267 // Check from the oldest bucket, as in most cases this will result
268 // in constant time check.
269 // If Bucket0 is filled, then it becomes Bucket1.
270 p.NonceCipher.Decrypt(
271 data[len(data)-NonceSize:],
272 data[len(data)-NonceSize:],
274 p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
276 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
277 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
278 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
283 p.nonceBucket0[p.nonceRecv] = struct{}{}
285 if p.nonceBucketN == NonceBucketSize {
286 p.nonceBucket1 = p.nonceBucket0
287 p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
291 if p.nonceRecv != p.NonceExpect {
298 if p.nonceRecv > p.nonceLatest {
299 p.nonceLatest = p.nonceRecv
303 atomic.AddInt64(&p.BytesIn, int64(len(data)))
304 p.LastPing = time.Now()
305 p.pktSizeR = binary.BigEndian.Uint16(p.bufR[S20BS : S20BS+PktSizeSize])
312 p.BytesPayloadIn += int64(p.pktSizeR)
313 tap.Write(p.bufR[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSizeR])