Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Secure Chat

A WhatsApp-style Flutter app with:

  • End-to-end encryption per contact, via the Signal Protocol (X3DH + Double Ratchet)
  • Encrypted voice/video calls via WebRTC (DTLS-SRTP)
  • Screenshot & screen-recording protection (full block on Android, detect+react on iOS)
  • Firebase for auth/signaling only — your server never sees plaintext or private keys

Project layout

lib/
  main.dart                     — app entry: global screenshot protection, crypto init on cold start
  services/
    crypto_service.dart         — Signal Protocol: identity, prekeys, sessions, encrypt/decrypt
    firestore_service.dart      — all server I/O: prekey bundles, contacts, ciphertext relay, call signaling
    security_service.dart       — screenshot/recording block + detection
    call_service.dart           — WebRTC peer connection setup for calls
    auth_service.dart           — firebase_auth phone sign-in; bootstraps crypto on first sign-in
    local_store.dart            — SQLCipher-encrypted local chat history
  models/models.dart            — Contact, ChatMessage
  screens/
    login_screen.dart           — phone number + SMS code
    contacts_screen.dart        — live contact list, incoming-call listener
    add_friend_screen.dart      — where Signal sessions get established
    chat_screen.dart            — encrypt on send, decrypt on receive, persists locally
    call_screen.dart            — WebRTC UI, handles both caller and callee signaling
android/app/src/main/AndroidManifest.xml   — permissions
ios/Runner/Info-additions.plist.md         — permission strings to merge in

How "only encrypted for added friends" actually works

This isn't just a UI restriction — it's structural:

  1. Each device generates a Signal identity key pair + prekeys (CryptoService.init, called on sign-in and on every cold start via AuthGate in main.dart).
  2. Public prekey bundles get published to Firestore (AuthService._onSignedIn).
  3. Adding a friend (add_friend_screen.dart) fetches their bundle from FirestoreService.fetchPrekeyBundle and runs the X3DH handshake, creating a session in the local Signal store.
  4. encryptMessage()/decryptMessage() require an existing session — there's no code path to encrypt or decrypt for someone you haven't added, because the session simply doesn't exist. Symmetrically, when a friend's first message arrives as a PreKeySignalMessage, decrypting it establishes your side of the session automatically (this is inherent to X3DH — no separate "add back" step is required to reply).

Setup steps to actually run this

  1. Install Flutter (3.19+) and run flutter create . in this directory once, to generate the platform boilerplate this repo doesn't include (ios/Runner/Info.plist, android/build.gradle, etc.) — merge, don't overwrite, since this repo's lib/, pubspec.yaml, and android/app/.../AndroidManifest.xml are already written.
  2. Firebase: create a Firebase project, enable Phone sign-in under Authentication and Cloud Firestore, then run flutterfire configure to generate lib/firebase_options.dart. Uncomment the Firebase.initializeApp line in main.dart.
  3. Firestore security rules: lock these down before shipping — at minimum, a user should only be able to write their own prekey_bundles/{uid} and users/{uid}/contacts/*, and only append (not edit/delete) messages in a chats/{chatId} they're a participant of. The scaffold's rules are permissive by default (whatever your Firebase project ships with) — don't run this in production without writing real rules.
  4. iOS permissions: merge the keys from ios/Runner/Info-additions.plist.md into ios/Runner/Info.plist.
  5. flutter pub get
  6. TURN server: add your TURN credentials in call_service.dart (see caveat below) before testing calls across real networks.
  7. flutter run — test with two devices/accounts to exercise add-friend, messaging, and calling end-to-end.

What's wired up vs. still open

Piece Status
Signal Protocol key generation, session building, encrypt/decrypt Real, functional logic
Firestore signaling: prekey bundles, contacts, message relay, call offer/answer/ICE Wired up (firestore_service.dart)
Phone auth (firebase_auth) Wired up (auth_service.dart, login_screen.dart)
Encrypted text chat end-to-end (send → Firestore → decrypt → persist) Wired up (chat_screen.dart)
Local encrypted message storage (SQLCipher) Wired up (local_store.dart)
WebRTC offer/answer/ICE flow, both caller and callee paths Wired up (call_screen.dart)
Incoming call ringing while not in an open chat Wired up (contacts_screen.dart)
Screenshot/recording protection Real plugin wiring
Firestore security rules Not written — see setup step 3
Screenshot notice sent to the peer (not just shown locally) Not implemented — TODO in chat_screen.dart
Removing/blocking a contact, deleting message history Not implemented
Push notifications for messages/calls while the app is backgrounded Not implemented
Multi-device (same account on phone + desktop) Not implemented — see caveat below

A few honest caveats

  • TURN server: STUN alone (in call_service.dart) won't work for every network. You'll want a TURN server (e.g. coturn, self-hosted or a paid service) for reliable connections behind restrictive NATs — uncomment and fill in the turn: entry in _iceServers.
  • iOS screenshots can't be blocked, only detected — see the note in Info-additions.plist.md.
  • One-time prekey reuse: this scaffold publishes a single prekey (preKeyId: 1) and never rotates it. In production, publish a batch of one-time prekeys and remove each one from the store after a friend consumes it — otherwise multiple people establishing sessions with you concurrently could reuse prekey material, weakening forward secrecy for that first message.
  • Mutual contact add: FirestoreService.addContact adds the contact to both users' lists at once, so the person you add doesn't need a separate "accept" step to see the chat. A production app would probably want an explicit friend-request/accept flow instead.
  • Multi-device support (using the same account on phone + desktop) adds real complexity to the Signal session model — this scaffold assumes one device per user for simplicity (deviceId: 1 throughout).
  • This is a scaffold, not a security audit. Before handling real user data, get the crypto implementation and Firestore security rules reviewed by someone with Signal Protocol / Firebase security experience.

About

Config files for my GitHub profile.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages