Track A protocol hardening for native-v1.
1. Protocol VERSION discipline (protocol.rs, native.rs, dosh-server.rs,
dosh-client.rs): bump protocol::VERSION to 2 and NATIVE_PROTOCOL_VERSION to 2.
Foreign wire-version datagrams now get a clear named "protocol version
mismatch - upgrade dosh" reject from the server instead of a silent timeout
(peek_foreign_wire_version + VERSION_MISMATCH_REASON). The native handshake's
plaintext protocol_version is also checked server-side before any crypto via
check_native_protocol_version, returning a typed ProtocolVersionMismatch.
2. Transport rekey (spec section 11/9): server rotates a client's traffic key
after rekey_after_packets OR rekey_after_secs (config knobs). Rotated keys are
derived independently of the handshake keys from the current key plus fresh
server CSPRNG material shipped confidentially in an AEAD Rekey packet
(derive_rekey_session_key). The previous epoch's key is retained briefly so
in-flight pre-rekey packets still decrypt (matched by session_key_id), and
stale-epoch packets are ignored, not fatal. Client handles Rekey/RekeyAck.
3. Connection migration (spec section 11): every authenticated, replay-accepted
packet now migrates client.endpoint to the new source address (input, resize,
ping, ack, resume, stream*, rekey-ack), not just resume. Ping now verifies its
AEAD tag before acting so migration cannot be spoofed.
4. Native auth rate limiting: per-source-IP token bucket
(native_auth_rate_limit_per_minute) enforced in handle_native_client_hello
BEFORE any X25519/Ed25519 work; over-limit hellos get a non-crypto reject.
5. Speed: replaced O(sessions x clients) per-packet linear scans with an O(1)
HashMap<conn_id, session_name> index in ServerState, kept in sync on every
client insert/remove (attach handlers, detach, remove_client, cleanup reap,
session-exit drain).
New config keys (ServerConfig, with defaults): rekey_after_packets = 100000,
rekey_after_secs = 3600.
Tests: version-mismatch reject (no hang), rate-limit flood reject, rekey
round-trip end-to-end + key-derivation unit tests, source-address migration,
client-index sync + cleanup purge. fmt and full test suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Track B (milestone 5 / §16 hardening):
- tests/hostile_network.rs: in-process UDP relay/shim between a test
client and a real dosh-server that can drop, reorder, and duplicate
datagrams, and rebind its upstream socket mid-session. Asserts:
session survives loss/reorder, duplicated/replayed Input is applied at
most once, stale packets after resume are ignored (not fatal), and a
client source-address change preserves the session.
- tests/parser_robustness.rs: deterministic randomized tests throwing
garbage at every reachable public parser (packet decode, from_body for
all protocol/native structs, authorized_keys, known_hosts, host-key
line, ssh-ed25519 blob, bootstrap, attach ticket); asserts none panic.
- fuzz/: standalone cargo-fuzz crate (own [workspace], non-default
member) with libfuzzer-sys harnesses for packet decode, from_body,
authorized_keys, known_hosts, handshake structs+verifiers, and attach
tickets. README documents the run command.
- .github/workflows/ci.yml: add fuzz-smoke job (nightly + cargo-fuzz,
short -max_total_time per target, tolerant if tooling unavailable);
existing fmt/test/build/bench steps unchanged.
cargo fmt --check and cargo test are green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>