aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-common
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-common')
-rw-r--r--packages/meshbay-common/src/meshbay_common/background.py3
-rw-r--r--packages/meshbay-common/src/meshbay_common/crypto.py10
-rw-r--r--packages/meshbay-common/src/meshbay_common/keyderive.py6
-rw-r--r--packages/meshbay-common/src/meshbay_common/protocol.py31
-rw-r--r--packages/meshbay-common/src/meshbay_common/webcrypto.py3
-rw-r--r--packages/meshbay-common/tests/test_background_tasks.py1
-rw-r--r--packages/meshbay-common/tests/test_handshake.py5
-rw-r--r--packages/meshbay-common/tests/test_js_python_parity.py6
-rw-r--r--packages/meshbay-common/tests/test_keyderive.py4
-rw-r--r--packages/meshbay-common/tests/test_paths.py2
-rw-r--r--packages/meshbay-common/tests/test_webcrypto.py16
11 files changed, 43 insertions, 44 deletions
diff --git a/packages/meshbay-common/src/meshbay_common/background.py b/packages/meshbay-common/src/meshbay_common/background.py
index 1f25dfd..0afc3e8 100644
--- a/packages/meshbay-common/src/meshbay_common/background.py
+++ b/packages/meshbay-common/src/meshbay_common/background.py
@@ -30,7 +30,8 @@ from __future__ import annotations
import asyncio
import logging
-from typing import Any, Coroutine
+from collections.abc import Coroutine
+from typing import Any
log = logging.getLogger(__name__)
diff --git a/packages/meshbay-common/src/meshbay_common/crypto.py b/packages/meshbay-common/src/meshbay_common/crypto.py
index eadb42b..e537500 100644
--- a/packages/meshbay-common/src/meshbay_common/crypto.py
+++ b/packages/meshbay-common/src/meshbay_common/crypto.py
@@ -5,17 +5,17 @@ Validated in Spike 1 and Spike 6 of the POC.
All operations use PyCA cryptography (OpenSSL-backed, hardware-accelerated).
"""
-import os
import base64
+import os
+import blake3
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.kdf.argon2 import Argon2id
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-from cryptography.hazmat.primitives import hashes, serialization
-import blake3
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# ── Key serialisation helpers ─────────────────────────────────────────────────
diff --git a/packages/meshbay-common/src/meshbay_common/keyderive.py b/packages/meshbay-common/src/meshbay_common/keyderive.py
index 497f877..4b90af3 100644
--- a/packages/meshbay-common/src/meshbay_common/keyderive.py
+++ b/packages/meshbay-common/src/meshbay_common/keyderive.py
@@ -24,11 +24,11 @@ See keyderive.js for the browser-side implementation.
"""
import hashlib
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from cryptography.hazmat.primitives.kdf.argon2 import Argon2id
-
# Argon2id parameters — same as keystore (see crypto.py)
_ITERATIONS = 3
_MEMORY_COST = 65536 # 64 MB — increase to 262144 for production
@@ -85,9 +85,11 @@ def encrypt_keypair_bundle(
Returns: AES-256-GCM ciphertext (nonce prepended).
"""
import os
+
+ import msgpack
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
+
from meshbay_common.crypto import sk_to_raw
- import msgpack
# Derive an AES key from the password (different info string from key derivation)
salt = hashlib.sha256(f"meshbay:bundle:v1:{username}".encode()).digest()
diff --git a/packages/meshbay-common/src/meshbay_common/protocol.py b/packages/meshbay-common/src/meshbay_common/protocol.py
index c444711..5374742 100644
--- a/packages/meshbay-common/src/meshbay_common/protocol.py
+++ b/packages/meshbay-common/src/meshbay_common/protocol.py
@@ -32,12 +32,11 @@ which local promise a reply belongs to.
import os
from dataclasses import dataclass, field
-from typing import Any
# The wire versions live in meshbay_common/__init__.py — one source, because a
# second copy here said "0.1" while every message on the wire carried "0.2".
# Nothing imported it, which is the only reason it was harmless.
-from meshbay_common import MNP_VERSION, MHP_VERSION # noqa: F401 (re-export)
+from meshbay_common import MHP_VERSION, MNP_VERSION # noqa: F401 (re-export)
from meshbay_common.groupbox import PURPOSE_UPLOAD, seal, unseal
from meshbay_common.webcrypto import (
chunk_key_aes,
@@ -45,7 +44,6 @@ from meshbay_common.webcrypto import (
encrypt_chunk_aes,
)
-
# ── MNP message types ─────────────────────────────────────────────────────────
class MNP:
@@ -70,7 +68,8 @@ class MNP:
# and no client: removed rather than repaired.
#
# Not a Double Ratchet message, and never was — finding C1
- # (`docs/MESHBAY_DESIGN.md` §13.1) rejected exactly that for groups. Since MNP 2.0 it is AES-256-GCM under a
+ # (`docs/MESHBAY_DESIGN.md` §13.1) rejected exactly that for groups. Since
+ # MNP 2.0 it is AES-256-GCM under a
# per-device subkey of the group's chat epoch key, signed over the
# ciphertext with the sending device's pinned Ed25519 key. There is no
# plaintext form on the wire (`chatbox.py`, docs/MESHBAY_DESIGN.md §4.5);
@@ -160,21 +159,21 @@ class MNP:
MEMBER_UNPIN_ACK = "member_unpin_ack"
APPS_ENABLED = "apps_enabled" # operator → node: which group apps to show
APPS_ENABLED_ACK = "apps_enabled_ack"
- TRANSFER_LIMITS = "transfer_limits" # operator → node: per-member caps for this group
+ TRANSFER_LIMITS = "transfer_limits" # operator → node: per-member caps here
TRANSFER_LIMITS_ACK = "transfer_limits_ack" # node → this group: the new caps
SET_SCAN_SETTINGS = "set_scan_settings" # operator → node: reconcile/debounce timing
SET_SCAN_SETTINGS_ACK = "set_scan_settings_ack"
MEDIA_META_REQ = "media_meta_req" # client → node: TMDB metadata for a path
MEDIA_META_RESP = "media_meta_resp" # node → client: TMDB metadata (or none)
- TMDB_CONFIG = "tmdb_config" # operator → node: set custom TMDB token/language (node-wide)
- TMDB_CONFIG_ACK = "tmdb_config_ack" # node → everyone: new TMDB config (never the token)
- TMDB_ENABLED = "tmdb_enabled" # operator → node: enable/disable TMDB for this group
- TMDB_ENABLED_ACK = "tmdb_enabled_ack" # node → this group: new per-group TMDB enabled state
- SEASON_META_REQ = "season_meta_req" # client → node: TMDB overview/poster for one season
- SEASON_META_RESP = "season_meta_resp" # node → client: season-level TMDB fields (or none)
- TMDB_SEARCH_REQ = "tmdb_search_req" # client → node: candidate TMDB matches for a query
- TMDB_SEARCH_RESP = "tmdb_search_resp" # node → client: candidate list (id, title, year, poster)
- TMDB_OVERRIDE = "tmdb_override" # operator → node: replace a show/movie's TMDB match
+ TMDB_CONFIG = "tmdb_config" # operator → node: token/language, node-wide
+ TMDB_CONFIG_ACK = "tmdb_config_ack" # node → everyone: config, never the token
+ TMDB_ENABLED = "tmdb_enabled" # operator → node: TMDB on/off here
+ TMDB_ENABLED_ACK = "tmdb_enabled_ack" # node → this group: TMDB on/off here
+ SEASON_META_REQ = "season_meta_req" # client → node: one season's overview
+ SEASON_META_RESP = "season_meta_resp" # node → client: season fields, or none
+ TMDB_SEARCH_REQ = "tmdb_search_req" # client → node: candidates for a query
+ TMDB_SEARCH_RESP = "tmdb_search_resp" # node → client: id, title, year, poster
+ TMDB_OVERRIDE = "tmdb_override" # operator → node: replace a match
TMDB_OVERRIDE_ACK = "tmdb_override_ack"
TMDB_REMATCH = "tmdb_rematch" # operator → node: drop one file's match
TMDB_REMATCH_ACK = "tmdb_rematch_ack"
@@ -257,7 +256,7 @@ class MNP:
# key without having to reconnect.
CHAT_EPOCH = "chat_epoch"
CHAT_EPOCH_ACK = "chat_epoch_ack"
- ROOT_UPDATE = "root_update" # operator → node: change writable/removable on a root
+ ROOT_UPDATE = "root_update" # operator → node: a root's flags
ROOT_UPDATE_ACK = "root_update_ack"
ROOT_EJECT = "root_eject" # operator → node: mark removable root as ejected
ROOT_EJECT_ACK = "root_eject_ack"
@@ -271,7 +270,7 @@ class MNP:
# node's `sender_id` (Tier 2, docs/MESHBAY_DESIGN.md §3.3).
GROUP_ROSTER_REQ = "group_roster_req"
GROUP_ROSTER_RESP = "group_roster_resp"
- ROSTER_READ = "roster_read" # operator → node: list pinned identities + members
+ ROSTER_READ = "roster_read" # operator → node: identities + members
ROSTER_READ_ACK = "roster_read_ack"
DENYLIST_READ = "denylist_read" # operator → node: show denylist entries
DENYLIST_READ_ACK = "denylist_read_ack"
diff --git a/packages/meshbay-common/src/meshbay_common/webcrypto.py b/packages/meshbay-common/src/meshbay_common/webcrypto.py
index 58958f8..3bd5ae6 100644
--- a/packages/meshbay-common/src/meshbay_common/webcrypto.py
+++ b/packages/meshbay-common/src/meshbay_common/webcrypto.py
@@ -26,9 +26,10 @@ This ensures AES and ChaCha20 keys are always distinct even from the same GEK.
"""
import os
+
+from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
-from cryptography.hazmat.primitives import hashes
def chunk_key_aes(gek: bytes, file_hash: bytes, chunk_index: int) -> bytes:
diff --git a/packages/meshbay-common/tests/test_background_tasks.py b/packages/meshbay-common/tests/test_background_tasks.py
index 4f0ac59..d01235b 100644
--- a/packages/meshbay-common/tests/test_background_tasks.py
+++ b/packages/meshbay-common/tests/test_background_tasks.py
@@ -28,7 +28,6 @@ import logging
from pathlib import Path
import pytest
-
from meshbay_common import background
REPO = Path(__file__).resolve().parents[3]
diff --git a/packages/meshbay-common/tests/test_handshake.py b/packages/meshbay-common/tests/test_handshake.py
index d4f6d2b..11313aa 100644
--- a/packages/meshbay-common/tests/test_handshake.py
+++ b/packages/meshbay-common/tests/test_handshake.py
@@ -12,7 +12,6 @@ import jwt
import pytest
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
-
from meshbay_common.handshake import (
HANDSHAKE_PREFIX,
NONCE_LEN,
@@ -207,8 +206,8 @@ def test_membership_refusal_carries_a_code_a_client_can_act_on():
is exactly the kind of coupling that breaks when someone improves a message.
"""
import jwt as _jwt
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
sk = Ed25519PrivateKey.generate()
pem_priv = sk.private_bytes(
@@ -234,8 +233,8 @@ def test_a_group_this_node_does_not_host_is_refused_with_a_code():
dark on 2026-09-11 with its real host online — or had to match on wording.
"""
import jwt as _jwt
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
sk = Ed25519PrivateKey.generate()
pem_priv = sk.private_bytes(
diff --git a/packages/meshbay-common/tests/test_js_python_parity.py b/packages/meshbay-common/tests/test_js_python_parity.py
index 9893a9f..3887414 100644
--- a/packages/meshbay-common/tests/test_js_python_parity.py
+++ b/packages/meshbay-common/tests/test_js_python_parity.py
@@ -21,7 +21,6 @@ import tempfile
from pathlib import Path
import pytest
-
from meshbay_common.adminop import admin_transcript
from meshbay_common.handshake import handshake_transcript, webrtc_binding
from meshbay_common.join import join_transcript
@@ -312,7 +311,6 @@ def _groupbox_payload(idx: int) -> dict:
@pytest.fixture(scope="module")
def groupbox_js(tmp_path_factory):
import msgpack
-
from meshbay_common.groupbox import seal
d = tmp_path_factory.mktemp("groupbox-parity")
@@ -486,7 +484,6 @@ def chatbox_js(tmp_path_factory):
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey,
)
-
from meshbay_common.chatbox import seal
d = tmp_path_factory.mktemp("chatbox-parity")
@@ -537,7 +534,6 @@ def test_browser_opens_a_chat_message_python_sealed(idx, vector, chatbox_js):
@pytest.mark.parametrize("idx,vector", list(enumerate(CHAT_VECTORS)))
def test_python_opens_a_chat_message_the_browser_sealed(idx, vector, chatbox_js):
import msgpack
-
from meshbay_common.chatbox import open_message
js, vectors = chatbox_js
@@ -580,7 +576,6 @@ def test_a_message_does_not_open_under_another_epoch(chatbox_js):
would round-trip against itself and pass every other test here.
"""
import pytest as _pytest
-
from meshbay_common.chatbox import open_message
_js, vectors = chatbox_js
@@ -598,7 +593,6 @@ def test_a_message_does_not_open_under_another_devices_key(chatbox_js):
any coordination — the property per-device ratchet chains were wanted for.
"""
import pytest as _pytest
-
from meshbay_common.chatbox import open_message
_js, vectors = chatbox_js
diff --git a/packages/meshbay-common/tests/test_keyderive.py b/packages/meshbay-common/tests/test_keyderive.py
index 0aa6201..40b3c71 100644
--- a/packages/meshbay-common/tests/test_keyderive.py
+++ b/packages/meshbay-common/tests/test_keyderive.py
@@ -1,12 +1,12 @@
"""Tests for password-based key derivation."""
import pytest
+from meshbay_common.crypto import pk_to_b64
from meshbay_common.keyderive import (
+ decrypt_keypair_bundle,
derive_keys_from_password,
encrypt_keypair_bundle,
- decrypt_keypair_bundle,
)
-from meshbay_common.crypto import pk_to_b64
def test_deterministic():
diff --git a/packages/meshbay-common/tests/test_paths.py b/packages/meshbay-common/tests/test_paths.py
index b9052e3..223a2a6 100644
--- a/packages/meshbay-common/tests/test_paths.py
+++ b/packages/meshbay-common/tests/test_paths.py
@@ -7,7 +7,6 @@ one file, and whether a member can save what they downloaded.
"""
import pytest
-
from meshbay_common.paths import (
find_fold_collisions,
fold,
@@ -18,7 +17,6 @@ from meshbay_common.paths import (
sanitize_for_download,
)
-
# ── Same file or not ─────────────────────────────────────────────────────────
def test_case_differences_fold_together():
diff --git a/packages/meshbay-common/tests/test_webcrypto.py b/packages/meshbay-common/tests/test_webcrypto.py
index 2ed6405..fbffdc1 100644
--- a/packages/meshbay-common/tests/test_webcrypto.py
+++ b/packages/meshbay-common/tests/test_webcrypto.py
@@ -1,10 +1,11 @@
"""Tests for AES-256-GCM webcrypto variant."""
import os
-import pytest
+
import blake3
+import pytest
from meshbay_common.crypto import generate_gek
-from meshbay_common.webcrypto import chunk_key_aes, encrypt_chunk_aes, decrypt_chunk_aes
+from meshbay_common.webcrypto import chunk_key_aes, decrypt_chunk_aes, encrypt_chunk_aes
def test_aes_roundtrip():
@@ -49,7 +50,10 @@ def test_aes_chunk_keys_unique_per_chunk():
def test_aes_gek_wrap_unwrap_roundtrip():
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from meshbay_common.crypto import (
- wrap_gek_aes, unwrap_gek_aes, sk_to_raw, pk_to_raw,
+ pk_to_raw,
+ sk_to_raw,
+ unwrap_gek_aes,
+ wrap_gek_aes,
)
gek = generate_gek()
sk = X25519PrivateKey.generate()
@@ -63,7 +67,7 @@ def test_aes_gek_wrap_unwrap_roundtrip():
def test_aes_gek_wrap_wrong_key_rejected():
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
- from meshbay_common.crypto import wrap_gek_aes, unwrap_gek_aes, sk_to_raw, pk_to_raw
+ from meshbay_common.crypto import pk_to_raw, sk_to_raw, unwrap_gek_aes, wrap_gek_aes
gek = generate_gek()
sk_a = X25519PrivateKey.generate()
@@ -77,7 +81,9 @@ def test_aes_gek_wrap_wrong_key_rejected():
def test_aes_gek_wrap_differs_from_chacha_wrap():
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from meshbay_common.crypto import (
- wrap_gek, wrap_gek_aes, pk_to_raw,
+ pk_to_raw,
+ wrap_gek,
+ wrap_gek_aes,
)
gek = generate_gek()
sk = X25519PrivateKey.generate()