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
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.
keygennow 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→ rcgenKeyPair), 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--advertisehost. - 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→ printsrtun3.<seed>. Removed--targetand the CN flags (they only shaped bundled certs).connect:--targetis 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.