aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport/quic_server.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/quic_server.py24
1 files changed, 23 insertions, 1 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/quic_server.py b/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
index 8a32538..6dde3ff 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
@@ -36,17 +36,20 @@ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from meshbay_common import MNP_VERSION
from meshbay_node.roots import RootSet, entry_abs_path
from meshbay_common.handshake import (
+ MNP_MIN_SUPPORTED,
NONCE_LEN,
ROLE_CLIENT,
ROLE_NODE,
HandshakeError,
authorize_token,
+ check_version,
handshake_transcript,
make_proof,
quic_binding,
verify_proof,
)
from meshbay_common.crypto import pk_to_b64
+from meshbay_common.groupbox import PURPOSE_ACK, seal
from meshbay_common.protocol import MNP, file_chunk_wire
from meshbay_node.indexer import GroupIndex
from meshbay_node.transport.wire import index_sync_message
@@ -269,6 +272,16 @@ class _MNPServerProtocol(QuicConnectionProtocol):
module, bound to the QUIC certificate hash. Finding C6 is closed on this
transport.
"""
+ # Same order as WebRTC: the range first, so a peer we cannot speak to is
+ # told so, rather than served messages it will misread (L2).
+ try:
+ check_version(msg.get("v", ""), msg.get("v_min", ""))
+ except HandshakeError as refusal:
+ self._send(stream_id, {"type": "error", "detail": str(refusal),
+ "code": refusal.code})
+ self._quic.close()
+ return
+
try:
peer = authorize_token(
msg.get("token", ""),
@@ -278,7 +291,8 @@ class _MNPServerProtocol(QuicConnectionProtocol):
denylist=self._ctx.get("denylist"),
)
except HandshakeError as refusal:
- self._send(stream_id, {"type": "error", "detail": str(refusal)})
+ self._send(stream_id, {"type": "error", "detail": str(refusal),
+ "code": refusal.code})
self._quic.close()
return
@@ -306,6 +320,7 @@ class _MNPServerProtocol(QuicConnectionProtocol):
self._send(stream_id, {
"type": MNP.HANDSHAKE_CHALLENGE,
"v": MNP_VERSION,
+ "v_min": MNP_MIN_SUPPORTED,
"nonce": base64.b64encode(self._gek_challenge).decode(),
})
@@ -355,12 +370,19 @@ class _MNPServerProtocol(QuicConnectionProtocol):
log.info("QUIC handshake OK — user=%s group=%s",
self._user_id[:8], self._group_id[:8])
+ # The config payload is empty here — QUIC serves no browser, so none of
+ # the fields WebRTC carries has a consumer on this transport. It is sealed
+ # anyway (decision D5): one shape per message on every transport, which is
+ # the lesson of the two `file_chunk` encoders and the two `index_sync`
+ # encodings. A field added later then has somewhere to go that is already
+ # authenticated, instead of arriving in clear beside a sealed one.
self._send(stream_id, {
"type": MNP.HANDSHAKE_ACK,
"v": MNP_VERSION,
"node_pk": pk_to_b64(self._ctx["sk_node"].public_key()),
"proof": base64.b64encode(node_proof).decode(),
"sig": base64.b64encode(self._ctx["sk_node"].sign(transcript)).decode(),
+ **seal(gek, PURPOSE_ACK, MNP.HANDSHAKE_ACK, peer.group_id, {}),
})
self._gek_challenge = None