From: Sergey Matveev Date: Tue, 5 Jan 2016 11:03:59 +0000 (+0300) Subject: Simplify transport packet padding scheme, save 1 byte X-Git-Tag: 5.0^2~28 X-Git-Url: http://www.git.cypherpunks.ru/?p=govpn.git;a=commitdiff_plain;h=b34ab62244f364d754c7db601af65d5e0a0a88a7 Simplify transport packet padding scheme, save 1 byte Signed-off-by: Sergey Matveev --- diff --git a/doc/developer.texi b/doc/developer.texi index d9ec24f..d9136eb 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -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. diff --git a/doc/transport.texi b/doc/transport.texi index bfe3501..4e26785 100644 --- a/doc/transport.texi +++ b/doc/transport.texi @@ -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}. diff --git a/src/govpn/peer.go b/src/govpn/peer.go index 026ab66..f3bb0b4 100644 --- a/src/govpn/peer.go +++ b/src/govpn/peer.go @@ -19,6 +19,7 @@ along with this program. If not, see . 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 } diff --git a/src/govpn/peer_test.go b/src/govpn/peer_test.go index 4e6f587..40f6b1d 100644 --- a/src/govpn/peer_test.go +++ b/src/govpn/peer_test.go @@ -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 { diff --git a/src/govpn/tap.go b/src/govpn/tap.go index cd1b5e8..43207c1 100644 --- a/src/govpn/tap.go +++ b/src/govpn/tap.go @@ -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) {