From: Sergey Matveev Date: Wed, 10 Apr 2024 21:54:15 +0000 (+0300) Subject: Shorter MAC X-Git-Tag: v1.0.0~19 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=2a323a865d511557a4fd8e76890c9f11c9108fe09efbf6ac511452d0c386568b;p=vors.git Shorter MAC --- diff --git a/cmd/client/main.go b/cmd/client/main.go index 6b4fe68..87d77fc 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -18,6 +18,7 @@ package main import ( "bufio" "bytes" + "crypto/subtle" "crypto/tls" "crypto/x509" "encoding/binary" @@ -39,11 +40,12 @@ import ( vors "go.stargrave.org/vors/internal" "golang.org/x/crypto/blake2s" "golang.org/x/crypto/chacha20" - "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/poly1305" "gopkg.in/hraban/opus.v2" ) +const TagLen = 8 + type Stream struct { name string ctr uint32 @@ -188,12 +190,7 @@ func main() { Streams[sid] = &Stream{name: *Name, stats: OurStats} tlsState := ctrl.ConnectionState() - key, err := tlsState.ExportKeyingMaterial( - cols[1], nil, chacha20poly1305.KeySize) - if err != nil { - log.Fatal(err) - } - ciph, err := chacha20poly1305.New(key) + keyOur, err := tlsState.ExportKeyingMaterial(cols[1], nil, chacha20.KeySize) if err != nil { log.Fatal(err) } @@ -277,10 +274,6 @@ func main() { stats: &Stats{dead: make(chan struct{})}, } go func() { - ciph, err := chacha20poly1305.New(key) - if err != nil { - log.Fatal(err) - } dec, err := opus.NewDecoder(vors.Rate, 1) if err != nil { log.Fatal(err) @@ -300,24 +293,39 @@ func main() { } } - ctr := uint32(sid) << 24 + var ciph *chacha20.Cipher + var macKey [32]byte + var mac *poly1305.MAC + tag := make([]byte, poly1305.TagSize) + var ctr uint32 pcm := make([]int16, vors.FrameLen) pcmbuf := make([]byte, 2*vors.FrameLen) - decbuf := make([]byte, 2*vors.FrameLen) - nonce := make([]byte, chacha20.NonceSize) - ctrBuf := nonce[len(nonce)-4:] + nonce := make([]byte, 12) var pkt []byte lost := -1 var lastDur int for buf := range stream.in { - ctr = binary.BigEndian.Uint32(buf) - copy(ctrBuf, buf) - pkt, err = ciph.Open( - decbuf[:0], nonce, buf[4:], []byte{buf[0]}) + copy(nonce[len(nonce)-4:], buf) + ciph, err = chacha20.NewUnauthenticatedCipher(key, nonce) if err != nil { - log.Println("decrypt:", stream.name, err) + log.Fatal(err) + } + clear(macKey[:]) + ciph.XORKeyStream(macKey[:], macKey[:]) + ciph.SetCounter(1) + mac = poly1305.New(&macKey) + if _, err = mac.Write(buf[4 : len(buf)-TagLen]); err != nil { + log.Fatal(err) + } + mac.Sum(tag[:0]) + if subtle.ConstantTimeCompare(tag[:TagLen], buf[len(buf)-TagLen:]) != 1 { + log.Println("decrypt:", stream.name, "tag differs") continue } + pkt = buf[4 : len(buf)-TagLen] + ciph.XORKeyStream(pkt, pkt) + + ctr = binary.BigEndian.Uint32(nonce[len(nonce)-4:]) if lost == -1 { // ignore the very first packet in the stream lost = 0 @@ -460,12 +468,14 @@ func main() { return } <-LoggerReady + var ciph *chacha20.Cipher + var macKey [32]byte + var mac *poly1305.MAC + tag := make([]byte, poly1305.TagSize) buf := make([]byte, 2*vors.FrameLen) pcm := make([]int16, vors.FrameLen) - nonce := make([]byte, ciph.NonceSize()) + nonce := make([]byte, 12) nonce[len(nonce)-4] = sid - ctr := nonce[len(nonce)-3:] - sidAndCtr := nonce[len(nonce)-4:] var pkt []byte var n, i int for { @@ -483,13 +493,29 @@ func main() { if vad != 0 && vors.RMS(pcm) < vad { continue } - incr(ctr) - copy(buf, sidAndCtr) n, err = opusEnc.Encode(pcm, buf[4:]) if err != nil { log.Fatal(err) } - pkt = ciph.Seal(buf[:4], nonce, buf[4:4+n], []byte{sid}) + + incr(nonce[len(nonce)-3:]) + copy(buf, nonce[len(nonce)-4:]) + ciph, err = chacha20.NewUnauthenticatedCipher(keyOur, 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 { + log.Fatal(err) + } + mac.Sum(tag[:0]) + copy(buf[4+n:], tag[:TagLen]) + pkt = buf[:4+n+TagLen] + OurStats.pkts++ OurStats.bytes += uint64(len(pkt)) OurStats.last = time.Now() diff --git a/doc/proto.texi b/doc/proto.texi index 10fb381..6ebaca7 100644 --- a/doc/proto.texi +++ b/doc/proto.texi @@ -11,13 +11,13 @@ dropped. 24-bit counter is long enough for very long talk sessions. Each packet is encrypted with ChaCha20-Poly1305. Key is generated during handshake procedure with the server and is shared among other participants. -Packet counter is used as a nonce. Stream identifier is additional -authenticated data. +Stream identifier with packet counter are used as a nonce. Only 64-bits +of Poly1305 are used. It is tuned for 32Kbps bandwidth. 24Kbps should be enough, but 40B of -IPv6 header, +16B of Poly1305 authentication tag, +4B of stream -identifier with the counter, +8B of UDP header for 50pps means ~28Kbps -of bandwidth only for overhead transmission. +IPv6 header, +8B of Poly1305 authentication tag, +4B of stream +identifier with the counter, +8B of UDP header for 50pps means also +24Kbps of bandwidth only for overhead transmission. Each client handshakes with the server over TCP protocol using TLS 1.3 with curve25519 key-agreement protocol. @command{vors-keygen} generates