""" Nothing in the interface reads an identity private key except the identity. The transport sees an identity on a node as two public keys, a signature and an X25519 agreement (`transport.js`, `_identityFromKeys`). In a browser that object wraps keys held in the page; in the desktop application the keys stay in the main process and the same object asks it to sign. That only holds if no other code reaches past the object for a key — which is what every admin op, device link, chat message and app used to do, twenty times over, and is what this test refuses. """ import re from spa_source import STATIC # Where a private key may be named: minted and sealed (keyderive.js), wrapped # into an identity (transport.js), re-sealed during a passphrase change in a # browser (transport-rewrap.js). ALLOWED = {"keyderive.js", "transport.js", "transport-rewrap.js"} def test_only_the_identity_touches_a_private_key(): offenders = [] for path in STATIC.glob("*.js"): if path.name in ALLOWED: continue text = path.read_text(encoding="utf-8") if re.search(r"skEdB64|skXB64|sessionKeys|signBytes\(", text): offenders.append(path.name) assert not offenders, f"these read a private key directly: {offenders}" def test_in_the_transport_only_the_identity_factory_signs_with_a_raw_key(): text = (STATIC / "transport.js").read_text(encoding="utf-8") factory = text[text.index("async function _identityFromKeys"):] factory = factory[:factory.index("\n}\n")] rest = text.replace(factory, "") assert "signBytes(" in factory assert "signBytes(" not in rest, "the transport signs with a raw key outside the identity" assert "skXB64" not in rest.replace("id.skXB64", ""), \ "the transport reads the X25519 private key outside the identity" def test_a_group_key_is_unwrapped_through_the_identity(): crypto = (STATIC / "crypto.js").read_text(encoding="utf-8") unwrap = crypto[crypto.index("async function unwrapGEK"):] unwrap = unwrap[:unwrap.index("\n}\n")] assert "shared(pkEphRaw)" in unwrap and "pkcs8" not in unwrap