aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/auth.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-30 13:55:59 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-30 13:55:59 +0200
commita55d40b74bda77dff6ec565abdd551607fc665d6 (patch)
treeeaab3cf9434be8bdcd67a9cddc7218050a6874e4 /packages/meshbay-hub/src/meshbay_hub/auth.py
parentf211a13dc2e5dd82eaba49222171d6f29d858eb5 (diff)
downloadmeshbay-a55d40b74bda77dff6ec565abdd551607fc665d6.tar.gz
feat(hub): a bundle pepper per account, handed only to a proven session
Sealed at rest and bound to the account; returned by sign-in, device sign-in, a passphrase change and GET /me/bundle-pepper, never by a refresh, to a node token, in a token or in a log. Erasure clears it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/auth.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/auth.py22
1 files changed, 22 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/auth.py b/packages/meshbay-hub/src/meshbay_hub/auth.py
index 0d0fd95..1d09581 100644
--- a/packages/meshbay-hub/src/meshbay_hub/auth.py
+++ b/packages/meshbay-hub/src/meshbay_hub/auth.py
@@ -324,6 +324,28 @@ def decrypt_email(stored: str) -> str:
return AESGCM(_email_key).decrypt(nonce, ct, None).decode()
+def seal_pepper(raw: bytes, user_id: str) -> str:
+ """Seal a bundle pepper for storage, bound to its account.
+
+ The same at-rest key as the e-mail, with the account id and a purpose as
+ associated data: a sealed value copied into another row, or into the e-mail
+ column, does not open.
+ """
+ if _email_key is None:
+ raise RuntimeError("Hub keypair not loaded")
+ nonce = os.urandom(12)
+ aad = f"meshbay:bundle_pepper:{user_id}".encode()
+ return base64.b64encode(nonce + AESGCM(_email_key).encrypt(nonce, raw, aad)).decode()
+
+
+def open_pepper(stored: str, user_id: str) -> bytes:
+ if _email_key is None:
+ raise RuntimeError("Hub keypair not loaded")
+ raw = base64.b64decode(stored)
+ aad = f"meshbay:bundle_pepper:{user_id}".encode()
+ return AESGCM(_email_key).decrypt(raw[:12], raw[12:], aad)
+
+
def hash_email_blind(email: str) -> str:
"""Deterministic HMAC-SHA256 of the lowercased email for uniqueness checks.