]> Cypherpunks repositories - govpn.git/commitdiff
Do not check nonce against buckets in TCP mode
authorSergey Matveev <stargrave@stargrave.org>
Tue, 25 Aug 2015 09:00:34 +0000 (12:00 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 25 Aug 2015 09:00:34 +0000 (12:00 +0300)
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 <stargrave@stargrave.org>
src/govpn/cmd/govpn-client/main.go
src/govpn/cmd/govpn-client/proxy.go
src/govpn/cmd/govpn-client/tcp.go
src/govpn/cmd/govpn-client/udp.go
src/govpn/cmd/govpn-server/main.go
src/govpn/cmd/govpn-server/tcp.go
src/govpn/cmd/govpn-server/udp.go
src/govpn/handshake.go
src/govpn/transport.go
src/govpn/transport_test.go

index 7f32b6c1a5af998b003c2ad45caef7b97a22d5bb..6d312ce20e26873ca6c4153c8b30f1e41fd88ade 100644 (file)
@@ -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 {
index 678a5ae931f68b49d1fc0f632b57ea2c79c4691f..98c9098e2dbf16ce7580ba7be2221fc907529993 100644 (file)
@@ -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)
index 373789af8be5f9c7f64ecf8df320c04bef20528c..dcf7b14b88f503add1378aadcb1c3005df76ff9f 100644 (file)
@@ -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)
index 103ed6d479c04db0485bd1db4e842ef21ebd789f..57a457f1a38c23d4da122feba23cd520b9336e41 100644 (file)
@@ -19,7 +19,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 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
 }
index 37135b9155d09df6314df3db11a7ec1d8ed43602..2aa5100e03627643da236dcc35c943903bc58ac9 100644 (file)
@@ -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{}
 }
index 42467d644ec98feac3bddcdbb379e38667314a09..d02565c6138c8ac08a94c60b8d7ed10a2ba92313 100644 (file)
@@ -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 {
index c2d6e96c2fe732c7064629e3bb02289701976c71..e6bbc5edb700882e124e46daf051c570e8448390 100644 (file)
@@ -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{})
index 3e3d0d62b0417281f4128ecf750f0e5793012555..d99154a9885717ea850db5d28b3f1ecd20250e25 100644 (file)
@@ -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:
index 23f04ddda4949c3782cb8429b312f1f8bcb22f73..5085d29285e32f108134533b650e329d9c088ca0 100644 (file)
@@ -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++
index 5809a9f4425a0a12a1edcacb38f276a5f8cf9ee2..f9f731e7139a825e9ba23f97f4183f4e604a33f8 100644 (file)
@@ -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)