Protocol v2: X25519 handshake, sequence-number nonces, hardened transport

Red-team review of the (byte-exact) v1 port found replay, cross-session
frame portability, pre-auth allocation amplification, and a symmetric
key that let either peer forge as itself — flaws inherent to the v1
wire format. v2 closes them:

- fixed 69-byte OVX2 hello; server validates before replying; v1 and
  wrong-version peers see an immediate close
- HKDF(ephemeral X25519 DH, PSK, transcript) -> direction-separated
  session keys; forward secrecy per connection
- strictly increasing u64 sequence nonces: any replay or reorder drops
  the connection (regression tests in tests/protocol_v2.rs)
- Config: message-size cap (64 KiB default), connection semaphore,
  idle/body-stall/handshake/send timeouts on both roles (client-side
  timers retire the old no-client-timeouts quirk)
- message processing inline per connection; user callbacks panic-
  isolated; poison-tolerant mutexes; demo key refused in release builds
- PROTOCOL-V2.md supersedes PARITY-CONTRACT.md (retired, kept as port
  history); Cereal payload encoding is unchanged

Payload serialization remains byte-exact with the original C++ (the
golden scrypt/cereal vectors still govern). Zero unsafe. Verified live:
server<->client handshake, encrypted echo, clean teardown.

Cuts over from the C++ implementation, which is deleted (phase 5 of the
port contract); it remains in the original gitea repository.
This commit is contained in:
2026-09-30 06:56:52 -04:00
parent 5cbac8037d
commit c614d8345f
13 changed files with 1325 additions and 352 deletions
+6 -2
View File
@@ -1,13 +1,17 @@
[package]
name = "omnivox"
version = "0.13.0"
version = "0.14.0"
edition = "2024"
license = "GPL-3.0-or-later"
description = "Encrypted async TCP client-server messaging (ChaCha20-Poly1305), Rust port of the OmniVox C++ library"
description = "Encrypted async TCP client-server messaging: X25519 handshake, ChaCha20-Poly1305, replay-proof sequencing (OmniVox protocol v2)"
[dependencies]
chacha20poly1305 = "0.10"
getrandom = "0.3"
hkdf = "0.12"
rand_core = { version = "0.6", features = ["getrandom"] }
sha2 = "0.10"
x25519-dalek = { version = "2", features = ["zeroize"] }
scrypt = "0.11"
thiserror = "2"
tokio = { version = "1", features = ["rt-multi-thread", "net", "io-util", "time", "sync", "macros"] }