diff options
Diffstat (limited to 'packages/meshbay-node/tests/test_roster_pairing.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_roster_pairing.py | 36 |
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): |