From 519ba049c93189664d14d2828c93351aabae927f1d9774eb50eba4d25aef0c9b Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Thu, 25 Sep 2025 13:18:02 +0300 Subject: [PATCH] Proper HKDF usage --- cmd/client/audio.go | 2 +- cmd/client/gui.go | 2 +- cmd/client/main.go | 6 +-- cmd/client/stats.go | 2 +- cmd/keygen/main.go | 4 +- cmd/server/main.go | 6 +-- cmd/server/peer.go | 2 +- cmd/server/room.go | 2 +- cmd/vad/main.go | 2 +- doc/PQHS | 44 +++++++++++-------- doc/Protocol | 4 +- go.mod | 2 +- internal/var.go | 2 +- internal/version.go | 2 +- pqhs/client.go | 8 ++-- pqhs/const.go | 12 ++--- pqhs/hs_test.go | 2 +- pqhs/key.go | 2 +- pqhs/mceliece6960119/fft.go | 2 +- pqhs/mceliece6960119/internal/fft_const.go | 4 +- pqhs/mceliece6960119/internal/powers.go | 4 +- pqhs/mceliece6960119/mceliece.go | 4 +- pqhs/mceliece6960119/pk_gen.go | 2 +- pqhs/server.go | 8 ++-- .../kem/ntruprime/sntrup761/ntruprime.go | 8 ++-- pqhs/sntrup761/pke/ntruprime/kem/kem.go | 2 +- .../ntruprime/kem/schemes/sntrup/schemes.go | 4 +- pqhs/state.go | 10 +++-- 28 files changed, 82 insertions(+), 72 deletions(-) diff --git a/cmd/client/audio.go b/cmd/client/audio.go index 57b3a9c..d77c712 100644 --- a/cmd/client/audio.go +++ b/cmd/client/audio.go @@ -19,7 +19,7 @@ import ( "log" "go.stargrave.org/opus/v2" - vors "go.stargrave.org/vors/v5/internal" + vors "go.stargrave.org/vors/v6/internal" ) func newOpusEnc() *opus.Encoder { diff --git a/cmd/client/gui.go b/cmd/client/gui.go index cc7a253..e70216d 100644 --- a/cmd/client/gui.go +++ b/cmd/client/gui.go @@ -21,7 +21,7 @@ import ( "sort" "github.com/jroimartin/gocui" - vors "go.stargrave.org/vors/v5/internal" + vors "go.stargrave.org/vors/v6/internal" ) var ( diff --git a/cmd/client/main.go b/cmd/client/main.go index 56ca240..0844a65 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -37,8 +37,8 @@ import ( "github.com/dchest/siphash" "github.com/jroimartin/gocui" "go.stargrave.org/opus/v2" - vors "go.stargrave.org/vors/v5/internal" - "go.stargrave.org/vors/v5/pqhs" + vors "go.stargrave.org/vors/v6/internal" + "go.stargrave.org/vors/v6/pqhs" "golang.org/x/crypto/chacha20poly1305" ) @@ -209,7 +209,7 @@ Magenta "S" means that peer is locally muted.`) var txKey, rxKey, keyCiphOur, keyMACOur []byte var txAEAD, rxAEAD cipher.AEAD - keys := hs.Binding(3*chacha20poly1305.KeySize + vors.SipHash24KeySize) + keys := hs.Keymat(3*chacha20poly1305.KeySize + vors.SipHash24KeySize) txKey, keys = keys[:chacha20poly1305.KeySize], keys[chacha20poly1305.KeySize:] rxKey, keys = keys[:chacha20poly1305.KeySize], keys[chacha20poly1305.KeySize:] keyCiphOur, keyMACOur = keys[:vors.ChaCha20KeySize], keys[vors.ChaCha20KeySize:] diff --git a/cmd/client/stats.go b/cmd/client/stats.go index cba958a..44786d8 100644 --- a/cmd/client/stats.go +++ b/cmd/client/stats.go @@ -24,7 +24,7 @@ import ( "github.com/dustin/go-humanize" "github.com/jroimartin/gocui" - vors "go.stargrave.org/vors/v5/internal" + vors "go.stargrave.org/vors/v6/internal" ) type Stats struct { diff --git a/cmd/keygen/main.go b/cmd/keygen/main.go index ea611f0..ef98a95 100644 --- a/cmd/keygen/main.go +++ b/cmd/keygen/main.go @@ -8,8 +8,8 @@ import ( "log" "os" - vors "go.stargrave.org/vors/v5/internal" - "go.stargrave.org/vors/v5/pqhs" + vors "go.stargrave.org/vors/v6/internal" + "go.stargrave.org/vors/v6/pqhs" ) func usage() { diff --git a/cmd/server/main.go b/cmd/server/main.go index 2087dd5..ac4cbb8 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -33,8 +33,8 @@ import ( "github.com/dchest/siphash" "github.com/jroimartin/gocui" - vors "go.stargrave.org/vors/v5/internal" - "go.stargrave.org/vors/v5/pqhs" + vors "go.stargrave.org/vors/v6/internal" + "go.stargrave.org/vors/v6/pqhs" "golang.org/x/crypto/chacha20poly1305" ) @@ -94,7 +94,7 @@ func newPeer(conn *net.TCPConn) { } { var rxKey, txKey []byte - keys := hs.Binding(3*chacha20poly1305.KeySize + vors.SipHash24KeySize) + keys := hs.Keymat(3*chacha20poly1305.KeySize + vors.SipHash24KeySize) rxKey, keys = keys[:chacha20poly1305.KeySize], keys[chacha20poly1305.KeySize:] txKey, peer.key = keys[:chacha20poly1305.KeySize], keys[chacha20poly1305.KeySize:] peer.mac = siphash.New(peer.key[vors.ChaCha20KeySize:]) diff --git a/cmd/server/peer.go b/cmd/server/peer.go index dbb5542..d37ed55 100644 --- a/cmd/server/peer.go +++ b/cmd/server/peer.go @@ -8,7 +8,7 @@ import ( "sync" "time" - vors "go.stargrave.org/vors/v5/internal" + vors "go.stargrave.org/vors/v6/internal" ) var ( diff --git a/cmd/server/room.go b/cmd/server/room.go index f55e596..4ec1132 100644 --- a/cmd/server/room.go +++ b/cmd/server/room.go @@ -8,7 +8,7 @@ import ( "time" "github.com/dustin/go-humanize" - vors "go.stargrave.org/vors/v5/internal" + vors "go.stargrave.org/vors/v6/internal" ) var ( diff --git a/cmd/vad/main.go b/cmd/vad/main.go index b1ae146..c1c29cf 100644 --- a/cmd/vad/main.go +++ b/cmd/vad/main.go @@ -23,7 +23,7 @@ import ( "os" "strconv" - vors "go.stargrave.org/vors/v5/internal" + vors "go.stargrave.org/vors/v6/internal" ) func usage() { diff --git a/doc/PQHS b/doc/PQHS index 128147c..245de87 100644 --- a/doc/PQHS +++ b/doc/PQHS @@ -1,51 +1,57 @@ Server has a long-term static Classic McEliece 6960-119 and X25519 keypairs. They are transferred to client outside the connection. +hash = SHAKE256 + Client: * has: serverStaticPubMcEliece, serverStaticPubX25519 * clientEphPrvX25519, clientEphPubX25519 = Generate() * ctMcElice, ssMcEliece = Encapsulate(serverStaticPubMcEliece) - * H = SHAKE256("VoRS v5") - * H = SHAKE256(H || serverStaticPubMcEliece || serverStaticPubX25519) - * H = SHAKE256(H || ctMcElice) - * CK = HKDF-Extract(SHAKE256, ikm=ssMcEliece, salt="") - * k = HKDF-Expand(SHAKE256, prk=CK, info="VoRS v5 client x25519") + * H = hash("VoRS v6") + * H = hash(H || serverStaticPubMcEliece || serverStaticPubX25519) + * H = hash(H || ctMcElice) + * CK = HKDF-Expand(prk=HKDF-Extract(salt="", ikm=ssMcEliece), + info="VoRS v6 ck") + * k = HKDF-Expand(prk=CK, info="VoRS v6 client x25519") * ctX25519 = ChaCha20-Poly1305(k, nonce=0, ad=H, pt=clientEphPubX25519) - * H = SHAKE256(H || ctX25519) + * H = hash(H || ctX25519) * ssX25519 = X25519(clientEphPrvX25519, serverStaticPubX25519) - * CK = HKDF-Extract(SHAKE256, ikm=ssX25519, salt=CK) + * CK = HKDF-Expand(prk=HKDF-Extract(salt=CK, ikm=ssX25519), + info="VoRS v6 ck") * sends: ctMcElice || ctX25519 Server: * ... * serverEphPrvX25519, serverEphPubX25519 = Generate() - * k = HKDF-Expand(SHAKE256, prk=CK, info="VoRS v5 server x25519") + * k = HKDF-Expand(prk=CK, info="VoRS v6 server x25519") * ctX25519 = ChaCha20-Poly1305(k, nonce=0, ad=H, pt=serverEphPubX25519) - * H = SHAKE256(H || ctX25519) + * H = hash(H || ctX25519) * ssX25519 = X25519(serverEphPrvX25519, clientEphPubX25519) - * CK = HKDF-Extract(SHAKE256, ikm=ssX25519, salt=CK) + * CK = HKDF-Expand(prk=HKDF-Extract(salt=CK, ikm=ssX25519), + info="VoRS v6 ck") * serverEphPrvSNTRUP761, serverEphPubSNTRUP761 = Generate() - * k = HKDF-Expand(SHAKE256, prk=CK, info="VoRS v5 server sntrup761") + * k = HKDF-Expand(prk=CK, info="VoRS v6 server sntrup761") * ctSNTRUP = ChaCha20-Poly1305(k, nonce=0, ad=H, pt=serverEphPubSNTRUP761) - * H = SHAKE256(H || ctSNTRUP) + * H = hash(H || ctSNTRUP) * sends: ctX25519 || ctSNTRUP Client: * has: prefinish message payload * ... * ctSNTRUP, ssSNTRUP = Encapsulate(serverEphPubSNTRUP761) - * k = HKDF-Expand(SHAKE256, prk=CK, info="VoRS v5 client sntrup761") + * k = HKDF-Expand(prk=CK, info="VoRS v6 client sntrup761") * ctSNTRUP = ChaCha20-Poly1305(k, nonce=0, ad=H, pt=ctSNTRUP) - * H = SHAKE256(H || ctSNTRUP) - * CK = HKDF-Extract(SHAKE256, ikm=ssSNTRUP, salt=CK) - * k = HKDF-Expand(SHAKE256, prk=CK, info="VoRS v5 client prefinish") + * H = hash(H || ctSNTRUP) + * CK = HKDF-Expand(prk=HKDF-Extract(salt=CK, ikm=ssSNTRUP), + info="VoRS v6 ck") + * k = HKDF-Expand(prk=CK, info="VoRS v6 client prefinish") * ctPrefinish = ChaCha20-Poly1305(k, nonce=0, ad=H, pt=prefinish) - * H = SHAKE256(H || ctPrefinish) + * H = hash(H || ctPrefinish) * sends: ctPrefinish Server: * ... Both: - clientChaPolyKey, serverChaPolyKey, VoIPKey = - HKDF-Expand(SHAKE256, ikm=CK, salt=H) + clientChaPolyKey, serverChaPolyKey, VoIPKey = HKDF-Expand( + prk=CK, info="VoRS v6 keymat") diff --git a/doc/Protocol b/doc/Protocol index 2537487..a3ca416 100644 --- a/doc/Protocol +++ b/doc/Protocol @@ -40,7 +40,7 @@ ones. With HKDF as a KDF and SHAKE as a hash function. NS(NS(arg0) || NS(arg1) || ...) => http://cr.yp.to/proto/netstrings.txt Netstring -* Client sends NS("VoRS v5") to the socket. Just a magic number. +* Client sends NS("VoRS v6") to the socket. Just a magic number. * Then it performs [PQHS]. @@ -67,8 +67,6 @@ ones. With HKDF as a KDF and SHAKE as a hash function. * Server replies with ["SID", SID], where SID is single byte stream number client must use. -TODO - * ["PING"] and ["PONG"] messages are then sent every ten seconds as a heartbeat. S <- C : hello diff --git a/go.mod b/go.mod index 20341b5..89befde 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module go.stargrave.org/vors/v5 +module go.stargrave.org/vors/v6 go 1.24.0 diff --git a/internal/var.go b/internal/var.go index cd35424..25630cd 100644 --- a/internal/var.go +++ b/internal/var.go @@ -6,7 +6,7 @@ import ( ) const ( - Magic = "VoRS v5" + Magic = "VoRS v6" CmdErr = "ERR" CmdCookie = "COOKIE" CmdSID = "SID" diff --git a/internal/version.go b/internal/version.go index a31d3e5..acea42b 100644 --- a/internal/version.go +++ b/internal/version.go @@ -3,7 +3,7 @@ package internal import "runtime" const ( - Version = "4.0.0" + Version = "6.0.0" Warranty = `Copyright (C) 2024-2025 Sergey Matveev This program is free software: you can redistribute it and/or modify diff --git a/pqhs/client.go b/pqhs/client.go index 1c4dd16..b76a8cb 100644 --- a/pqhs/client.go +++ b/pqhs/client.go @@ -5,10 +5,10 @@ import ( "crypto/rand" "crypto/sha3" - vors "go.stargrave.org/vors/v5/internal" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119" - sntrup761kem "go.stargrave.org/vors/v5/pqhs/sntrup761/kem" - sntrup761 "go.stargrave.org/vors/v5/pqhs/sntrup761/kem/ntruprime/sntrup761" + vors "go.stargrave.org/vors/v6/internal" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119" + sntrup761kem "go.stargrave.org/vors/v6/pqhs/sntrup761/kem" + sntrup761 "go.stargrave.org/vors/v6/pqhs/sntrup761/kem/ntruprime/sntrup761" "golang.org/x/crypto/chacha20poly1305" ) diff --git a/pqhs/const.go b/pqhs/const.go index c5df1e2..94b60f4 100644 --- a/pqhs/const.go +++ b/pqhs/const.go @@ -1,9 +1,11 @@ package pqhs const ( - CtxClientX25519 = "VoRS v5 client x25519" - CtxServerX25519 = "VoRS v5 server x25519" - CtxServerSNTRUP761 = "VoRS v5 server sntrup761" - CtxClientSNTRUP761 = "VoRS v5 client sntrup761" - CtxClientPrefinish = "VoRS v5 client prefinish" + CtxClientX25519 = "VoRS v6 client x25519" + CtxServerX25519 = "VoRS v6 server x25519" + CtxServerSNTRUP761 = "VoRS v6 server sntrup761" + CtxClientSNTRUP761 = "VoRS v6 client sntrup761" + CtxClientPrefinish = "VoRS v6 client prefinish" + CtxCK = "VoRS v6 ck" + CtxKeymat = "VoRS v6 keymat" ) diff --git a/pqhs/hs_test.go b/pqhs/hs_test.go index c26e8de..e42fe11 100644 --- a/pqhs/hs_test.go +++ b/pqhs/hs_test.go @@ -38,7 +38,7 @@ func TestHandshake(t *testing.T) { if string(prefinish) != "whatever" { t.Fatal("prefinish differs") } - if !bytes.Equal(c.Binding(1234), s.Binding(1234)) { + if !bytes.Equal(c.Keymat(1234), s.Keymat(1234)) { t.Fatal("bindings differs") } } diff --git a/pqhs/key.go b/pqhs/key.go index 5a6003e..0beff66 100644 --- a/pqhs/key.go +++ b/pqhs/key.go @@ -4,7 +4,7 @@ import ( "crypto/ecdh" "crypto/rand" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119" ) func KeyGen() ( diff --git a/pqhs/mceliece6960119/fft.go b/pqhs/mceliece6960119/fft.go index 1d117de..0c29b5d 100644 --- a/pqhs/mceliece6960119/fft.go +++ b/pqhs/mceliece6960119/fft.go @@ -5,7 +5,7 @@ package mceliece6960119 -import "go.stargrave.org/vors/v5/pqhs/mceliece6960119/internal" +import "go.stargrave.org/vors/v6/pqhs/mceliece6960119/internal" func fft(out *[exponent][gfBits]uint64, in *[2][gfBits]uint64) { radixConversions(in) diff --git a/pqhs/mceliece6960119/internal/fft_const.go b/pqhs/mceliece6960119/internal/fft_const.go index 476eb3f..69ae78d 100644 --- a/pqhs/mceliece6960119/internal/fft_const.go +++ b/pqhs/mceliece6960119/internal/fft_const.go @@ -1,8 +1,8 @@ package internal import ( - "go.stargrave.org/vors/v5/pqhs/mceliece6960119/math/gf2e12" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119/math/gf2e13" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119/math/gf2e12" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119/math/gf2e13" ) var ButterfliesReversal4096 = [64]byte{ diff --git a/pqhs/mceliece6960119/internal/powers.go b/pqhs/mceliece6960119/internal/powers.go index 2d4c391..19a5455 100644 --- a/pqhs/mceliece6960119/internal/powers.go +++ b/pqhs/mceliece6960119/internal/powers.go @@ -1,8 +1,8 @@ package internal import ( - "go.stargrave.org/vors/v5/pqhs/mceliece6960119/math/gf2e12" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119/math/gf2e13" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119/math/gf2e12" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119/math/gf2e13" ) var Powers4096 = [64][gf2e12.Bits]uint64{ diff --git a/pqhs/mceliece6960119/mceliece.go b/pqhs/mceliece6960119/mceliece.go index eba3b34..37bc9a1 100644 --- a/pqhs/mceliece6960119/mceliece.go +++ b/pqhs/mceliece6960119/mceliece.go @@ -20,8 +20,8 @@ import ( "fmt" "io" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119/internal" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119/math/gf2e13" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119/internal" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119/math/gf2e13" ) const ( diff --git a/pqhs/mceliece6960119/pk_gen.go b/pqhs/mceliece6960119/pk_gen.go index a505a8a..15b9626 100644 --- a/pqhs/mceliece6960119/pk_gen.go +++ b/pqhs/mceliece6960119/pk_gen.go @@ -5,7 +5,7 @@ package mceliece6960119 -import "go.stargrave.org/vors/v5/pqhs/mceliece6960119/internal" +import "go.stargrave.org/vors/v6/pqhs/mceliece6960119/internal" const exponent = 128 diff --git a/pqhs/server.go b/pqhs/server.go index 2c16506..c4eb390 100644 --- a/pqhs/server.go +++ b/pqhs/server.go @@ -4,10 +4,10 @@ import ( "crypto/ecdh" "crypto/rand" - vors "go.stargrave.org/vors/v5/internal" - "go.stargrave.org/vors/v5/pqhs/mceliece6960119" - sntrup761kem "go.stargrave.org/vors/v5/pqhs/sntrup761/kem" - sntrup761 "go.stargrave.org/vors/v5/pqhs/sntrup761/kem/ntruprime/sntrup761" + vors "go.stargrave.org/vors/v6/internal" + "go.stargrave.org/vors/v6/pqhs/mceliece6960119" + sntrup761kem "go.stargrave.org/vors/v6/pqhs/sntrup761/kem" + sntrup761 "go.stargrave.org/vors/v6/pqhs/sntrup761/kem/ntruprime/sntrup761" "golang.org/x/crypto/chacha20poly1305" ) diff --git a/pqhs/sntrup761/kem/ntruprime/sntrup761/ntruprime.go b/pqhs/sntrup761/kem/ntruprime/sntrup761/ntruprime.go index b03c744..1c2625d 100644 --- a/pqhs/sntrup761/kem/ntruprime/sntrup761/ntruprime.go +++ b/pqhs/sntrup761/kem/ntruprime/sntrup761/ntruprime.go @@ -13,10 +13,10 @@ import ( "crypto/sha512" "io" - "go.stargrave.org/vors/v5/pqhs/sntrup761/kem" - "go.stargrave.org/vors/v5/pqhs/sntrup761/kem/ntruprime/internal" - sntrupKem "go.stargrave.org/vors/v5/pqhs/sntrup761/pke/ntruprime/kem" - ntrup "go.stargrave.org/vors/v5/pqhs/sntrup761/pke/ntruprime/sntrup761" + "go.stargrave.org/vors/v6/pqhs/sntrup761/kem" + "go.stargrave.org/vors/v6/pqhs/sntrup761/kem/ntruprime/internal" + sntrupKem "go.stargrave.org/vors/v6/pqhs/sntrup761/pke/ntruprime/kem" + ntrup "go.stargrave.org/vors/v6/pqhs/sntrup761/pke/ntruprime/sntrup761" ) type ( diff --git a/pqhs/sntrup761/pke/ntruprime/kem/kem.go b/pqhs/sntrup761/pke/ntruprime/kem/kem.go index 2b6309a..297f756 100644 --- a/pqhs/sntrup761/pke/ntruprime/kem/kem.go +++ b/pqhs/sntrup761/pke/ntruprime/kem/kem.go @@ -4,7 +4,7 @@ package kem import ( "io" - "go.stargrave.org/vors/v5/pqhs/sntrup761/kem" + "go.stargrave.org/vors/v6/pqhs/sntrup761/kem" ) // A Scheme represents a specific instance of a NTRU PRIME KEM. diff --git a/pqhs/sntrup761/pke/ntruprime/kem/schemes/sntrup/schemes.go b/pqhs/sntrup761/pke/ntruprime/kem/schemes/sntrup/schemes.go index b0b411a..38fa338 100644 --- a/pqhs/sntrup761/pke/ntruprime/kem/schemes/sntrup/schemes.go +++ b/pqhs/sntrup761/pke/ntruprime/kem/schemes/sntrup/schemes.go @@ -10,8 +10,8 @@ package sntrupSchemes import ( "strings" - "go.stargrave.org/vors/v5/pqhs/sntrup761/kem/ntruprime/sntrup761" - "go.stargrave.org/vors/v5/pqhs/sntrup761/pke/ntruprime/kem" + "go.stargrave.org/vors/v6/pqhs/sntrup761/kem/ntruprime/sntrup761" + "go.stargrave.org/vors/v6/pqhs/sntrup761/pke/ntruprime/kem" ) var allSchemes = [...]kem.Scheme{ diff --git a/pqhs/state.go b/pqhs/state.go index 35cbe65..bfc2353 100644 --- a/pqhs/state.go +++ b/pqhs/state.go @@ -30,6 +30,10 @@ func (state *SymmetricState) CK(key []byte) { if err != nil { panic(err) } + state.ck, err = hkdf.Expand(NewSHAKE256, state.ck, CtxCK, 64) + if err != nil { + panic(err) + } } func (state *SymmetricState) Seal(ctx string, data []byte) []byte { @@ -55,10 +59,10 @@ func (state *SymmetricState) Open(ctx string, ct []byte) (pt []byte, err error) return } -func (state *SymmetricState) Binding(l int) []byte { - binding, err := hkdf.Expand(NewSHAKE256, state.ck, string(state.h), l) +func (state *SymmetricState) Keymat(l int) []byte { + keymat, err := hkdf.Expand(NewSHAKE256, state.ck, CtxKeymat, l) if err != nil { panic(err) } - return binding + return keymat } -- 2.51.0