From: Sergey Matveev Date: Tue, 25 Aug 2015 09:00:34 +0000 (+0300) Subject: Do not check nonce against buckets in TCP mode X-Git-Tag: 4.0^2~29 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=327a01aa70ad295de80da527bfdb7e5dd84beae8;p=govpn.git Do not check nonce against buckets in TCP mode There is no need in nonce checking against buckets in TCP mode because all incoming data can not be reordered normally. Any reordering means intrusion by the man in the middle. This is performance optimization. Signed-off-by: Sergey Matveev --- diff --git a/src/govpn/cmd/govpn-client/main.go b/src/govpn/cmd/govpn-client/main.go index 7f32b6c..6d312ce 100644 --- a/src/govpn/cmd/govpn-client/main.go +++ b/src/govpn/cmd/govpn-client/main.go @@ -21,7 +21,6 @@ package main import ( "flag" - "io" "log" "net" "os" @@ -78,7 +77,7 @@ func main() { } govpn.PeersInitDummy(id, conf) - var conn io.Writer + var conn govpn.RemoteConn var sink chan []byte var ready chan struct{} switch *proto { diff --git a/src/govpn/cmd/govpn-client/proxy.go b/src/govpn/cmd/govpn-client/proxy.go index 678a5ae..98c9098 100644 --- a/src/govpn/cmd/govpn-client/proxy.go +++ b/src/govpn/cmd/govpn-client/proxy.go @@ -21,13 +21,14 @@ package main import ( "bufio" "encoding/base64" - "io" "log" "net" "net/http" + + "govpn" ) -func proxyTCP() (io.Writer, chan []byte, chan struct{}) { +func proxyTCP() (govpn.RemoteConn, chan []byte, chan struct{}) { proxyAddr, err := net.ResolveTCPAddr("tcp", *proxyAddr) if err != nil { log.Fatalln("Can not resolve proxy address:", err) diff --git a/src/govpn/cmd/govpn-client/tcp.go b/src/govpn/cmd/govpn-client/tcp.go index 373789a..dcf7b14 100644 --- a/src/govpn/cmd/govpn-client/tcp.go +++ b/src/govpn/cmd/govpn-client/tcp.go @@ -20,7 +20,6 @@ package main import ( "encoding/binary" - "io" "log" "net" @@ -38,7 +37,11 @@ func (c TCPSender) Write(data []byte) (int, error) { return c.conn.Write(append(size, data...)) } -func startTCP() (io.Writer, chan []byte, chan struct{}) { +func (c TCPSender) Reorderable() bool { + return false +} + +func startTCP() (govpn.RemoteConn, chan []byte, chan struct{}) { remote, err := net.ResolveTCPAddr("tcp", *remoteAddr) if err != nil { log.Fatalln("Can not resolve remote address:", err) diff --git a/src/govpn/cmd/govpn-client/udp.go b/src/govpn/cmd/govpn-client/udp.go index 103ed6d..57a457f 100644 --- a/src/govpn/cmd/govpn-client/udp.go +++ b/src/govpn/cmd/govpn-client/udp.go @@ -19,7 +19,6 @@ along with this program. If not, see . package main import ( - "io" "log" "net" "time" @@ -27,13 +26,24 @@ import ( "govpn" ) -func startUDP() (io.Writer, chan []byte, chan struct{}) { +type UDPSender struct { + conn *net.UDPConn +} + +func (c UDPSender) Write(data []byte) (int, error) { + return c.conn.Write(data) +} + +func (c UDPSender) Reorderable() bool { + return true +} + +func startUDP() (govpn.RemoteConn, chan []byte, chan struct{}) { remote, err := net.ResolveUDPAddr("udp", *remoteAddr) if err != nil { log.Fatalln("Can not resolve remote address:", err) } c, err := net.DialUDP("udp", nil, remote) - conn := io.Writer(c) if err != nil { log.Fatalln("Can not listen on UDP:", err) } @@ -55,5 +65,5 @@ func startUDP() (io.Writer, chan []byte, chan struct{}) { } }() ready <- struct{}{} - return conn, sink, ready + return UDPSender{c}, sink, ready } diff --git a/src/govpn/cmd/govpn-server/main.go b/src/govpn/cmd/govpn-server/main.go index 37135b9..2aa5100 100644 --- a/src/govpn/cmd/govpn-server/main.go +++ b/src/govpn/cmd/govpn-server/main.go @@ -22,7 +22,6 @@ package main import ( "bytes" "flag" - "io" "log" "net" "os" @@ -45,7 +44,7 @@ var ( type Pkt struct { addr string - conn io.Writer + conn govpn.RemoteConn data []byte ready chan struct{} } diff --git a/src/govpn/cmd/govpn-server/tcp.go b/src/govpn/cmd/govpn-server/tcp.go index 42467d6..d02565c 100644 --- a/src/govpn/cmd/govpn-server/tcp.go +++ b/src/govpn/cmd/govpn-server/tcp.go @@ -36,6 +36,10 @@ func (c TCPSender) Write(data []byte) (int, error) { return c.conn.Write(append(size, data...)) } +func (c TCPSender) Reorderable() bool { + return false +} + func startTCP(sink chan Pkt) { bind, err := net.ResolveTCPAddr("tcp", *bindAddr) if err != nil { diff --git a/src/govpn/cmd/govpn-server/udp.go b/src/govpn/cmd/govpn-server/udp.go index c2d6e96..e6bbc5e 100644 --- a/src/govpn/cmd/govpn-server/udp.go +++ b/src/govpn/cmd/govpn-server/udp.go @@ -35,6 +35,10 @@ func (c UDPSender) Write(data []byte) (int, error) { return c.conn.WriteToUDP(data, c.addr) } +func (c UDPSender) Reorderable() bool { + return true +} + func startUDP(sink chan Pkt) { bind, err := net.ResolveUDPAddr("udp", *bindAddr) ready := make(chan struct{}) diff --git a/src/govpn/handshake.go b/src/govpn/handshake.go index 3e3d0d6..d99154a 100644 --- a/src/govpn/handshake.go +++ b/src/govpn/handshake.go @@ -22,7 +22,6 @@ import ( "crypto/rand" "crypto/subtle" "encoding/binary" - "io" "log" "time" @@ -41,7 +40,7 @@ const ( type Handshake struct { addr string - conn io.Writer + conn RemoteConn LastPing time.Time Conf *PeerConf dsaPubH *[ed25519.PublicKeySize]byte @@ -134,7 +133,7 @@ func dhKeyGen(priv, pub *[32]byte) *[32]byte { } // Create new handshake state. -func HandshakeNew(addr string, conn io.Writer, conf *PeerConf) *Handshake { +func HandshakeNew(addr string, conn RemoteConn, conf *PeerConf) *Handshake { state := Handshake{ addr: addr, conn: conn, @@ -161,9 +160,8 @@ func idTag(id *PeerId, data []byte) []byte { // Start handshake's procedure from the client. It is the entry point // for starting the handshake procedure. // First handshake packet // will be sent immediately. -func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake { +func HandshakeStart(addr string, conn RemoteConn, conf *PeerConf) *Handshake { state := HandshakeNew(addr, conn, conf) - var dhPubRepr *[32]byte state.dhPriv, dhPubRepr = dhKeypairGen() @@ -253,10 +251,10 @@ func (h *Handshake) Server(data []byte) *Peer { // Switch peer peer := newPeer( + false, h.addr, h.conn, h.Conf, - 0, keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize])) h.LastPing = time.Now() return peer @@ -329,7 +327,13 @@ func (h *Handshake) Client(data []byte) *Peer { } // Switch peer - peer := newPeer(h.addr, h.conn, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:])) + peer := newPeer( + true, + h.addr, + h.conn, + h.Conf, + keyFromSecrets(h.sServer[:], h.sClient[:]), + ) h.LastPing = time.Now() return peer default: diff --git a/src/govpn/transport.go b/src/govpn/transport.go index 23f04dd..5085d29 100644 --- a/src/govpn/transport.go +++ b/src/govpn/transport.go @@ -41,10 +41,16 @@ const ( TimeoutHeartbeat = 4 ) +type RemoteConn interface { + io.Writer + // Can incoming packets be reordered + Reorderable() bool +} + type Peer struct { Addr string Id *PeerId - Conn io.Writer + Conn RemoteConn // Traffic behaviour NoiseEnable bool @@ -56,6 +62,7 @@ type Peer struct { NonceOur uint64 `json:"-"` NonceRecv uint64 `json:"-"` NonceCipher *xtea.Cipher `json:"-"` + nonceExpect uint64 `json:"-"` nonceBucket0 map[uint64]struct{} nonceBucket1 map[uint64]struct{} nonceFound bool @@ -201,7 +208,7 @@ func cprCycleCalculate(rate int) time.Duration { return time.Second / time.Duration(rate*(1<<10)/MTU) } -func newPeer(addr string, conn io.Writer, conf *PeerConf, nonce int, key *[SSize]byte) *Peer { +func newPeer(isClient bool, addr string, conn RemoteConn, conf *PeerConf, key *[SSize]byte) *Peer { now := time.Now() timeout := conf.Timeout cprCycle := cprCycleCalculate(conf.CPR) @@ -213,25 +220,33 @@ func newPeer(addr string, conn io.Writer, conf *PeerConf, nonce int, key *[SSize timeout = timeout / TimeoutHeartbeat } peer := Peer{ - Addr: addr, - Conn: conn, - Timeout: timeout, - Established: now, - LastPing: now, - Id: conf.Id, - NoiseEnable: noiseEnable, - CPR: conf.CPR, - CPRCycle: cprCycle, - NonceOur: uint64(nonce), - NonceRecv: uint64(0), - nonceBucket0: make(map[uint64]struct{}, NonceBucketSize), - nonceBucket1: make(map[uint64]struct{}, NonceBucketSize), - Key: key, - NonceCipher: newNonceCipher(key), - buf: make([]byte, MTU+S20BS), - tag: new([poly1305.TagSize]byte), - keyAuth: new([SSize]byte), - nonce: make([]byte, NonceSize), + Addr: addr, + Conn: conn, + Timeout: timeout, + Established: now, + LastPing: now, + Id: conf.Id, + NoiseEnable: noiseEnable, + CPR: conf.CPR, + CPRCycle: cprCycle, + NonceRecv: 0, + Key: key, + NonceCipher: newNonceCipher(key), + buf: make([]byte, MTU+S20BS), + tag: new([poly1305.TagSize]byte), + keyAuth: new([SSize]byte), + nonce: make([]byte, NonceSize), + } + if isClient { + peer.NonceOur = 1 + peer.nonceExpect = 0 + 2 + } else { + peer.NonceOur = 0 + peer.nonceExpect = 1 + 2 + } + if conn.Reorderable() { + peer.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize) + peer.nonceBucket1 = make(map[uint64]struct{}, NonceBucketSize) } return &peer } @@ -265,21 +280,30 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, ready chan struct{}) bool // If Bucket0 is filled, then it becomes Bucket1. p.NonceCipher.Decrypt(p.buf, data[:NonceSize]) ready <- struct{}{} + p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize]) - if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound { - p.FramesDup++ - return false - } - if _, p.nonceFound = p.nonceBucket0[p.NonceRecv]; p.nonceFound { - p.FramesDup++ - return false - } - p.nonceBucket0[p.NonceRecv] = struct{}{} - p.nonceBucketN++ - if p.nonceBucketN == NonceBucketSize { - p.nonceBucket1 = p.nonceBucket0 - p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize) - p.nonceBucketN = 0 + if p.Conn.Reorderable() { + if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound { + p.FramesDup++ + return false + } + if _, p.nonceFound = p.nonceBucket0[p.NonceRecv]; p.nonceFound { + p.FramesDup++ + return false + } + p.nonceBucket0[p.NonceRecv] = struct{}{} + p.nonceBucketN++ + if p.nonceBucketN == NonceBucketSize { + p.nonceBucket1 = p.nonceBucket0 + p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize) + p.nonceBucketN = 0 + } + } else { + if p.nonceRecv != p.nonceExpect { + p.FramesDup++ + return false + } + p.nonceExpect += 2 } p.FramesIn++ diff --git a/src/govpn/transport_test.go b/src/govpn/transport_test.go index 5809a9f..f9f731e 100644 --- a/src/govpn/transport_test.go +++ b/src/govpn/transport_test.go @@ -14,7 +14,7 @@ var ( conf *PeerConf ) -type Dummy struct{ +type Dummy struct { dst *[]byte } @@ -25,6 +25,10 @@ func (d Dummy) Write(b []byte) (int, error) { return len(b), nil } +func (d Dummy) Reorderable() bool { + return true +} + func init() { MTU = 1500 peerId, _ = IDDecode("ffffffffffffffffffffffffffffffff") @@ -34,7 +38,7 @@ func init() { NoiseEnable: false, CPR: 0, } - peer = newPeer("foo", Dummy{&ciphertext}, conf, 128, new([SSize]byte)) + peer = newPeer(true, "foo", Dummy{&ciphertext}, conf, new([SSize]byte)) plaintext = make([]byte, 789) ready = make(chan struct{}) go func() { @@ -54,7 +58,7 @@ func BenchmarkEnc(b *testing.B) { func BenchmarkDec(b *testing.B) { peer.EthProcess(plaintext, ready) - peer = newPeer("foo", Dummy{nil}, conf, 128, new([SSize]byte)) + peer = newPeer(true, "foo", Dummy{nil}, conf, new([SSize]byte)) b.ResetTimer() for i := 0; i < b.N; i++ { peer.nonceBucket0 = make(map[uint64]struct{}, 1)