"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 {
}
}
- 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()
if err != nil {
log.Fatal(err)
}
+ keyCiph, keyMAC := key[:32], key[32:]
stream := &Stream{
name: name,
in: make(chan []byte, 1<<10),
}
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)
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:])
log.Println("wrong addr:", from)
continue
}
- if n <= 4+vors.TagLen {
+ if n <= 4+siphash.Size {
log.Println("too small:", n)
continue
}
}
<-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)
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))
"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 (
}
{
- 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:])
}
{
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 {
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
package main
import (
+ "hash"
"log/slog"
"net"
"sync"
sid byte
addr *net.UDPAddr
key []byte
+ mac hash.Hash
stats *Stats
room *Room
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.
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
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
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.
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
+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=
"github.com/flynn/noise"
)
-const NoisePrologue = "VoRS v1"
+const NoisePrologue = "VoRS v2"
var NoiseCipherSuite = noise.NewCipherSuite(
noise.DH25519,
)
const (
- TagLen = 8
-
CmdPing = "PING"
CmdPong = "PONG"
CmdAdd = "ADD"
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