aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub')
-rw-r--r--packages/meshbay-hub/tests/test_android_keys.py74
-rw-r--r--packages/meshbay-hub/tests/test_android_shell.py5
2 files changed, 77 insertions, 2 deletions
diff --git a/packages/meshbay-hub/tests/test_android_keys.py b/packages/meshbay-hub/tests/test_android_keys.py
new file mode 100644
index 0000000..a00b909
--- /dev/null
+++ b/packages/meshbay-hub/tests/test_android_keys.py
@@ -0,0 +1,74 @@
+"""
+The Android keyring against the desktop's, where the shared vectors cannot see.
+
+`tests/vectors/keyring.json` holds the bytes (and the Android unit tests read
+it). What a vector cannot hold is a *list*: which admin operations may be
+signed at all, and the Argon2 parameters a format change would move. Two
+implementations of a list drift silently — an operation the desktop stopped
+signing at MNP 6.0 and Android still signs is a script in the page driving an
+older node into widening its sharing. So both are read from source and
+compared.
+"""
+
+import re
+from pathlib import Path
+
+import pytest
+
+PACKAGES = Path(__file__).resolve().parents[2]
+MAIN = PACKAGES / "meshbay-android" / "app" / "src" / "main"
+KEYS = MAIN / "kotlin" / "org" / "meshbay" / "client" / "keys"
+CLIENT = PACKAGES / "meshbay-client" / "src"
+SHIM = MAIN / "assets" / "bridge" / "meshbay-bridge.js"
+
+pytestmark = pytest.mark.skipif(not KEYS.exists(), reason="android sources not present")
+
+
+def _read(path: Path) -> str:
+ return path.read_text(encoding="utf-8")
+
+
+def test_the_same_admin_operations_are_signed():
+ js = _read(CLIENT / "transcripts.js")
+ js = js.split("const ADMIN_OPS = new Set([", 1)[1].split("]", 1)[0]
+ kt = _read(KEYS / "Transcripts.kt").split("val ADMIN_OPS = setOf(", 1)[1].split(")", 1)[0]
+ desktop = set(re.findall(r"'([a-z_]+)'", js))
+ android = set(re.findall(r'"([a-z_]+)"', kt))
+ assert desktop and android == desktop, android ^ desktop
+ # The ones MNP 6.0 took away must not come back on either side.
+ assert not {"root_add", "root_update", "group_attach"} & android
+
+
+def test_the_argon2_parameters_are_the_desktops():
+ js = _read(CLIENT / "keyring.js")
+ m = re.search(r"memory: (\d+), passes: (\d+), parallelism: (\d+), tagLength: (\d+)", js)
+ kt = _read(KEYS / "Kdf.kt")
+ want = dict(zip(("MEMORY_KIB", "PASSES", "PARALLELISM", "TAG"), m.groups()))
+ for name, value in want.items():
+ assert re.search(rf"const val ARGON2_{name} = {value}\b", kt), name
+
+
+def test_the_shim_offers_the_desktops_key_surface():
+ preload = _read(CLIENT / "preload.js")
+ shim = _read(SHIM)
+
+ def channels(text: str, call: str) -> set[str]:
+ return set(re.findall(rf"{call}\('((?:keys|device|secrets):[\w:-]+)'", text))
+
+ assert channels(shim, "call") == channels(preload, r"ipcRenderer\.invoke")
+
+
+def test_signing_is_by_kind_and_no_private_key_is_returned():
+ channels = _read(KEYS.parent / "bridge" / "KeyChannels.kt")
+ assert '"keys:sign" -> keyring.signAs(' in channels
+ # No channel hands the page a stored key: the store's slots are never a
+ # return value, and identity/mint/open answer with public keys.
+ assert "secrets.read()" in channels # the keyring's load, and nothing else
+ assert channels.count("secrets.read()") == 1
+ assert '"pkEdB64"' in channels and '"skEd"' not in channels
+
+
+def test_widening_browser_access_is_confirmed_natively():
+ channels = _read(KEYS.parent / "bridge" / "KeyChannels.kt")
+ branch = channels.split('"keys:set-browser-access" ->', 1)[1].split('"keys:created-here"', 1)[0]
+ assert 'confirm("native.browser_access_confirm")' in branch
diff --git a/packages/meshbay-hub/tests/test_android_shell.py b/packages/meshbay-hub/tests/test_android_shell.py
index 53b3914..ef7355d 100644
--- a/packages/meshbay-hub/tests/test_android_shell.py
+++ b/packages/meshbay-hub/tests/test_android_shell.py
@@ -133,8 +133,9 @@ def _shim_channels() -> set[str]:
def test_the_shim_offers_desktop_channels_and_native_answers_each():
preload_js = _read(CLIENT / "src" / "preload.js")
preload = set(re.findall(r"ipcRenderer\.invoke\('([\w:-]+)'", preload_js))
- channels_kt = _read(SRC / "bridge" / "Channels.kt")
- native = set(re.findall(r'^\s*"([\w:-]+)" ->', channels_kt, flags=re.M))
+ native = set()
+ for name in ("Channels.kt", "KeyChannels.kt"):
+ native |= set(re.findall(r'^\s*"([\w:-]+)" ->', _read(SRC / "bridge" / name), flags=re.M))
shim = _shim_channels()
assert shim, "no channel found in the shim"
assert shim <= preload, f"channels the desktop does not have: {shim - preload}"