aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-common/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-common/src')
-rw-r--r--packages/meshbay-common/src/meshbay_common/crypto.py40
-rw-r--r--packages/meshbay-common/src/meshbay_common/protocol.py6
2 files changed, 36 insertions, 10 deletions
diff --git a/packages/meshbay-common/src/meshbay_common/crypto.py b/packages/meshbay-common/src/meshbay_common/crypto.py
index 682e1c0..b2ff3c0 100644
--- a/packages/meshbay-common/src/meshbay_common/crypto.py
+++ b/packages/meshbay-common/src/meshbay_common/crypto.py
@@ -168,21 +168,45 @@ def unwrap_gek_aes(bundle: dict, sk_recipient: bytes, pk_recipient: bytes) -> by
# ── Keystore (local key storage) ──────────────────────────────────────────────
-# Argon2id parameters — calibrate to ~500ms on target hardware before production.
-# POC measured 78ms with these; increase memory_cost to 262144 (256MB) for prod.
+# Argon2id parameters for the node keystore.
+#
+# Finding M2: these sat at 64 MB long after the hub's password verifier was raised
+# to 256 MB, and the docs recorded the bump as done — true for the hub, false here.
+# The keystore protects the node's Ed25519 and X25519 private keys, so it is the
+# more valuable target of the two.
+#
+# Parameters are recorded in each keystore envelope, so raising them does not
+# invalidate existing files: LEGACY_* is used when an envelope predates the field.
ARGON2_ITERATIONS = 3
-ARGON2_MEMORY_COST = 65536 # 64 MB — increase to 262144 for production
+ARGON2_MEMORY_COST = 262144 # 256 MB
ARGON2_LANES = 4
ARGON2_KEY_LENGTH = 32
-def derive_keystore_key(password: str, salt: bytes) -> bytes:
- """Derive AES-256 key from password using Argon2id."""
+LEGACY_ARGON2_ITERATIONS = 3
+LEGACY_ARGON2_MEMORY_COST = 65536 # 64 MB — keystores written before M2
+LEGACY_ARGON2_LANES = 4
+
+
+def derive_keystore_key(
+ password: str,
+ salt: bytes,
+ *,
+ iterations: int | None = None,
+ memory_cost: int | None = None,
+ lanes: int | None = None,
+) -> bytes:
+ """
+ Derive an AES-256 key from a password using Argon2id.
+
+ Parameters default to the current production values; callers pass the values
+ recorded in an existing envelope when opening an older keystore.
+ """
return Argon2id(
salt=salt,
length=ARGON2_KEY_LENGTH,
- iterations=ARGON2_ITERATIONS,
- lanes=ARGON2_LANES,
- memory_cost=ARGON2_MEMORY_COST,
+ iterations=ARGON2_ITERATIONS if iterations is None else iterations,
+ lanes=ARGON2_LANES if lanes is None else lanes,
+ memory_cost=ARGON2_MEMORY_COST if memory_cost is None else memory_cost,
).derive(password.encode())
def encrypt_keystore(plaintext: bytes, key: bytes) -> tuple[bytes, bytes, bytes]:
diff --git a/packages/meshbay-common/src/meshbay_common/protocol.py b/packages/meshbay-common/src/meshbay_common/protocol.py
index 55dcdde..d86b4ef 100644
--- a/packages/meshbay-common/src/meshbay_common/protocol.py
+++ b/packages/meshbay-common/src/meshbay_common/protocol.py
@@ -29,8 +29,10 @@ class MNP:
CHAT_ATTACHMENT = "chat_attach" # attachment metadata
CHAT_HISTORY = "chat_hist" # request message history
CHAT_HISTORY_RESPONSE = "chat_hist_resp" # history response with messages
- GEK_REQUEST = "gek_req" # browser requests group GEK
- GEK_RESPONSE = "gek_resp" # node delivers GEK over secure channel
+ # GEK_REQUEST / GEK_RESPONSE were removed (NS3, and finding L1): the node must
+ # never serve the GEK in plaintext. Members obtain it by unwrapping their own
+ # ECIES bundle. The constants lingered after the handlers were deleted, leaving
+ # the wire contract looking as though the endpoint still existed.
FILE_UPLOAD = "file_upload" # client pushes file chunk to node
FILE_UPLOAD_ACK = "file_upload_ack" # node acknowledges chunk receipt
FILE_DELETE = "file_delete" # client requests file deletion