From be1ff89465c9878f2fbd641fb0d4eba729439ac8 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 13 Aug 2026 11:56:04 +0200 Subject: refactor(quic): share the unified handshake authorization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 11.5.4 — QUIC half. Findings M1, M9 on this transport. quic_server._do_handshake_sync was a second, weaker copy of the WebRTC logic: group_id was optional, so omitting it skipped the membership check entirely and fell back to the node's first group (M1); node-scoped daemon tokens were accepted as client tokens (M9); and the checks could drift from the WebRTC path independently, which is how they diverged in the first place. Authorization now comes from meshbay_common.handshake, shared with WebRTC. C6 IS STILL OPEN ON THIS TRANSPORT. There is no GEK proof here yet: a forged or stolen token still reaches the node over QUIC and can inject chat without holding the group key. What remains is the challenge/response and the mutual node proof — quic_binding() is written and unit-tested for exactly this, and 11.5.6 (whether a certificate hash is the right anchor, or an RFC 5705 exporter is reachable from aioquic) is still unproven. This commit narrows the gap to the proof itself; it does not close the finding. QUIC tests updated: default tokens are members of the test group, and clients pass group_id, since it is mandatory now. Tests: 9 quic/multi-group, full node+common suite green. Co-Authored-By: Claude Opus 5 --- packages/meshbay-node/tests/test_quic_transport.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'packages/meshbay-node/tests/test_quic_transport.py') diff --git a/packages/meshbay-node/tests/test_quic_transport.py b/packages/meshbay-node/tests/test_quic_transport.py index 0c1a1cd..c2c2b70 100644 --- a/packages/meshbay-node/tests/test_quic_transport.py +++ b/packages/meshbay-node/tests/test_quic_transport.py @@ -49,7 +49,8 @@ def make_jwt(sk_hub, pk_node_b64, ttl=3600, groups=None): "iss": "test-hub", "sub": "user-001", "pk_user": pk_node_b64, "hub_id": "test-hub", "jti": "test-jti", "iat": now, "exp": now + ttl, - "groups": groups or [], + # group_id is mandatory (M1), so default tokens are members of "g". + "groups": groups if groups is not None else ["g"], }, sk_pem, algorithm="EdDSA") @@ -80,6 +81,7 @@ async def test_quic_chunk_roundtrip(sk_node, sk_hub, gek, shared_dir, tmp_path): host="127.0.0.1", port=19100, jwt_token=token, gek=gek, pk_node_b64=pk_to_b64(sk_node.public_key()), + group_id="g", ) as client: chunk0 = await client.fetch_chunk(entry.id, chunk_index=0) chunk1 = await client.fetch_chunk(entry.id, chunk_index=1) @@ -116,6 +118,7 @@ async def test_quic_fetch_index(sk_node, sk_hub, gek, shared_dir, tmp_path): host="127.0.0.1", port=19101, jwt_token=token, gek=gek, pk_node_b64=pk_to_b64(sk_node.public_key()), + group_id="g", ) as client: wire = await client.fetch_index() recovered = GroupIndex.deserialize(wire, sk_node=sk_node, gek=gek) @@ -220,6 +223,7 @@ async def test_quic_session_resumption(sk_node, sk_hub, gek, shared_dir, tmp_pat async with QuicChunkClient( host="127.0.0.1", port=19104, jwt_token=token, gek=gek, pk_node_b64=pk_b64, + group_id="g", ) as client: wire = await client.fetch_index() assert GroupIndex.deserialize(wire, sk_node=sk_node, gek=gek).count == 2 @@ -233,6 +237,7 @@ async def test_quic_session_resumption(sk_node, sk_hub, gek, shared_dir, tmp_pat host="127.0.0.1", port=19104, jwt_token=token, gek=gek, pk_node_b64=pk_b64, session_ticket=saved_ticket, + group_id="g", ) as client: wire = await client.fetch_index() assert GroupIndex.deserialize(wire, sk_node=sk_node, gek=gek).count == 2 @@ -269,6 +274,7 @@ async def test_quic_denylist_blocks_user(sk_node, sk_hub, gek, shared_dir, tmp_p async with QuicChunkClient( host="127.0.0.1", port=19105, jwt_token=token, gek=gek, pk_node_b64=pk_b64, + group_id="g", ) as client: wire = await client.fetch_index() assert GroupIndex.deserialize(wire, sk_node=sk_node, gek=gek).count == 2 @@ -281,6 +287,7 @@ async def test_quic_denylist_blocks_user(sk_node, sk_hub, gek, shared_dir, tmp_p async with QuicChunkClient( host="127.0.0.1", port=19105, jwt_token=token, gek=gek, pk_node_b64=pk_b64, + group_id="g", ) as client: await client.fetch_index() -- cgit v1.2.3