diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_identity_seam.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_identity_seam.py | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_identity_seam.py b/packages/meshbay-hub/tests/test_identity_seam.py new file mode 100644 index 0000000..15db6d8 --- /dev/null +++ b/packages/meshbay-hub/tests/test_identity_seam.py @@ -0,0 +1,49 @@ +""" +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 |