From ca2a2fad9ef8b019efb4d85de74ebd904d27454f23ad7487d93ecf3782bcd96c Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 16 Apr 2024 00:50:34 +0300 Subject: [PATCH] SipHash24 for short messages is much faster and secure enough --- cmd/client/main.go | 63 ++++++++++++++++++++++----------------------- cmd/server/main.go | 40 ++++++++++++---------------- cmd/server/peer.go | 2 ++ doc/features.texi | 8 +++--- doc/index.texi | 2 +- doc/integrity.texi | 4 +-- doc/proto.texi | 8 +++--- go.mod | 1 + go.sum | 2 ++ internal/noise.go | 2 +- internal/var.go | 2 -- internal/version.go | 2 +- 12 files changed, 66 insertions(+), 70 deletions(-) diff --git a/cmd/client/main.go b/cmd/client/main.go index fadaf51..6ac7509 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -32,13 +32,13 @@ import ( "strings" "time" + "github.com/dchest/siphash" "github.com/flynn/noise" "github.com/jroimartin/gocui" "go.stargrave.org/opus/v2" vors "go.stargrave.org/vors/internal" "golang.org/x/crypto/blake2s" "golang.org/x/crypto/chacha20" - "golang.org/x/crypto/poly1305" ) type Stream struct { @@ -268,14 +268,20 @@ func main() { } } - var keyOur []byte + var keyCiphOur []byte + var keyMACOur []byte { - h, err := blake2s.New256(hs.ChannelBinding()) + xof, err := blake2s.NewXOF(32+16, nil) if err != nil { log.Fatalln(err) } - h.Write([]byte(vors.NoisePrologue)) - keyOur = h.Sum(nil) + xof.Write([]byte(vors.NoisePrologue)) + xof.Write(hs.ChannelBinding()) + buf := make([]byte, 32+16) + if _, err = io.ReadFull(xof, buf); err != nil { + log.Fatalln(err) + } + keyCiphOur, keyMACOur = buf[:32], buf[32:] } seen := time.Now() @@ -352,6 +358,7 @@ func main() { if err != nil { log.Fatal(err) } + keyCiph, keyMAC := key[:32], key[32:] stream := &Stream{ name: name, in: make(chan []byte, 1<<10), @@ -402,9 +409,8 @@ func main() { } var ciph *chacha20.Cipher - var macKey [32]byte - var mac *poly1305.MAC - tag := make([]byte, poly1305.TagSize) + mac := siphash.New(keyMAC) + tag := make([]byte, siphash.Size) var ctr uint32 pcm := make([]int16, vors.FrameLen) nonce := make([]byte, 12) @@ -413,26 +419,23 @@ func main() { var lastDur int for buf := range stream.in { copy(nonce[len(nonce)-4:], buf) - ciph, err = chacha20.NewUnauthenticatedCipher(key, nonce) - if err != nil { - log.Fatal(err) - } - clear(macKey[:]) - ciph.XORKeyStream(macKey[:], macKey[:]) - ciph.SetCounter(1) - mac = poly1305.New(&macKey) - if _, err = mac.Write(buf[4 : len(buf)-vors.TagLen]); err != nil { + mac.Reset() + if _, err = mac.Write(buf[: len(buf)-siphash.Size]); err != nil { log.Fatal(err) } mac.Sum(tag[:0]) if subtle.ConstantTimeCompare( - tag[:vors.TagLen], - buf[len(buf)-vors.TagLen:], + tag[:siphash.Size], + buf[len(buf)-siphash.Size:], ) != 1 { stream.stats.bads++ continue } - pkt = buf[4 : len(buf)-vors.TagLen] + ciph, err = chacha20.NewUnauthenticatedCipher(keyCiph, nonce) + if err != nil { + log.Fatal(err) + } + pkt = buf[4 : len(buf)-siphash.Size] ciph.XORKeyStream(pkt, pkt) ctr = binary.BigEndian.Uint32(nonce[len(nonce)-4:]) @@ -532,7 +535,7 @@ func main() { log.Println("wrong addr:", from) continue } - if n <= 4+vors.TagLen { + if n <= 4+siphash.Size { log.Println("too small:", n) continue } @@ -573,9 +576,8 @@ func main() { } <-LoggerReady var ciph *chacha20.Cipher - var macKey [32]byte - var mac *poly1305.MAC - tag := make([]byte, poly1305.TagSize) + mac := siphash.New(keyMACOur) + tag := make([]byte, siphash.Size) buf := make([]byte, 2*vors.FrameLen) pcm := make([]int16, vors.FrameLen) nonce := make([]byte, 12) @@ -608,21 +610,18 @@ func main() { incr(nonce[len(nonce)-3:]) copy(buf, nonce[len(nonce)-4:]) - ciph, err = chacha20.NewUnauthenticatedCipher(keyOur, nonce) + ciph, err = chacha20.NewUnauthenticatedCipher(keyCiphOur, nonce) if err != nil { log.Fatal(err) } - clear(macKey[:]) - ciph.XORKeyStream(macKey[:], macKey[:]) - ciph.SetCounter(1) ciph.XORKeyStream(buf[4:4+n], buf[4:4+n]) - mac = poly1305.New(&macKey) - if _, err = mac.Write(buf[4 : 4+n]); err != nil { + mac.Reset() + if _, err = mac.Write(buf[: 4+n]); err != nil { log.Fatal(err) } mac.Sum(tag[:0]) - copy(buf[4+n:], tag[:vors.TagLen]) - pkt = buf[:4+n+vors.TagLen] + copy(buf[4+n:], tag) + pkt = buf[:4+n+siphash.Size] OurStats.pkts++ OurStats.bytes += vors.IPHdrLen(srvAddrUDP.IP) + 8 + uint64(len(pkt)) diff --git a/cmd/server/main.go b/cmd/server/main.go index 514e8fe..b6b7255 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -33,12 +33,11 @@ import ( "strings" "time" + "github.com/dchest/siphash" "github.com/flynn/noise" "github.com/jroimartin/gocui" vors "go.stargrave.org/vors/internal" "golang.org/x/crypto/blake2s" - "golang.org/x/crypto/chacha20" - "golang.org/x/crypto/poly1305" ) var ( @@ -257,12 +256,17 @@ func newPeer(conn *net.TCPConn) { } { - h, err := blake2s.New256(hs.ChannelBinding()) + xof, err := blake2s.NewXOF(32+16, nil) if err != nil { log.Fatalln(err) } - h.Write([]byte(vors.NoisePrologue)) - peer.key = h.Sum(nil) + xof.Write([]byte(vors.NoisePrologue)) + xof.Write(hs.ChannelBinding()) + peer.key = make([]byte, 32+16) + if _, err = io.ReadFull(xof, peer.key); err != nil { + log.Fatalln(err) + } + peer.mac = siphash.New(peer.key[32:]) } { @@ -379,11 +383,7 @@ func main() { var err error var sid byte var peer *Peer - var ciph *chacha20.Cipher - var macKey [32]byte - var mac *poly1305.MAC - tag := make([]byte, poly1305.TagSize) - nonce := make([]byte, 12) + tag := make([]byte, siphash.Size) for { n, from, err = lnUDP.ReadFromUDP(buf) if err != nil { @@ -422,27 +422,19 @@ func main() { if n == 1 { continue } - if n <= 4+vors.TagLen { + if n <= 4+siphash.Size { peer.stats.bads++ continue } - copy(nonce[len(nonce)-4:], buf) - ciph, err = chacha20.NewUnauthenticatedCipher(peer.key, nonce) - if err != nil { - log.Fatal(err) - } - clear(macKey[:]) - ciph.XORKeyStream(macKey[:], macKey[:]) - ciph.SetCounter(1) - mac = poly1305.New(&macKey) - if _, err = mac.Write(buf[4 : n-vors.TagLen]); err != nil { + peer.mac.Reset() + if _, err = peer.mac.Write(buf[: n-siphash.Size]); err != nil { log.Fatal(err) } - mac.Sum(tag[:0]) + peer.mac.Sum(tag[:0]) if subtle.ConstantTimeCompare( - tag[:vors.TagLen], - buf[n-vors.TagLen:n], + tag[:siphash.Size], + buf[n-siphash.Size:n], ) != 1 { peer.stats.bads++ continue diff --git a/cmd/server/peer.go b/cmd/server/peer.go index d1e078a..82c569d 100644 --- a/cmd/server/peer.go +++ b/cmd/server/peer.go @@ -1,6 +1,7 @@ package main import ( + "hash" "log/slog" "net" "sync" @@ -29,6 +30,7 @@ type Peer struct { sid byte addr *net.UDPAddr key []byte + mac hash.Hash stats *Stats room *Room diff --git a/doc/features.texi b/doc/features.texi index 8987ded..27f5881 100644 --- a/doc/features.texi +++ b/doc/features.texi @@ -11,9 +11,11 @@ is single client speaking at one time. Concealment) and DTX (discontinuous transmission) features enabled. Optional VAD (voice activity detection). -@item Noise protocol-based handshake over TCP between client and server -for creating authenticated encrypted channel and authentication based on -server's public key knowledge. +@item Noise protocol-based 0-RTT handshake over TCP between client and +server for creating authenticated encrypted channel and authentication +based on server's public key knowledge. + +@item Fast ChaCha20 encryption with SipHash24 message authentication. @item Rooms, optionally password protected. diff --git a/doc/index.texi b/doc/index.texi index b1b19ee..b1c2fb9 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -43,7 +43,7 @@ stop trying to use and revive legacy obsolete IPv4. Either use some overlay network on top of it, or VPN, whatever. @item Mono-cypher, mono-codec protocol. The @url{https://opus-codec.org/, Opus} -audio codec is perfect for VoIP tasks. ChaCha20-Poly1305 is more than +audio codec is perfect for VoIP tasks. ChaCha20 is more than appropriate and satisfies as fast and secure encryption solution. @float diff --git a/doc/integrity.texi b/doc/integrity.texi index e26a34d..59815f8 100644 --- a/doc/integrity.texi +++ b/doc/integrity.texi @@ -6,6 +6,6 @@ that you retrieved trusted and untampered software. Its fingerprint: @code{SHA256:qmlbyzvDRNXGJNxteapAWOmJRrBrZ7afLsEqr36M6kA}. @example -$ ssh-keygen -Y verify -f PUBKEY-SSH.pub -I vors@@cypherpunks.ru -n file \ - -s vors-@value{VERSION}.tar.zst.sig < vors-@value{VERSION}.tar.zst +$ ssh-keygen -Y verify -f PUBKEY-SSH.pub -I vors@@stargrave.org -n file \ + -s vors-@value{VERSION}.tar.zst.sig