From dd73ae246970989d174280e54077fb305a83c295 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 10 Jul 2026 14:55:51 +0200 Subject: [PATCH] fix(host): fresh random per-session nonce salt instead of the static "pkf1" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every session sealed with the literal salt b"pkf1", so GCM nonce uniqueness (nonce = salt || sequence) rested ENTIRELY on the per-session key being fresh — correct today, but a single key-reuse bug anywhere in the handshake path would have meant immediate catastrophic nonce reuse instead of merely a wrong key. Random salt per session keeps the documented second line of defense real. The salt is negotiated via Welcome, so every deployed client just follows — no wire or compat change. Co-Authored-By: Claude Fable 5 --- crates/punktfunk-host/src/punktfunk1.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/punktfunk-host/src/punktfunk1.rs b/crates/punktfunk-host/src/punktfunk1.rs index 27600d95..ffd7545d 100644 --- a/crates/punktfunk-host/src/punktfunk1.rs +++ b/crates/punktfunk-host/src/punktfunk1.rs @@ -987,6 +987,13 @@ async fn serve_session( let mut key = [0u8; 16]; rand::thread_rng().fill_bytes(&mut key); + // Fresh per-session salt alongside the fresh key. GCM nonce uniqueness only *requires* one + // of the two to be unique per session (the nonce is salt || sequence under the session + // key), but a constant salt would make a key-reuse bug catastrophic instead of merely + // wrong — this keeps the second line of defense real. Negotiated via Welcome, so clients + // just follow. + let mut salt = [0u8; 4]; + rand::thread_rng().fill_bytes(&mut salt); let welcome = Welcome { abi_version: punktfunk_core::WIRE_VERSION, udp_port, @@ -1012,7 +1019,7 @@ async fn serve_session( shard_payload: mtu1500_shard_payload_for(peer.ip()) as u16, encrypt: true, key, - salt: *b"pkf1", + salt, frames: match source { Punktfunk1Source::Synthetic => frames, Punktfunk1Source::Virtual => 0, // unbounded — client streams until we close