Files
rustunnel/docs/superpowers/specs/2026-08-02-connection-key-rtun3-seed-design.md
bzuccaro 2bf8da739b
CI / cargo fmt (push) Canceled after 0s
CI / cargo clippy (macos-latest) (push) Canceled after 0s
CI / cargo clippy (ubuntu-latest) (push) Canceled after 0s
CI / cargo clippy (windows-latest) (push) Canceled after 0s
CI / cargo test (macos-latest) (push) Canceled after 0s
CI / cargo test (ubuntu-latest) (push) Canceled after 0s
CI / cargo test (windows-latest) (push) Canceled after 0s
CI / cargo build (macos-latest) (push) Canceled after 0s
CI / cargo build (ubuntu-latest) (push) Canceled after 0s
CI / cargo build (windows-latest) (push) Canceled after 0s
CI / cargo build --release (macos-latest) (push) Canceled after 0s
CI / cargo build --release (ubuntu-latest) (push) Canceled after 0s
CI / cargo build --release (windows-latest) (push) Canceled after 0s
CI / CLI smoke (macos-latest) (push) Canceled after 0s
CI / CLI smoke (ubuntu-latest) (push) Canceled after 0s
CI / CLI smoke (windows-latest) (push) Canceled after 0s
CI / Minimal E2E (macos-latest) (push) Canceled after 0s
CI / Minimal E2E (ubuntu-latest) (push) Canceled after 0s
CI / Minimal E2E (windows-latest) (push) Canceled after 0s
feat: replace connection keys with short seed-derived rtun3 keys
Connection keys are now a ~49-char seed (rtun3.) instead of a bundled
~1740-char certificate blob. Both endpoints deterministically derive an
identical Ed25519 CA from the seed and mint ephemeral server/client leaves
at startup (keyderive.rs); the app-layer auth token is derived from the seed.
Target is passed separately on connect (resocks-style).

- connkey.rs: rtun3 seed parse/format
- keyderive.rs: CA/server/client/token derivation
- keygen takes no args; connect requires --target
- remove miniz_oxide; TLS layer unchanged
- add determinism + key-based e2e + wrong-seed-rejected tests
- update wiki, README, design spec, CI smoke
2026-08-02 12:31:53 -06:00

3.2 KiB

Connection Keys as Seeds (rtun3) — Design

Goal

Replace the bundled-certificate connection key (rtun1/rtun2, ~1740 chars) with a ~49-char seed key. No certificates are ever shipped; both endpoints derive an identical CA from the seed and mint ephemeral leaf certs locally. Target is passed separately on connect (resocks-style). App-layer auth token is derived from the seed. Supersedes the rtun2 compression work entirely.

Motivation: the connection key should be trivial to copy/paste. Compression could only halve the size; seed-derivation (as in resocks/kbtls) makes it ~35x smaller.

Format

rtun3.<base64url(32-byte seed)>
  • Parse accepts only the rtun3. prefix; rtun1./rtun2. are rejected.
  • Payload must decode to exactly 32 bytes; all-zero seed is rejected.
  • keygen now takes no arguments — it just prints a fresh seed key.

Derivation (keyderive.rs)

Deterministic and identical on both endpoints (same seed → same result):

  • CA: Ed25519 keypair from the seed (Seed PKCS#8 → rcgen KeyPair), self-signed with fixed serial/CN/validity. Ed25519 signatures are deterministic, so both sides produce the byte-identical CA.
  • Server leaf (at listen): fresh random Ed25519 key signed by the derived CA, SANs = localhost/rustunnel/127.0.0.1 + bind IP + optional --advertise host.
  • Client leaf (at connect): fresh random Ed25519 key signed by the derived CA.
  • Auth token: sha256(seed || "rustunnel-auth-token") → first 16 bytes hex.

Derived PEM strings feed the existing build_server_config_from_pem / build_client_config_from_pem builders — the TLS layer is unchanged.

Command changes

  • keygen: rustunnel keygen → prints rtun3.<seed>. Removed --target and the CN flags (they only shaped bundled certs).
  • connect: --target is now required (there is no target in the key).
  • listen --connection-key $KEY: decode seed → derive server material + token.
  • Auto path (no certs/no key): generate fresh seed, print key + connect hint.
  • File-based mTLS (generate --out, --cert/--key/--ca-cert, --auth-token) unchanged.

Files

File Change
Cargo.toml Remove miniz_oxide (no direct ring dep needed; rcgen provides crypto)
src/connkey.rs Rewrite: seed key parse/format (rtun3)
src/keyderive.rs New: CA/server/client/token derivation
src/redact.rs is_connection_key detects rtun3.
src/main.rs Rework listen/connect/keygen seed flow
src/tunnel.rs Add key-based e2e tests

Testing

  • Seed round-trip, format, reject bad/length/zero/rtun1/rtun2.
  • Determinism: same seed → identical CA + token; different seed → different.
  • Server/client material share the same CA.
  • Key-based e2e: derived material establishes an mTLS tunnel with no files.
  • Wrong-seed connector is rejected even with the correct token.
  • Redaction prefix tests.

Docs

Update wiki (connection-keys.md, data-models.md, glossary.md, rustunnel-cli.md, getting-started.md, security.md, patterns-and-conventions.md, dependencies.md, overview/index.md) and README.

Out of scope

  • File-based mTLS, insecure_skip_tls_verify, performance tuning, key registry.