From: Sergey Matveev Date: Fri, 1 May 2015 14:59:46 +0000 (+0300) Subject: Explicitly store payload size in each message X-Git-Tag: 3.0^2~16 X-Git-Url: http://www.git.cypherpunks.ru/?p=govpn.git;a=commitdiff_plain;h=a627f8cd4bcd8e96d6d2b8e1adb90fa090a3788d Explicitly store payload size in each message Signed-off-by: Sergey Matveev --- diff --git a/doc/developer.texi b/doc/developer.texi index b257885..b218c35 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -11,7 +11,7 @@ @item Password authenticated key agreement DH-EKE powered by @url{http://cr.yp.to/ecdh.html, Curve25519} @item Packet overhead -24 bytes per packet +26 bytes per packet @item Handshake overhead 4 UDP (2 from client, 2 from server) packets, 200 bytes total payload @end table diff --git a/doc/transport.texi b/doc/transport.texi index d0cb756..b12a308 100644 --- a/doc/transport.texi +++ b/doc/transport.texi @@ -2,8 +2,8 @@ @section Transport protocol @verbatim -ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA) + - AUTH(ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA)) +ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA) + + AUTH(ENCn(SERIAL) + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA)) @end verbatim All transport and handshake messages are indistinguishable from @@ -24,6 +24,7 @@ Encrypted @code{SERIAL} is used as a nonce for @code{DATA} encryption: encryption key is different during each handshake, so (key, nonce) pair is always used only once. @code{ENC} is Salsa20 cipher, with established session @code{KEY} and encrypted @code{SERIAL} used as a nonce. +@code{DATA_SIZE} is @emph{uint16} storing length of the @code{DATA}. @code{AUTH} is Poly1305 authentication function. First 256 bits of Salsa20 output are used as a one-time key for @code{AUTH}. Next 256 bits diff --git a/tap.go b/tap.go index 6b298f7..8a19df3 100644 --- a/tap.go +++ b/tap.go @@ -24,6 +24,10 @@ import ( "golang.org/x/crypto/poly1305" ) +const ( + EtherSize = 14 +) + type TAP struct { Name string dev io.ReadWriter @@ -33,8 +37,12 @@ type TAP struct { synced bool } +func TAPMaxMTU() int { + return MTU - poly1305.TagSize - NonceSize - PktSizeSize - EtherSize +} + func NewTAP(ifaceName string) (*TAP, error) { - maxIfacePktSize := MTU - poly1305.TagSize - NonceSize + maxIfacePktSize := TAPMaxMTU() + EtherSize tapRaw, err := newTAPer(ifaceName) if err != nil { return nil, err diff --git a/transport.go b/transport.go index 2638243..4277628 100644 --- a/transport.go +++ b/transport.go @@ -19,7 +19,6 @@ along with this program. If not, see . package govpn import ( - "crypto/subtle" "encoding/binary" "io" "log" @@ -35,10 +34,11 @@ const ( NonceSize = 8 KeySize = 32 // S20BS is Salsa20's internal blocksize in bytes - S20BS = 64 - HeartbeatSize = 12 + 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 ) type UDPPkt struct { @@ -61,6 +61,7 @@ type Peer struct { nonceRecv uint64 frame []byte nonce []byte + pktSize uint64 BytesIn int64 BytesOut int64 FramesIn int @@ -86,8 +87,7 @@ func (p *Peer) Zero() { } var ( - HeartbeatMark = []byte("\x00\x00\x00HEARTBEAT") - Emptiness = make([]byte, KeySize) + Emptiness = make([]byte, 1<<16) taps = make(map[string]*TAP) heartbeatPeriod *time.Duration ) @@ -230,7 +230,7 @@ func newPeer(addr *net.UDPAddr, id PeerId, nonce int, key *[KeySize]byte) *Peer // will be written to the interface immediately (except heartbeat ones). func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) bool { size := len(udpPkt) - copy(p.buf[:KeySize], Emptiness) + copy(p.buf, Emptiness) copy(p.tag[:], udpPkt[size-poly1305.TagSize:]) copy(p.buf[S20BS:], udpPkt[NonceSize:size-poly1305.TagSize]) salsa20.XORKeyStream( @@ -255,13 +255,14 @@ func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) boo ready <- struct{}{} p.LastPing = time.Now() p.NonceRecv = p.nonceRecv - p.frame = p.buf[S20BS : S20BS+size-NonceSize-poly1305.TagSize] - p.BytesIn += int64(len(p.frame)) - p.FramesIn++ - if subtle.ConstantTimeCompare(p.frame[:HeartbeatSize], HeartbeatMark) == 1 { + p.pktSize, _ = binary.Uvarint(p.buf[S20BS : S20BS+PktSizeSize]) + if p.pktSize == 0 { p.HeartbeatRecv++ return true } + p.frame = p.buf[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSize] + p.BytesIn += int64(p.pktSize) + p.FramesIn++ tap.Write(p.frame) return true } @@ -282,14 +283,14 @@ func (p *Peer) EthProcess(ethPkt []byte, conn WriteToer, ready chan struct{}) { if size == 0 && !p.LastSent.Add(heartbeatPeriodGet()).Before(now) { return } - copy(p.buf[:KeySize], Emptiness) + copy(p.buf, Emptiness) if size > 0 { - copy(p.buf[S20BS:], ethPkt) + copy(p.buf[S20BS+PktSizeSize:], ethPkt) ready <- struct{}{} + binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(size)) + p.BytesOut += int64(size) } else { p.HeartbeatSent++ - copy(p.buf[S20BS:], HeartbeatMark) - size = HeartbeatSize } p.NonceOur = p.NonceOur + 2 @@ -300,10 +301,9 @@ func (p *Peer) EthProcess(ethPkt []byte, conn WriteToer, ready chan struct{}) { salsa20.XORKeyStream(p.buf, p.buf, p.nonce, p.Key) copy(p.buf[S20BS-NonceSize:S20BS], p.nonce) copy(p.keyAuth[:], p.buf[:KeySize]) - p.frame = p.buf[S20BS-NonceSize : S20BS+size] + p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+size] poly1305.Sum(p.tag, p.frame, p.keyAuth) - p.BytesOut += int64(len(p.frame)) p.FramesOut++ p.LastSent = now if _, err := conn.WriteTo(append(p.frame, p.tag[:]...), p.Addr); err != nil {