package aerotun import ( "crypto/cipher" "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/binary" "errors" "fmt" "sync" "golang.org/x/crypto/chacha20poly1305" ) // Version 2 binds data to fresh sender AND receiver boot identities. Directional // keys and XChaCha nonces cannot overlap between peers or restarts. const ( headerSize = 44 // magic(2), version(1), flags(1), sender(16), receiver(16), counter(8) flagHello = 4 flagAck = 8 flagConfirm = 16 maxPacketSize = 65535 ) type sessionID [16]byte var zeroSession sessionID var errReplay = errors.New("replayed or stale packet") var errSession = errors.New("peer session is not established") type replayWindow struct{ max, bits uint64 } func (w *replayWindow) accept(n uint64) bool { if n == 0 { return false } if n > w.max { d := n - w.max if d >= 64 { w.bits = 1 } else { w.bits = (w.bits << d) | 1 } w.max = n return true } d := w.max - n if d >= 64 || w.bits&(uint64(1)< 9000) { err = errSession return } if flags == flagHello { return } if sender != p.remote { if (flags != flagAck && flags != flagConfirm) || p.retired[sender] || len(p.retired) >= 1024 { err = errSession return } if p.remote != zeroSession { p.retired[p.remote] = true } p.remote = sender p.replay = replayWindow{} p.tx = p.aead(p.local, sender) p.rx = a changed = true } if !p.replay.accept(n) { err = errReplay return } return }