""" Bundle store — SQLite-backed storage for GEK bundles and keypair bundles. GEK bundles: ECIES-wrapped GEK targeted at a specific user's X25519 key. Keypair bundles: AES-GCM encrypted (Ed25519 + X25519) private keys, encrypted with the user's password-derived bundle_key. Opaque to the node. Both are stored and served over the P2P DataChannel during MNP handshake. """ import logging from pathlib import Path import aiosqlite log = logging.getLogger(__name__) _SCHEMA_GEK = """\ CREATE TABLE IF NOT EXISTS gek_bundles ( group_id TEXT NOT NULL, user_id TEXT NOT NULL, pk_eph_b64 TEXT NOT NULL, nonce_b64 TEXT NOT NULL, wrapped_b64 TEXT NOT NULL, stored_at TEXT NOT NULL DEFAULT (datetime('now')), PRIMARY KEY (group_id, user_id) ); """ _SCHEMA_KEYPAIR = """\ CREATE TABLE IF NOT EXISTS keypair_bundles ( user_id TEXT PRIMARY KEY, bundle_enc TEXT NOT NULL, stored_at TEXT NOT NULL DEFAULT (datetime('now')) ); """ class BundleStore: def __init__(self, db_path: Path): self._db_path = db_path self._db: aiosqlite.Connection | None = None async def open(self) -> None: self._db_path.parent.mkdir(parents=True, exist_ok=True) self._db = await aiosqlite.connect(str(self._db_path)) await self._db.execute(_SCHEMA_GEK) await self._db.execute(_SCHEMA_KEYPAIR) await self._db.commit() async def store( self, group_id: str, user_id: str, pk_eph_b64: str, nonce_b64: str, wrapped_b64: str, ) -> None: assert self._db await self._db.execute( "INSERT OR REPLACE INTO gek_bundles " "(group_id, user_id, pk_eph_b64, nonce_b64, wrapped_b64, stored_at) " "VALUES (?, ?, ?, ?, ?, datetime('now'))", (group_id, user_id, pk_eph_b64, nonce_b64, wrapped_b64), ) await self._db.commit() async def fetch(self, group_id: str, user_id: str) -> dict | None: assert self._db async with self._db.execute( "SELECT pk_eph_b64, nonce_b64, wrapped_b64 FROM gek_bundles " "WHERE group_id = ? AND user_id = ?", (group_id, user_id), ) as cursor: row = await cursor.fetchone() if not row: return None return { "pk_eph_b64": row[0], "nonce_b64": row[1], "wrapped_b64": row[2], } async def store_keypair(self, user_id: str, bundle_enc: str) -> None: assert self._db await self._db.execute( "INSERT OR REPLACE INTO keypair_bundles " "(user_id, bundle_enc, stored_at) VALUES (?, ?, datetime('now'))", (user_id, bundle_enc), ) await self._db.commit() async def fetch_keypair(self, user_id: str) -> str | None: assert self._db async with self._db.execute( "SELECT bundle_enc FROM keypair_bundles WHERE user_id = ?", (user_id,), ) as cursor: row = await cursor.fetchone() return row[0] if row else None async def close(self) -> None: if self._db: await self._db.close() self._db = None