feat(descriptor): SBQ2 connection descriptor format; release v5.8.0

The SB1 invitation runs 2000-2400 characters and needs QR version 38-40, past
the point where a single code is scannable, so the app falls back to an
animated multi-frame QR. SBQ2 is a fixed binary layout carrying only what
brings up DTLS -- ICE credentials, certificate fingerprint, candidates -- with
the SDP rebuilt from a template by a strict serializer. Measured on real Chrome
and Firefox SDP across four network profiles: 98-149 bytes, QR version 6-8.

Key material is meant to move to the DataChannel, bound by a commitment in the
descriptor. That half does not exist yet, so nothing calls this module: the
format is landed for review and freeze, not wired into the connection path.
doc/DESCRIPTOR-SBQ2.md records the gate on phase 3.

The decoder is a parser of hostile input: fixed offsets, explicit lengths,
deny-by-default on reserved values and unknown TLV extension types, trailing
bytes rejected, ICE credentials alphabet-checked so a CRLF cannot reach the
serializer. No compression -- DEFLATE adds bytes on this payload, and dropping
it removes the decompression-bomb surface with it.

Candidate pruning keeps coverage before count: one candidate per (family, type,
transport) survives before any surplus, so an IPv6-only or UDP-blocked path
cannot be pruned away by a v4-first sort.

Tests cover round-trip against captured Chrome and Firefox SDP, IPv6 and NAT64
addresses, ICE-TCP candidates, the TLV area, clock skew, one-shot binding and
SAS transcript coverage.
This commit is contained in:
lockbitchat
2026-08-06 11:54:05 -04:00
parent 3212138a0d
commit 6e82cfcae2
14 changed files with 1765 additions and 37 deletions
+37
View File
@@ -1,5 +1,42 @@
# Changelog
## v5.8.0 — A connection descriptor that fits in a small QR code
No change to how messages are protected, and no change to how a connection is
established. This release adds the wire format for a much smaller invitation and
the code that reads and writes it; nothing in the application calls it yet.
### Added
- `src/network/descriptor/sbq2.js` — version 2 of the connection descriptor. The
current `SB1:` payload runs 20002400 characters and needs QR version 3840, at
which point the app has to fall back to an animated multi-frame code. Measured
on real Chrome and Firefox SDP across four network profiles, SBQ2 is **98149
bytes**, which is **QR version 68** — a single, instantly scannable image.
The saving comes from sending only what is needed to bring up DTLS (ICE
credentials, certificate fingerprint, candidates) and templating the SDP rather
than shipping it verbatim. Key material is intended to move to the DataChannel,
bound to the descriptor by a commitment; **that half is not implemented**, which
is why the format is not yet in the connection path. See
`doc/DESCRIPTOR-SBQ2.md` for the layout, the security argument and the migration
gate.
The decoder is written as a parser of hostile input: fixed offsets, explicit
lengths, deny-by-default on every reserved value and on unknown extension types,
trailing bytes rejected, and ICE credentials alphabet-checked so a CRLF cannot
reach the SDP serializer. Compression is deliberately absent — on this payload
DEFLATE adds bytes, and removing it removes the decompression-bomb surface too.
- `doc/DESCRIPTOR-SBQ2.md`, and `tests/descriptor-sbq2.test.mjs` covering
round-trip against real Chrome and Firefox SDP, IPv6 and NAT64 addresses,
ICE-TCP candidates, candidate-coverage pruning, the TLV extension area, clock
skew, one-shot binding and the SAS transcript.
- `tests/fixtures/sdp-chrome.json` and `tests/fixtures/sdp-firefox.json`
SDP captured from real browsers rather than written by hand.
## v5.7.2 — Documentation, and a version that keeps itself honest
No changes to the protocol or to how messages are protected.