import (
"bufio"
"bytes"
+ "crypto/subtle"
"crypto/tls"
"crypto/x509"
"encoding/binary"
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
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)
}
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)
}
}
- 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
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 {
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()
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