diff options
Diffstat (limited to 'packages')
4 files changed, 25 insertions, 11 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js index afa5d27..af119c7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js @@ -75,11 +75,12 @@ async function generateKeypairs() { // is exactly what a GPU is good at, so 600k iterations bought far less than the // wall-clock time suggested. // -// 64 MB / t=3 / p=1 measured at ~320 ms through this WASM build on a desktop, so -// roughly a second on a modest phone — the most that belongs in a login. Memory -// is what matters here: at 64 MB per guess, a 24 GB card holds a few hundred in -// parallel instead of the effectively unbounded number PBKDF2 allows. -const ARGON2_MEM_KIB = 65536; // 64 MB +// 128 MB / t=3 / p=1 measured at ~640 ms through this WASM build on a desktop. +// Memory is the lever, not time: each guess must hold 128 MB, so a 24 GB card +// fits ~187 in parallel and its bandwidth caps it near 2k guesses/s, against no +// ceiling at all for PBKDF2. 256 MB would double that again at ~1.3 s, which is +// too much to ask of a phone for something paid at every sign-in. +const ARGON2_MEM_KIB = 131072; // 128 MB const ARGON2_TIME = 3; const ARGON2_LANES = 1; @@ -273,7 +274,9 @@ async function loginAndRecover(username, password) { && localStorage.getItem(`meshbay_kp_${username}`)) || null; if (bundleEnc) { - const keys = await decryptBundle(bundleEnc, password, username); + // Reuse the keys just derived — decryptBundle() would run the KDF again, + // and at these parameters that is another 0.6 s for nothing. + const keys = await decryptBundleWithKey(bundleEnc, result.bundleKey); result.skEdB64 = keys.skEd; result.skXB64 = keys.skX; result.keypairBundleEnc = bundleEnc; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/vendor/PROVENANCE.md b/packages/meshbay-hub/src/meshbay_hub/static/vendor/PROVENANCE.md index 35d742b..6935e91 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/vendor/PROVENANCE.md +++ b/packages/meshbay-hub/src/meshbay_hub/static/vendor/PROVENANCE.md @@ -22,7 +22,7 @@ request and nothing to locate at runtime. keypair bundle is protected by the passphrase alone and rests on every node whose group its owner joins (finding C4), so PBKDF2 — compute-only, and therefore cheap on a GPU — was the wrong tool for it. Measured through this build on the dev -machine: Argon2id 64 MB / t=3 / p=1 takes ~320 ms, against ~240 ms for +machine: Argon2id 128 MB / t=3 / p=1 takes ~640 ms, against ~240 ms for PBKDF2-SHA512 at 600k, for a memory cost a GPU cannot ignore. ### argon2.wasm diff --git a/packages/meshbay-hub/tests/test_bundle_kdf_parity.py b/packages/meshbay-hub/tests/test_bundle_kdf_parity.py index c3c8ff9..27e10d4 100644 --- a/packages/meshbay-hub/tests/test_bundle_kdf_parity.py +++ b/packages/meshbay-hub/tests/test_bundle_kdf_parity.py @@ -41,7 +41,7 @@ pytestmark = pytest.mark.skipif( # Parameters must match keyderive.js. If someone tunes them there and not here, # this test fails — which is the point: changing them silently orphans every # bundle already written. -MEM_KIB, TIME_COST, LANES = 65536, 3, 1 +MEM_KIB, TIME_COST, LANES = 131072, 3, 1 CASES = ["alice", "grenet", "utilisateur-é", ""] PASSWORDS = ["correct horse battery staple", "p", "üñïçø∂é ✓ 🔐"] diff --git a/packages/meshbay-node/src/meshbay_node/hub_client.py b/packages/meshbay-node/src/meshbay_node/hub_client.py index 691ac7c..334107d 100644 --- a/packages/meshbay-node/src/meshbay_node/hub_client.py +++ b/packages/meshbay-node/src/meshbay_node/hub_client.py @@ -241,6 +241,7 @@ class HubClient: # working one until someone notices the node has vanished. async with websockets.connect( ws_url, ping_interval=20, ping_timeout=20, close_timeout=5, + open_timeout=15, ) as ws: auth_msg = { "type": "auth", @@ -250,10 +251,20 @@ class HubClient: if group_ids: auth_msg["group_ids"] = group_ids await ws.send(json.dumps(auth_msg)) - auth_resp = json.loads(await ws.recv()) + # Bounded: a hub that accepts the socket and then says nothing + # — which is what it does for a few seconds while restarting — + # would otherwise park this task here forever, with the node + # running, silent, and invisible to everyone. + auth_resp = json.loads( + await asyncio.wait_for(ws.recv(), timeout=15)) if auth_resp.get("type") != "auth_ok": - log.error("WS auth failed: %s", auth_resp) - return + # Not fatal: the token may simply have expired while we + # were disconnected. Refresh on the next pass rather than + # ending the task, which used to strand the node for good. + log.warning("WS auth refused: %s — retrying in 5s", auth_resp) + await asyncio.sleep(5) + await self.ensure_fresh_token() + continue self._ws = ws log.info("Hub WS connected") |