Passphrase-based peer rendezvous for iroh-gossip swarms over the public Mainline DHT. Two nodes that share a passphrase + application salt find each other and join the same gossip topic — no bootstrap server, no prior knowledge of peer addresses. The algorithm is a clock-free variant of HyParView-style partition healing on BEP 44 mutable items, with K-sharded slots, logical aging with random-tie eviction, vouching of up to V neighbors at age 1, and a two-layer AEAD record envelope. Full spec in PROTOCOL.md. Ships with: * 119 tests (unit / property / integration / doctest) all green * 100% mutation-testing catch rate (cargo-mutants) * cargo-fuzz harnesses for the parse surface * Kani proofs of no-UB / no-panic on crypto::epoch_of and wire::decode_entries across their full symbolic input spaces * Stateright distributed-system model: safety invariants S1 + S2 proved exhaustively for N ≤ 6, K ≤ 3; liveness witness under weak fairness * Miri green on the synchronous modules * cargo-deny + cargo-audit clean * criterion benches * Full-matrix CI (Linux + macOS + Windows × stable + MSRV 1.93.0) * Dedicated nightly lane for Miri / mutation / fuzz / Kani / Stateright Pinned iroh 0.98.x. License MIT. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
110 lines
5.0 KiB
Markdown
110 lines
5.0 KiB
Markdown
# Security Policy
|
|
|
|
## Reporting a vulnerability
|
|
|
|
Email **steve@waits.net** with the subject prefix `[iroh-gossip-rendezvous
|
|
security]`. Please include:
|
|
|
|
- A description of the vulnerability and its impact.
|
|
- Steps to reproduce, ideally with a minimal test case.
|
|
- Your suggestion for a fix or mitigation, if you have one.
|
|
|
|
I'll acknowledge receipt within 5 business days and aim to publish a fix
|
|
(or a detailed mitigation advisory) within 30 days. Please do not file a
|
|
public issue for security vulnerabilities until a fix is available.
|
|
|
|
## Supported versions
|
|
|
|
Only the latest minor release receives security fixes. Older `0.x` lines
|
|
become unsupported once a newer minor version ships. `1.0` will establish
|
|
a longer support window.
|
|
|
|
## Threat model
|
|
|
|
The crate's design assumes:
|
|
|
|
- The public Mainline DHT is **untrusted**: any node can read any slot,
|
|
publish spoofed records under keys it controls, or drop / replay puts.
|
|
Signature verification prevents spoofing; last-writer-wins semantics
|
|
tolerates drop/replay.
|
|
- The **passphrase** is a shared secret among members. Anyone with the
|
|
passphrase can participate in the swarm, decrypt DHT records, and sign
|
|
their own slot writes.
|
|
- The iroh-gossip layer **itself** provides point-to-point TLS between
|
|
gossip members. Application payload broadcast over the swarm is visible
|
|
to every gossip member.
|
|
|
|
### What the crate protects against
|
|
|
|
- **Passive DHT eavesdroppers**: record plaintext (peer IDs, ages) is
|
|
AEAD-encrypted under a key derived from the passphrase. An observer
|
|
sees only opaque ciphertext, a public key, and a monotonic sequence
|
|
number.
|
|
- **Active DHT attackers who don't know the passphrase**: cannot produce
|
|
valid signed puts; their malformed records are rejected on read.
|
|
- **Cryptographic correctness of the core primitives**: HKDF-SHA256 for
|
|
key derivation, Ed25519 for signing, ChaCha20-Poly1305 for AEAD, all
|
|
from the audited [RustCrypto] stack.
|
|
- **Per-record key isolation**: compromise of a single record's one-time
|
|
key (`k_r`) leaks only that record — the wrapper master key `W_k` stays
|
|
protected.
|
|
|
|
### What the crate does **not** protect against
|
|
|
|
- **Passphrase compromise.** The passphrase is the sole secret. Anyone
|
|
obtaining it can decrypt every past and future DHT record, forge slot
|
|
writes, and impersonate members. There is no forward secrecy: an
|
|
adversary who captures DHT traffic and later obtains the passphrase can
|
|
decrypt the archive.
|
|
- **Post-compromise security against a former member.** A removed member
|
|
who still has the passphrase retains full read/write access. Revocation
|
|
requires an out-of-band passphrase rotation.
|
|
- **Metadata confidentiality on the DHT.** Observers see: the slot
|
|
addresses (public, deterministically derived from the passphrase), record
|
|
sizes, write frequencies, and sequence numbers. These reveal the rough
|
|
number of active members, the write rate, and the app salt's identity
|
|
(via slot-address fingerprinting if the adversary guesses app salts).
|
|
- **Gossip-layer payload confidentiality.** Once the gossip topic is
|
|
joined, application payloads broadcast over iroh-gossip are visible to
|
|
every member. Payload encryption is out of scope; layer MLS / RFC 9420
|
|
on top if needed.
|
|
- **Denial of service on the Mainline DHT.** An adversary with heavy DHT
|
|
resources can crowd out slots or drop puts. The K-sharded design
|
|
mitigates this (§7 of PROTOCOL.md) but does not eliminate it.
|
|
|
|
### Non-goals
|
|
|
|
- **MLS / post-compromise security** — future work; separate crate if built.
|
|
- **Anonymity.** Mainline DHT participation is observable; adversaries who
|
|
correlate DHT queries with network metadata can de-anonymize members.
|
|
- **Traffic analysis resistance.** Same caveat.
|
|
|
|
## Cryptographic primitives and key derivation
|
|
|
|
- **Key agreement**: None. Keys are deterministically derived from the
|
|
passphrase + app_salt via HKDF-SHA256.
|
|
- **Slot signing**: Ed25519 via [`ed25519-dalek`]. Each slot has its own
|
|
signing key, derived via HKDF info string `"sk25519:" || be64(slot_k)`.
|
|
- **Wrapper master key**: 32 bytes per slot, HKDF info string
|
|
`"wrap:" || be64(slot_k)`.
|
|
- **Per-epoch wrapper**: `W_k^(e) = HKDF-Expand(W_k, "epoch" || be64(e))`
|
|
where `e = floor(seq / E)` (default `E = 64`). Epoch rotation provides
|
|
coarse-grained key hygiene but not forward secrecy.
|
|
- **Per-record AEAD**: a fresh 32-byte one-time key `k_r`, wrapped under
|
|
`W_k^(e)` with a random 12-byte nonce; the record body is AEAD-encrypted
|
|
under `k_r` with a fixed all-zero nonce (safe because `k_r` is one-shot
|
|
and discarded after encryption).
|
|
- **Zeroization**: `k_r`, the derived `W_k^(e)`, and any intermediate
|
|
HKDF buffers containing passphrase bytes are [`zeroize`]d after use.
|
|
|
|
## Supply chain
|
|
|
|
- All crypto dependencies are from the [RustCrypto] organization.
|
|
- CI runs `cargo deny check` and `cargo audit` on every push; known
|
|
transitive `unmaintained` advisories are documented in `deny.toml`.
|
|
- `cargo-semver-checks` guards public API stability on PRs.
|
|
|
|
[RustCrypto]: https://github.com/RustCrypto
|
|
[`ed25519-dalek`]: https://docs.rs/ed25519-dalek
|
|
[`zeroize`]: https://docs.rs/zeroize
|