9 Commits

Author SHA1 Message Date
Filip
6da7515d1e Transfer contact verification state during device pairing
When authorizing a new device, include the TOFU registry
(known_identity_keys) and manual verifications (verified_contacts) in
the encrypted pairing payload, so a contact verified on the existing
device stays verified on the newly paired one. Previously these stores
are device-local and started empty on the new device, dropping verified
status. Fields are optional and ignored by older clients; symmetric with
the iOS client.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-15 20:00:53 +02:00
filip
20f006cf5e Document client hardening round: AS-IS in CLAUDE.md + change requests for server and iOS/Android
CHANGES_2026-06-12_client_hardening.md lists deployment steps for the
server (shared protocol.py fix) and mirror requirements R1-R7 with
acceptance test scenarios for the native iOS/Android clients.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:08:59 +02:00
filip
4d15799b5e GUI/CLI fixes: crash paths, privacy-lock bypass, threading, logout leaks
- _on_image_download_failed called self.statusBar() which does not
  exist on a QWidget -> AttributeError crash; use status_bar label.
- Ctrl+Shift+P no longer disables the privacy overlay while the session
  is password-locked (lock bypass).
- Registration code confirmation no longer touches Qt widgets from the
  asyncio thread; new AsyncBridge.confirm_result signal carries the
  result back to the Qt thread.
- MainWindow.closeEvent now disconnects all bridge signal connections
  (tracked in _bridge_connections), removes the theme listener and
  stops the periodic refresh timer — every logout/login cycle leaked a
  window that kept handling notifications (duplicate mark_read, tray
  toasts).
- AsyncBridge logout rewires _key_change_cb onto the fresh ChatClient
  (key-change MITM warning was dead after logout) and clears
  _pending_send_queue so queued messages cannot be sent under a
  different identity.
- CLI: fix await precedence crash in the react-to-message prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:08:31 +02:00
filip
d499fd8436 Client core hardening: X3DH session adoption, sync watermark, PoW, key rotation
- Adopt a new X3DH session (install into self.sessions + persist) only
  after the first message decrypts successfully. Previously
  _process_x3dh_header saved the candidate session immediately, so a
  replayed/forged X3DH header permanently overwrote a working ratchet.
- Advance the incremental-sync watermark (__last_server_ts) only across
  the prefix of messages settled in the cache. An undecryptable message
  (e.g. sender key not yet received) is re-fetched and retried up to
  _MAX_DECRYPT_RETRIES=3 times instead of being silently lost forever.
  Watermark is no longer touched on offset>0 pages and never regresses.
- Fix NameError in the proof-of-work registration path (logger ->
  self._logger) and run _solve_pow in an executor so it does not block
  the event loop.
- Persist the rotated RSA login key only after the server confirmed
  rotate_keys; writing private.pem first bricked the account when the
  request failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:08:31 +02:00
filip
f0666ea6ac Preserve next-message bytes when draining oversized protocol message
ProtocolReader discarded the entire chunk containing the newline
delimiter while draining an oversized message, including bytes after
the newline that belong to the next pipelined message. This corrupted
framing for the rest of the connection (affects server and client).
Salvaged bytes are now kept in a _leftover buffer that read_message()
consumes before touching the stream.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:08:12 +02:00
Filip
f42ecf5c5b Add message retention and hide emails by default
- db: cleanup_old_messages(days) purges messages older than N days in
  batches; recipients/reads/deliveries/reactions follow via ON DELETE
  CASCADE. Returns attachment file_ids no longer referenced by any
  surviving message (forwarded copies keep their files) and removes
  their image_uploads rows
- server: MESSAGE_RETENTION_DAYS env var (default 0 = keep forever);
  hourly cleanup deletes expired messages and securely removes orphaned
  attachment blobs from the upload dir
- schema: email_visible now defaults to 0 — previously any logged-in
  user who knew a UUID could read another user's email via get_profile
- migrations: SQL script to apply the new default and reset the flag on
  existing databases (run manually, see file header)
- docker-compose: document MESSAGE_RETENTION_DAYS

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 10:30:42 +02:00
Filip
750290ddc1 Fix OPK loss on SPK-grace retry, deletion sync, add keys_updated push
- chat_core: defer one-time-prekey deletion until the first message
  decrypts successfully; deleting it on load made the SPK grace-period
  retry derive a wrong shared secret and lose the message permanently
- chat_core: fix get_deleted_since params (since -> since_ts) and
  response field (message_ids -> deleted_ids) so incremental deletion
  sync actually works
- chat_core: route keys_updated pushes into the notification queue
- server: notify contacts with keys_updated when a user uploads a new
  SPK or logs in with a new device, so clients invalidate cached key
  bundles instead of waiting for the TTL
- server: rate-limit download_stream like other heavy handlers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 01:39:36 +02:00
Filip
4ded15d569 Preserve pentest suite and cloudflare.ini.example from previous remote history
Carried over from the old Kecalek_python master (214da18).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:41:11 -04:00
Filip
2e7b72307d Initial commit — encrypted chat server + Python clients (v0.8.5)
E2E encrypted chat (X3DH + Double Ratchet, Signal Protocol).
Server: asyncio TCP + TLS, MySQL. Clients: PyQt6 GUI + CLI.
Secrets (.env, TLS keys, Cloudflare token), runtime data and
mobile clients (separate repos) are gitignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:22:39 -04:00