diff options
Diffstat (limited to 'docs/rendezvous.md')
| -rw-r--r-- | docs/rendezvous.md | 72 |
1 files changed, 60 insertions, 12 deletions
diff --git a/docs/rendezvous.md b/docs/rendezvous.md index ac8e598..68b5df3 100644 --- a/docs/rendezvous.md +++ b/docs/rendezvous.md | |||
| @@ -18,11 +18,16 @@ A connection to a `.btf` service has three layers: | |||
| 18 | 1. **Discovery (Nostr).** The service publishes a signed descriptor as a Nostr | 18 | 1. **Discovery (Nostr).** The service publishes a signed descriptor as a Nostr |
| 19 | event. A caller fetches it by the service public key and verifies the | 19 | event. A caller fetches it by the service public key and verifies the |
| 20 | signature. The descriptor says which rendezvous relay the service is waiting | 20 | signature. The descriptor says which rendezvous relay the service is waiting |
| 21 | at and which encryption key to use. | 21 | at, which encryption key to use, and optionally which Tor hidden service can |
| 22 | be dialed directly. | ||
| 22 | 2. **Rendezvous (TCP).** Caller and service both connect out to the same relay | 23 | 2. **Rendezvous (TCP).** Caller and service both connect out to the same relay |
| 23 | and are paired by public key. Neither needs an inbound port or a public IP. | 24 | and are paired by public key. Neither needs an inbound port or a public IP. |
| 24 | After pairing the relay is a blind byte pipe. | 25 | After pairing the relay is a blind byte pipe. |
| 25 | 3. **Encrypted channel (X25519 + XChaCha20-Poly1305).** Over the paired pipe the | 26 | 3. **Direct onion fallback bypass.** If the descriptor carries a valid signed |
| 27 | `.onion:port` endpoint and the caller is running with Tor mode, the caller | ||
| 28 | tries that endpoint before using rendezvous. This removes the rendezvous | ||
| 29 | relay from the data path for peers that choose to run hidden services. | ||
| 30 | 4. **Encrypted channel (X25519 + XChaCha20-Poly1305).** Over the paired pipe the | ||
| 26 | caller and service run an ephemeral to static handshake and then exchange | 31 | caller and service run an ephemeral to static handshake and then exchange |
| 27 | authenticated frames. The relay sees only ciphertext. | 32 | authenticated frames. The relay sees only ciphertext. |
| 28 | 33 | ||
| @@ -107,6 +112,8 @@ The default rendezvous port is `8434`. | |||
| 107 | The descriptor is a JSON object, signed with the identity key over a domain | 112 | The descriptor is a JSON object, signed with the identity key over a domain |
| 108 | separated digest of its own fields. | 113 | separated digest of its own fields. |
| 109 | 114 | ||
| 115 | Legacy descriptor: | ||
| 116 | |||
| 110 | ```json | 117 | ```json |
| 111 | { | 118 | { |
| 112 | "v": 2, | 119 | "v": 2, |
| @@ -118,7 +125,22 @@ separated digest of its own fields. | |||
| 118 | } | 125 | } |
| 119 | ``` | 126 | ``` |
| 120 | 127 | ||
| 121 | The signed digest is: | 128 | Onion-capable descriptor: |
| 129 | |||
| 130 | ```json | ||
| 131 | { | ||
| 132 | "v": 3, | ||
| 133 | "pubkey": "<hex 32-byte x-only identity key>", | ||
| 134 | "enc": "<hex 32-byte X25519 public key>", | ||
| 135 | "meeting_node": "host:port", | ||
| 136 | "onion": "<56-char-v3-name>.onion:8433", | ||
| 137 | "created": <unix seconds>, | ||
| 138 | "sig": "<legacy v2 signature>", | ||
| 139 | "sig3": "<signature over the onion-capable digest>" | ||
| 140 | } | ||
| 141 | ``` | ||
| 142 | |||
| 143 | The legacy signed digest is: | ||
| 122 | 144 | ||
| 123 | ``` | 145 | ``` |
| 124 | digest = SHA256( ".btf-descriptor-v2" | 146 | digest = SHA256( ".btf-descriptor-v2" |
| @@ -128,17 +150,37 @@ digest = SHA256( ".btf-descriptor-v2" | |||
| 128 | || created (8 bytes, big endian) ) | 150 | || created (8 bytes, big endian) ) |
| 129 | ``` | 151 | ``` |
| 130 | 152 | ||
| 131 | The signature is a deterministic BIP340 Schnorr signature (no auxiliary | 153 | `sig` is kept on v3 descriptors so old nodes can still resolve and dial through |
| 132 | randomness) over `digest` by the identity key. | 154 | the rendezvous relay. The onion-capable digest is: |
| 155 | |||
| 156 | ``` | ||
| 157 | digest3 = SHA256( ".btf-descriptor-v3-onion" | ||
| 158 | || pubkey[32] | ||
| 159 | || enc (raw bytes of the string) | ||
| 160 | || meeting_node (raw bytes of the string) | ||
| 161 | || onion (raw bytes of the normalized string) | ||
| 162 | || created (8 bytes, big endian) ) | ||
| 163 | ``` | ||
| 164 | |||
| 165 | `sig` and `sig3` are deterministic BIP340 Schnorr signatures (no auxiliary | ||
| 166 | randomness) over their respective digests by the identity key. | ||
| 133 | 167 | ||
| 134 | **Verification** (untrusted input, must never throw): parse the JSON, require all | 168 | **Verification** (untrusted input, must never throw): parse the JSON, require all |
| 135 | fields with the right types, confirm `pubkey` equals the address being resolved, | 169 | fields with the right types, confirm `pubkey` equals the address being resolved, |
| 136 | recompute the digest, and verify the Schnorr signature. Any failure rejects the | 170 | recompute the digest, and verify the Schnorr signature. Any failure rejects the |
| 137 | descriptor. | 171 | descriptor. |
| 138 | 172 | ||
| 173 | If `onion` is present, readers accept it only when it is a normalized v3 onion | ||
| 174 | endpoint and `sig3` verifies. A bad or tampered onion field is ignored; the v2 | ||
| 175 | descriptor can still be used for rendezvous fallback if `sig` verifies. | ||
| 176 | |||
| 139 | `created` lets a reader prefer the freshest descriptor when more than one is | 177 | `created` lets a reader prefer the freshest descriptor when more than one is |
| 140 | seen. A node only publishes a descriptor once it has actually registered at a | 178 | seen. A node without a direct onion endpoint only publishes a descriptor once it |
| 141 | `meeting_node`, so it never advertises a relay it cannot be reached at. | 179 | has actually registered at a `meeting_node`, so it never advertises a relay it |
| 180 | cannot be reached at. A node with a direct onion endpoint may publish before | ||
| 181 | rendezvous registration using `meeting_node = "rendezvous-pending"`; Tor peers | ||
| 182 | can still dial the signed onion endpoint, and rendezvous becomes fallback once | ||
| 183 | registration succeeds. | ||
| 142 | 184 | ||
| 143 | ## 6. Rendezvous transport | 185 | ## 6. Rendezvous transport |
| 144 | 186 | ||
| @@ -243,8 +285,9 @@ tunnel. | |||
| 243 | not published anywhere. | 285 | not published anywhere. |
| 244 | - **Forward secrecy.** The caller's per connection ephemeral key means a later | 286 | - **Forward secrecy.** The caller's per connection ephemeral key means a later |
| 245 | compromise of the service encryption key does not decrypt past sessions. | 287 | compromise of the service encryption key does not decrypt past sessions. |
| 246 | - **IP privacy.** Neither side needs a public IP or an inbound port. The observable | 288 | - **IP privacy.** Neither side needs a public IP or an inbound port for |
| 247 | network fact is that each side has a TCP connection to a relay. | 289 | rendezvous. With a signed `.onion` endpoint, the reachable side exposes only a |
| 290 | Tor hidden service and the relay is not in the peer connection path. | ||
| 248 | 291 | ||
| 249 | ## 10. Threat model and limitations | 292 | ## 10. Threat model and limitations |
| 250 | 293 | ||
| @@ -265,6 +308,11 @@ tunnel. | |||
| 265 | unreachable, discovery stalls until one is reachable again. Relay TLS is | 308 | unreachable, discovery stalls until one is reachable again. Relay TLS is |
| 266 | verified, which closes a man in the middle path on discovery, but availability | 309 | verified, which closes a man in the middle path on discovery, but availability |
| 267 | still rests on the relay set. | 310 | still rests on the relay set. |
| 311 | - **Direct onion needs Tor.** With `-managedtor`, Bitflash starts a Tor process, | ||
| 312 | writes a local hidden-service config, and advertises the generated onion | ||
| 313 | endpoint. With `-onionservice`, an operator can still run Tor manually and | ||
| 314 | provide the endpoint. In both cases Tor owns the hidden-service private key and | ||
| 315 | uptime. | ||
| 268 | 316 | ||
| 269 | ## 11. Wire reference | 317 | ## 11. Wire reference |
| 270 | 318 | ||
| @@ -273,9 +321,9 @@ tunnel. | |||
| 273 | | `.btf` Base32 alphabet | `abcdefghijklmnopqrstuvwxyz234567`, no padding | | 321 | | `.btf` Base32 alphabet | `abcdefghijklmnopqrstuvwxyz234567`, no padding | |
| 274 | | Address payload | `pubkey[32] || checksum[2]`, 55 Base32 chars | | 322 | | Address payload | `pubkey[32] || checksum[2]`, 55 Base32 chars | |
| 275 | | Address checksum | `SHA256(".btf-checksum-v1" || pubkey)[0..2]` | | 323 | | Address checksum | `SHA256(".btf-checksum-v1" || pubkey)[0..2]` | |
| 276 | | Descriptor domain sep | `.btf-descriptor-v2` | | 324 | | Descriptor domain sep | `.btf-descriptor-v2`; optional onion sep `.btf-descriptor-v3-onion` | |
| 277 | | Descriptor version | `2` | | 325 | | Descriptor version | `2`; `3` when a signed onion endpoint is present | |
| 278 | | Descriptor signature | BIP340 Schnorr, deterministic (no aux rand) | | 326 | | Descriptor signature | BIP340 Schnorr, deterministic (no aux rand), v3 keeps `sig` plus `sig3` | |
| 279 | | Nostr descriptor event | kind `38501`, `d = btf-descriptor-2` | | 327 | | Nostr descriptor event | kind `38501`, `d = btf-descriptor-2` | |
| 280 | | Nostr relay event | kind `38502`, `d = btf-relay-2` | | 328 | | Nostr relay event | kind `38502`, `d = btf-relay-2` | |
| 281 | | Nostr pool event | kind `38503`, `d = btf-pool-2` | | 329 | | Nostr pool event | kind `38503`, `d = btf-pool-2` | |