""" Tier 2 for chat (docs/MESHBAY_DESIGN.md §3.3): whose device signed a message, checked against the group's roster and the keys this machine accepted before — through `main()`, against the in-memory node, with real countersignatures. """ import json import pytest from fake_hub import PASSPHRASE, USERNAME, FakeHub from fake_node import FakeNode from meshbay_cli import __main__ as cli from meshbay_cli.client.keys import Identity from meshbay_cli.client.roster import RosterCheck, judge, verify_roster from meshbay_cli.commands import chat, context def _new() -> Identity: from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey return Identity(Ed25519PrivateKey.generate(), X25519PrivateKey.generate()) @pytest.fixture def node(monkeypatch, capsys): hub, fake = FakeHub(), FakeNode() monkeypatch.setattr(context, "Hub", lambda url: hub) async def open_group(account, group): return fake.session() monkeypatch.setattr(chat, "open_group", open_group) assert cli.main(["--hub", hub.url, "-u", USERNAME, "--password", PASSPHRASE, "login"]) == 0 capsys.readouterr() return fake def _last(capsys, n=1) -> list[dict]: assert cli.main(["--json", "chat", f"-{n}", "Family"]) == 0 return json.loads(capsys.readouterr().out) def test_each_verdict_on_a_message_signing_device(node, capsys): phone = node.say("from bob's phone") assert _last(capsys)[0]["trust"] == "first" # first sight: pinned assert _last(capsys)[0]["trust"] == "pinned" # A second device, countersigned by the first: no notice. laptop = _new() node.list_device("bob-test", laptop, countersigned_by=phone) node.say("from bob's laptop", device=laptop) assert _last(capsys)[0]["trust"] == "linked" # Named as countersigned and carrying no proof, as a lying node would list it. planted = _new() node.list_device("bob-test", planted, countersigned_by=phone, proof=False) node.say("hello, it's bob", device=planted) assert _last(capsys)[0]["trust"] == "changed" # Not in the roster at all. node.say("hello again", device=_new()) assert _last(capsys)[0]["trust"] == "changed" # Said in plain text under the message, and nowhere else. assert cli.main(["chat", "-5", "Family"]) == 0 out = capsys.readouterr().out.splitlines() assert sum("key you have not seen before" in line for line in out) == 2 assert out[-1].lstrip().startswith("! This account") assert out[-3].lstrip().startswith("! This account") # Another account, first sight. node.say("hi from carol", sender="carol-test") assert _last(capsys)[0]["trust"] == "first" async def test_a_device_listed_after_the_roster_was_read_is_not_a_substitution(node): """The roster is read once per connection; a member's new device added since is read again before "changed" is said.""" from meshbay_cli.client.account import Account from meshbay_cli.client.store import Store store = Store() account = Account.open(context.Hub("x"), None, lambda: PASSPHRASE, store) session = node.session() phone = node.say("one") check = RosterCheck(account, session) assert await check.status("u-bob-test", phone.pk_ed) == "first" tablet = _new() node.list_device("bob-test", tablet, countersigned_by=phone) # after the read assert await check.status("u-bob-test", tablet.pk_ed) == "linked" def test_a_root_is_a_device_naming_no_countersigner(): a, b = _new(), _new() rows = [{"user_id": "u", "pk_ed25519": a.pk_ed, "pk_x25519": a.pk_x, "added_by_pk": ""}, {"user_id": "u", "pk_ed25519": b.pk_ed, "pk_x25519": b.pk_x, "added_by_pk": a.pk_ed, "add_sig": "", "add_nonce": "", "add_ts": 0}] got = verify_roster({"devices": rows, "node_pk": "pk"}, "pk")["u"] assert got.verified == [a.pk_ed] and got.unevidenced == [b.pk_ed] assert judge([a.pk_ed], got, b.pk_ed) == ("changed", None) assert judge(None, got, b.pk_ed)[0] == "first"