From 0370d001a4e74d2af0809a3e1eb5ada699b1bca2 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 14 Sep 2026 03:01:58 +0200 Subject: fix(hub): the password verifier is Argon2id 64 MiB, and a hash's version names its parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pw_version` 4: Argon2id 64 MiB, t=3, lanes=4 — RFC 9106's second recommended setting. A v3 hash (256 MB) still verifies at its own parameters and is rewritten at the new ones on the next sign-in, through the rehash path that already existed. Why not more. The verifier matters against an offline attacker holding the database; online guessing is bounded by the sign-in lockout. That attacker pays the client's 600 000 PBKDF2-SHA512 iterations and the hub's Argon2id per guess, since `auth_key` is 256 bits and cannot be searched directly. Memory above 64 MiB multiplies that cost by a constant — at most 16 at 256 MB, less with PBKDF2 counted — while the hub pays the same memory at every sign-in, one derivation at a time. Measured on meshbay.org: 450 ms at 256 MB, 105 ms at 64 MiB, so a burst of sign-ins clears about four times faster. Changing the current version exposed a latent lockout. `hash_password` always used the current version's parameters, while the raw-password scheme recorded `pw_version = 2` — harmless while versions 2 and 3 shared their parameters, and with version 4 every legacy registration and v1→v2 rehash would have stored a 64 MiB hash labelled 256 MB, which nothing could then verify. Seventeen tests caught it. `hash_password` now takes the version it is hashing for. The OpenSSL deadlock between two concurrent `lanes=4` derivations is the same at 64 MiB, so Argon2 stays on its single worker. The loop-stall test measures against a v3 hash, because half of a 45 ms inline derivation is too close to scheduling noise to be a reliable bound. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LcF3QKWii7uQ2kSyXErzCt --- packages/meshbay-hub/src/meshbay_hub/api/users.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/api') diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py index 991a4f7..7b2fc8c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/users.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py @@ -222,8 +222,8 @@ async def register( if not credential: raise HTTPException(status_code=400, detail="auth_key or password required") - pw_hash, pw_salt = await hash_password_off_loop(credential) pw_ver = current_pw_version() if body.auth_key else 2 + pw_hash, pw_salt = await hash_password_off_loop(credential, pw_ver) hub_id = _cfg.identity.id if _cfg else "meshbay.org" user = User( username=body.username, @@ -399,7 +399,7 @@ async def login( user.pw_version = current_pw_version() elif user.pw_version < 2: # Legacy rehash: upgrade Argon2 params within the password scheme (v1 -> v2) - new_hash, new_salt = await hash_password_off_loop(body.password) + new_hash, new_salt = await hash_password_off_loop(body.password, 2) user.pw_hash = new_hash user.pw_salt = new_salt user.pw_version = 2 -- cgit v1.2.3