aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_roster_pairing.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests/test_roster_pairing.py')
-rw-r--r--packages/meshbay-node/tests/test_roster_pairing.py36
1 files changed, 32 insertions, 4 deletions
diff --git a/packages/meshbay-node/tests/test_roster_pairing.py b/packages/meshbay-node/tests/test_roster_pairing.py
index 9e45dbc..61d53ac 100644
--- a/packages/meshbay-node/tests/test_roster_pairing.py
+++ b/packages/meshbay-node/tests/test_roster_pairing.py
@@ -247,10 +247,16 @@ async def test_join_cannot_be_replayed_onto_another_connection(tmp_path, roster)
assert await roster.get_identity("grenet") is None
-async def test_pinned_identity_presenting_a_new_key_is_refused(tmp_path, roster):
+async def test_a_key_this_node_never_pinned_is_refused(tmp_path, roster):
"""
- 11.5.8's rule, applied to people: a changed key is refused outright rather
- than warned about, and clearing it is a deliberate operator action.
+ 11.5.8's rule, applied to people: an unrecognised key does not get in, and
+ a code cannot talk its way past that.
+
+ What changed with device linking (2026-08-18) is the way back, not the
+ refusal. This used to be `key_changed` and needed an operator to unpin; now
+ it is `unknown_device` and the person approves the new key from a device
+ already paired here. Nothing is pinned either way, which is the part that
+ matters.
"""
session = _session(tmp_path, roster)
_, old_pk_ed, old_pk_x = _keypair()
@@ -260,8 +266,30 @@ async def test_pinned_identity_presenting_a_new_key_is_refused(tmp_path, roster)
await session._do_join_request(
_join_msg(session, sk_ed2, new_pk_ed, new_pk_x, code="ANY-CODE"))
+ assert _last(session).get("reason") == "unknown_device"
+ assert await roster.find_device("grenet", new_pk_ed) is None
+ assert [d["pk_ed25519"] for d in await roster.list_devices("grenet")] == \
+ [old_pk_ed]
+
+
+async def test_a_pinned_key_arriving_with_a_different_x25519_is_refused(
+ tmp_path, roster):
+ """
+ The join transcript signs both keys together, so a pinned Ed25519 key
+ presenting a different encryption key is either a client that regenerated
+ half its identity or two messages spliced. Either way the pair is not the
+ one admitted, and the group key must not be wrapped for it.
+ """
+ session = _session(tmp_path, roster)
+ sk_ed, pk_ed, pk_x = _keypair()
+ await roster.pin_identity("grenet", "grenet", pk_ed, pk_x, "code")
+
+ _, _, other_pk_x = _keypair()
+ await session._do_join_request(
+ _join_msg(session, sk_ed, pk_ed, other_pk_x, code="ANY-CODE"))
+
assert _last(session).get("reason") == "key_changed"
- assert (await roster.get_identity("grenet"))["pk_ed25519"] == old_pk_ed
+ assert (await roster.find_device("grenet", pk_ed))["pk_x25519"] == pk_x
async def test_attempts_are_bounded(tmp_path, roster):