]> Sergey Matveev's repositories - vors.git/commitdiff
SipHash24 for short messages is much faster and secure enough v2.0.0
authorSergey Matveev <stargrave@stargrave.org>
Mon, 15 Apr 2024 21:50:34 +0000 (00:50 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 16 Apr 2024 05:25:14 +0000 (08:25 +0300)
12 files changed:
cmd/client/main.go
cmd/server/main.go
cmd/server/peer.go
doc/features.texi
doc/index.texi
doc/integrity.texi
doc/proto.texi
go.mod
go.sum
internal/noise.go
internal/var.go
internal/version.go

index fadaf51fd28367cfbed9a8468f666009e8433415a1dfffb436a083f2b60b2243..6ac75099d2099c8beaf373edb4e32d3bc1f9277a15914b5b59d35eac64929709 100644 (file)
@@ -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+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))
index 514e8feb04915b12bccb0d1e893ab4783a30c0c0821cf4c1f97f1dd93880f31f..b6b72553beee9b38afae7afcc0c6df8df93ffd846bb98f9fbae3c280158658f0 100644 (file)
@@ -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
index d1e078a9491a8bca2a67896c008b2ffa89e0c8312148fb3500c647d1277418c5..82c569df65155da64be4d930b3d37dba3b478c467d40ed409b4f9a867fbc6f85 100644 (file)
@@ -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
 
index 8987dedcff866f3689ea95fc872a4f25bd51e8baffceb729943c26add984e176..27f58819f8d8c81d6ccf90ad10c9a63b0d086804b1e32df585a62a77581e7ae1 100644 (file)
@@ -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.
 
index b1b19ee60221ae3161dcfbf206e7223fcd9c774b3bc61c7bf103cf2ca58cbaf3..b1c2fb92c984dcac6778daf581001651506cd0356798a9218df06f612e2cb6c7 100644 (file)
@@ -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
index e26a34d49c048b52789d14ac36d81f97088edb991c26606dca0ebfe2ac87105a..59815f8cd94a046ed2b93e35ab47dd130f49f3c9e5116b846c5794c6d999e701 100644 (file)
@@ -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 <vors-@value{VERSION}.tar.zst
 @end example
index b420984d2c2766e9f8fb1a8dbe35018aca78469bca628b663a975344230829d0..a1af4d43a52e65b4a9eb9b6e30a2a7e718c84f2164a7fe8e68bd90e003641b61 100644 (file)
@@ -10,10 +10,10 @@ Each frame has a single byte stream identifier (unique identifier of the
 participant) and 24-bit big-endian packet counter. Reordered packets are
 dropped. 24-bit counter is long enough for very long talk sessions.
 
-Each packet is encrypted with ChaCha20-Poly1305. The key is generated
-during the handshake procedure with the server and is shared among the
-other participants. The stream identifier together with the packet
-counter is used as a nonce. Only 64-bits of Poly1305 are used.
+Each packet is encrypted with ChaCha20 and authenticated with SipHash24.
+The keys are generated during the handshake procedure with the server
+and is shared among the other participants. The stream identifier
+together with the packet counter is used as a nonce.
 
 It is tuned for 24Kbps bandwidth. But remember that it has additional 8B
 of MAC tag, 4B VoRS, 8B UDP and 40B IPv6 headers.
diff --git a/go.mod b/go.mod
index 0276bb781f818eb307bf3c65fa65c8ac8a58673b754e31ff215fbdec368ee4fc..563ffc7c424648d3d4a0ab62ce68d2f92d12d9492d99798a19a5e9f8147323a8 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module go.stargrave.org/vors
 go 1.21
 
 require (
+       github.com/dchest/siphash v1.2.3
        github.com/dustin/go-humanize v1.0.1
        github.com/flynn/noise v1.1.0
        github.com/jroimartin/gocui v0.5.0
diff --git a/go.sum b/go.sum
index c4ccfe2880830bedbf387a9c3aef57d7fac0b9e0a984955f9572638eed1a8033..4646fd1f78488ac8abef15cd3f3ae72889f346e9fd21d6f1624dce9b36cae45d 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
+github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
index 5e4b8837942d17de6fc415f378d2badb528b10924810d3ac824d9b44366dec78..365a39c1f41c1635456e363865faa7a7ca223f7c0b5b6ac9b13092434dc7c045 100644 (file)
@@ -8,7 +8,7 @@ import (
        "github.com/flynn/noise"
 )
 
-const NoisePrologue = "VoRS v1"
+const NoisePrologue = "VoRS v2"
 
 var NoiseCipherSuite = noise.NewCipherSuite(
        noise.DH25519,
index 8236a4d77bfcf305a1e86c1c3201cb42a27b3bf287ce5c3a35b4d883dd030402..0ef1d5a837b85209d6db4e3c6d5f898138241699d5a1e4459a93e8c3942efe7e 100644 (file)
@@ -6,8 +6,6 @@ import (
 )
 
 const (
-       TagLen = 8
-
        CmdPing = "PING"
        CmdPong = "PONG"
        CmdAdd  = "ADD"
index 28dd1a0bedc5aec0dea375558fceb4fe41f23596d6c2e94c05f62573a58c5966..f454ea5866d033e35b58d627c47d4b70e07fcb278a62b663ebece3b6d9496d1f 100644 (file)
@@ -3,7 +3,7 @@ package internal
 import "runtime"
 
 const (
-       Version  = "1.0.0"
+       Version  = "2.0.0"
        Warranty = `Copyright (C) 2024 Sergey Matveev
 
 This program is free software: you can redistribute it and/or modify