From 7fa74d722108ca4338d14db4ecba53800df86fca Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 7 Sep 2026 21:04:36 +0200 Subject: docs(chat): correct the claim that two devices never share a key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `chatbox.py` said per-device subkey derivation means "two devices never share an AES key". That is false in the deployment that exists, and stating it hid the reason the design is actually safe. Two clients of one account on one node normally hold the **same** identity key: a second browser fetches the keypair bundle from the node and recovers the existing key rather than minting a new one, and so does a fresh Electron install. Device linking — a distinct key, countersigned — is the exception, not the rule, which is why nobody is ever asked to pin anything when they open a second browser. So two clients routinely share a device key and therefore its chat subkey. What makes that safe is the nonce, not the derivation: 96 random bits, never a counter. Two independent senders under one key collide only on the birthday bound, unreachable at chat volume; two independent senders advancing one *counter* collide immediately, which is precisely what C1 and §15.0b are about. The true property is "no mutable sending state at all" — the hazard removed rather than partitioned — and the design therefore degrades correctly into the deployment as it is, where a chain-based one would have failed silently on the day someone opened a second tab. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TZZxYjz8YeWRz13xDi8LJr --- .../meshbay-common/src/meshbay_common/chatbox.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'packages/meshbay-common') diff --git a/packages/meshbay-common/src/meshbay_common/chatbox.py b/packages/meshbay-common/src/meshbay_common/chatbox.py index e04bd9b..f3cfc76 100644 --- a/packages/meshbay-common/src/meshbay_common/chatbox.py +++ b/packages/meshbay-common/src/meshbay_common/chatbox.py @@ -16,10 +16,23 @@ The property is given up on the record rather than inherited by accident. delivers it to members wrapped under the current group key. Each device derives its *own* subkey from it, by name, so: -* two devices never share an AES key, and nonce reuse across devices is - impossible without any coordination — the property per-device ratchet chains - were wanted for, obtained by derivation instead of by mutable state (which is - C1 one level down, and is exactly what `GroupSenderKeyStore` got wrong); +* two *keys* never share a subkey, and — the part that actually matters — + **there is no mutable sending state at all**, so nothing can be advanced + twice. §15.0b wanted per-device chains because two devices advancing one + chain produce key and nonce reuse (C1, one level down, and exactly what + `GroupSenderKeyStore` got wrong). Derivation plus a *random* nonce removes + the hazard rather than partitioning it; + + Be precise about what that does **not** say, because the obvious stronger + claim is false in the deployment that exists: two clients of one account on + one node normally hold the **same** identity key — a second browser recovers + it from the keypair bundle rather than minting a new one — so they share a + device key and therefore this subkey. That is safe here only because the + nonce is 96 random bits and not a counter: two independent senders under one + key collide with probability governed by the birthday bound, which at chat + volume is unreachable, whereas two independent senders advancing one counter + collide immediately. The design degrades correctly into that reality; a + chain-based one would not have; * a receiver derives any sender's subkey from the epoch key it already has, so nothing is distributed per device and there is no per-device state to persist, migrate or lose; -- cgit v1.2.3