]> Cypherpunks.ru repositories - govpn.git/commitdiff
Simplify transport packet padding scheme, save 1 byte
authorSergey Matveev <stargrave@stargrave.org>
Tue, 5 Jan 2016 11:03:59 +0000 (14:03 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 5 Jan 2016 12:58:53 +0000 (15:58 +0300)
Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
doc/developer.texi
doc/transport.texi
src/govpn/peer.go
src/govpn/peer_test.go
src/govpn/tap.go

index d9ec24fffbcdff245b5983402cfd8e400e2e92a5..d9136eb11518d2afc4179c35bd555bf06fa36236 100644 (file)
@@ -18,7 +18,7 @@ and @url{http://ed25519.cr.yp.to/, Ed25519}.
 @item Verifier password hashing algorithm
 @url{https://password-hashing.net/#argon2, Argon2d}.
 @item Packet overhead
-26 bytes per packet.
+25 bytes per packet.
 @item Handshake overhead
 4 UDP (2 from client, 2 from server) packets (round-trips for TCP),
 264 bytes total payload.
index bfe35013d7de4cbf4676db0e6fa86fcd2778fcd4..4e26785b409c9138cba94e8282d469c71fc24ff8 100644 (file)
@@ -19,7 +19,7 @@ TAG || ENCRYPTED || NONCE --> PACKET
 +--< ENCRYPT(KEY, NONCE, PAYLOAD)
                     ^       ^
                     |       |
-                    |       +--< SIZE || DATA [|| NOISE]
+                    |       +--< DATA || PAD [|| ZEROS]
                     |
                     +--< PRP(PRP_KEY, SERIAL)
 @end verbatim
@@ -44,11 +44,9 @@ PRP_KEY = ENCRYPT(KEY, 0, 128-bit)
 Salsa20's output is ignored and only remaining is XORed with ther data,
 encrypting it.
 
-@code{SIZE} is big-endian @emph{uint16} storing length of the
-@code{DATA}.
-
-@code{NOISE} is optional. It is just some junk data, intended to fill up
-packet to MTU size. This is useful for concealing payload packets length.
+@code{DATA} is padded with @code{PAD} (0x80 byte). Optional @code{ZEROS}
+may follow, to fillup packet with the junk to conceal pyload packet
+length.
 
 @code{AUTH} is Poly1305 authentication function. First 256 bits of
 Salsa20's output are used as a one-time key for @code{AUTH}.
index 026ab660077d65d563149673689c120f4e412e0a..f3bb0b43befea0c5081fc9dfb994be8415b8a532 100644 (file)
@@ -19,6 +19,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package govpn
 
 import (
+       "bytes"
        "encoding/binary"
        "io"
        "sync"
@@ -38,12 +39,12 @@ const (
        S20BS = 64
        // Maximal amount of bytes transfered with single key (4 GiB)
        MaxBytesPerKey int64 = 1 << 32
-       // Size of packet's size mark in bytes
-       PktSizeSize = 2
        // Heartbeat rate, relative to Timeout
        TimeoutHeartbeat = 4
        // Minimal valid packet length
-       MinPktLength = 2 + 16 + 8
+       MinPktLength = 1 + 16 + 8
+       // Padding byte
+       PadByte = byte(0x80)
 )
 
 func newNonceCipher(key *[32]byte) *xtea.Cipher {
@@ -109,7 +110,7 @@ type Peer struct {
        bufR     []byte
        tagR     *[TagSize]byte
        keyAuthR *[SSize]byte
-       pktSizeR uint16
+       pktSizeR int
 
        // Transmitter
        BusyT    sync.Mutex `json:"-"`
@@ -206,23 +207,20 @@ func (p *Peer) EthProcess(data []byte) {
        p.BusyT.Lock()
 
        // Zero size is a heartbeat packet
+       SliceZero(p.bufT)
        if len(data) == 0 {
                // If this heartbeat is necessary
                if !p.LastSent.Add(p.Timeout).Before(p.now) {
                        p.BusyT.Unlock()
                        return
                }
-               p.bufT[S20BS+0] = 0
-               p.bufT[S20BS+1] = 0
+               p.bufT[S20BS+0] = PadByte
                p.HeartbeatSent++
        } else {
                // Copy payload to our internal buffer and we are ready to
                // accept the next one
-               binary.BigEndian.PutUint16(
-                       p.bufT[S20BS:S20BS+PktSizeSize],
-                       uint16(len(data)),
-               )
-               copy(p.bufT[S20BS+PktSizeSize:], data)
+               copy(p.bufT[S20BS:], data)
+               p.bufT[S20BS+len(data)] = PadByte
                p.BytesPayloadOut += int64(len(data))
        }
 
@@ -231,7 +229,7 @@ func (p *Peer) EthProcess(data []byte) {
        } else if p.EncLess {
                p.frameT = p.bufT[S20BS : S20BS+MTU]
        } else {
-               p.frameT = p.bufT[S20BS : S20BS+PktSizeSize+len(data)+NonceSize]
+               p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
        }
        p.nonceOur += 2
        binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
@@ -252,9 +250,6 @@ func (p *Peer) EthProcess(data []byte) {
                }
                out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
        } else {
-               for i := 0; i < SSize; i++ {
-                       p.bufT[i] = 0
-               }
                salsa20.XORKeyStream(
                        p.bufT[:S20BS+len(p.frameT)-NonceSize],
                        p.bufT[:S20BS+len(p.frameT)-NonceSize],
@@ -317,7 +312,7 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
                        p.BusyR.Unlock()
                        return false
                }
-               out = p.bufR[S20BS:]
+               out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
        }
 
        // Check if received nonce is known to us in either of two buckets.
@@ -360,18 +355,26 @@ func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
        p.FramesIn++
        atomic.AddInt64(&p.BytesIn, int64(len(data)))
        p.LastPing = time.Now()
-       p.pktSizeR = binary.BigEndian.Uint16(out[:PktSizeSize])
+       p.pktSizeR = bytes.LastIndexByte(out, PadByte)
+       if p.pktSizeR == -1 {
+               p.BusyR.Unlock()
+               return false
+       }
+       // Validate the pad
+       for i := p.pktSizeR + 1; i < len(out); i++ {
+               if out[i] != 0 {
+                       p.BusyR.Unlock()
+                       return false
+               }
+       }
 
        if p.pktSizeR == 0 {
                p.HeartbeatRecv++
                p.BusyR.Unlock()
                return true
        }
-       if int(p.pktSizeR) > len(data)-MinPktLength {
-               return false
-       }
        p.BytesPayloadIn += int64(p.pktSizeR)
-       tap.Write(out[PktSizeSize : PktSizeSize+p.pktSizeR])
+       tap.Write(out[:p.pktSizeR])
        p.BusyR.Unlock()
        return true
 }
index 4e6f5879e5596ab72a01fc071927e39df932be2d..40f6b1d4970210c8e6d0ccd45d4a2629ac24110d 100644 (file)
@@ -71,10 +71,27 @@ func TestSymmetric(t *testing.T) {
        }
 }
 
-func TestSymmetricEncLess(t *testing.T) {
+func TestSymmetricNoise(t *testing.T) {
        peerd := newPeer(true, "foo", Dummy{nil}, conf, new([SSize]byte))
        peer.NoiseEnable = true
+       peerd.NoiseEnable = true
+       f := func(payload []byte) bool {
+               if len(payload) == 0 {
+                       return true
+               }
+               peer.EthProcess(payload)
+               return peerd.PktProcess(ciphertext, Dummy{nil}, true)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Error(err)
+       }
+       peer.NoiseEnable = true
+}
+
+func TestSymmetricEncLess(t *testing.T) {
+       peerd := newPeer(true, "foo", Dummy{nil}, conf, new([SSize]byte))
        peer.EncLess = true
+       peer.NoiseEnable = true
        peerd.EncLess = true
        peerd.NoiseEnable = true
        f := func(payload []byte) bool {
index cd1b5e84962c43d97db2633c6689f062ac95a4a8..43207c19e7cc032771d05eb27c40453fd4a4de13 100644 (file)
@@ -42,9 +42,9 @@ var (
 )
 
 // Return maximal acceptable TAP interface MTU. This is daemon's MTU
-// minus nonce, MAC, packet size mark and Ethernet header sizes.
+// minus nonce, MAC, pad and Ethernet header sizes.
 func TAPMaxMTU() int {
-       return MTU - poly1305.TagSize - NonceSize - PktSizeSize - EtherSize
+       return MTU - poly1305.TagSize - NonceSize - 1 - EtherSize
 }
 
 func NewTAP(ifaceName string) (*TAP, error) {