Skip to content

Encrypted P2P Chat

Carter Perez edited this page Dec 9, 2025 · 1 revision

Encrypted P2P Chat

Real-time encrypted messaging application using Signal Protocol encryption with passwordless WebAuthn authentication.

Overview

A secure chat application implementing end-to-end encryption with forward secrecy and break-in recovery. Features passwordless authentication via WebAuthn/Passkeys and real-time messaging through WebSockets and SurrealDB live queries.

Status: Complete | Difficulty: Advanced

Tech Stack

Backend

Technology Version Purpose
FastAPI 0.121+ Async Python web framework
PostgreSQL 16 User auth & credentials
SurrealDB 1.0 Real-time messaging (live queries)
Redis 7.1 Challenge/session storage
SQLModel 0.0.27 SQLAlchemy + Pydantic hybrid
Alembic 1.17+ Async migrations
PyNaCl 1.6 Encryption primitives
liboqs-python 0.14 Post-quantum cryptography

Frontend

Technology Version Purpose
SolidJS 1.9 Fine-grained reactivity
TypeScript 5.9 Type safety
Vite 7.2 Build tool (Node 20.19+)
Tailwind CSS v4 Styling
@tanstack/solid-query 5.90 Server state
nanostores 1.1 Lightweight state

Infrastructure

  • Docker + Docker Compose
  • Nginx reverse proxy
  • Makefile automation

Features

Authentication

  • Passwordless login with WebAuthn/Passkeys (FIDO2)
  • Discoverable credentials (device-based)
  • Multi-device support
  • Signature counter verification

Encryption (Signal Protocol)

  • Double Ratchet for message encryption
  • X3DH key exchange for async messaging
  • Forward secrecy
  • Break-in recovery
  • Out-of-order message handling
  • Per-message AES-256-GCM

Real-Time Messaging

  • WebSocket connections with management
  • SurrealDB live queries
  • Online/offline presence
  • Typing indicators
  • Read receipts
  • Heartbeat keep-alive

Architecture

┌─────────────────────────────────────────────────────────┐
│                   Frontend (SolidJS)                     │
│  ┌─────────┐  ┌──────────┐  ┌─────────┐  ┌───────────┐ │
│  │  Auth   │  │   Chat   │  │  Rooms  │  │  Crypto   │ │
│  │ WebAuthn│  │ Messages │  │  List   │  │  Service  │ │
│  └────┬────┘  └────┬─────┘  └────┬────┘  └─────┬─────┘ │
└───────┼────────────┼─────────────┼─────────────┼───────┘
        │            │             │             │
        ▼            ▼             ▼             ▼
┌─────────────────────────────────────────────────────────┐
│              WebSocket + REST API (Nginx)                │
└─────────────────────────┬───────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────┐
│                   Backend (FastAPI)                      │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │                    Services                       │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────┐   │   │
│  │  │   Auth   │  │ Message  │  │   Presence   │   │   │
│  │  │ WebAuthn │  │ Encrypt  │  │   Tracking   │   │   │
│  │  └──────────┘  └──────────┘  └──────────────┘   │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────┐   │   │
│  │  │  Prekey  │  │WebSocket │  │   Ratchet    │   │   │
│  │  │  X3DH    │  │ Manager  │  │   State      │   │   │
│  │  └──────────┘  └──────────┘  └──────────────┘   │   │
│  └──────────────────────────────────────────────────┘   │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │                  Encryption Core                  │   │
│  │  double_ratchet.py  |  x3dh_manager.py           │   │
│  └──────────────────────────────────────────────────┘   │
└────────┬──────────────────┬──────────────────┬──────────┘
         │                  │                  │
         ▼                  ▼                  ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ PostgreSQL  │    │  SurrealDB  │    │    Redis    │
│ Users/Creds │    │  Messages   │    │  Sessions   │
│ Keys/Prekeys│    │ Live Queries│    │ Challenges  │
└─────────────┘    └─────────────┘    └─────────────┘

Key Exchange Flow (X3DH)

Alice                                              Bob
  │                                                  │
  │  1. Fetch Bob's prekey bundle                   │
  │  ──────────────────────────────────────────────>│
  │                                                  │
  │  2. Bundle: IdentityKey, SignedPrekey,          │
  │     OneTimePrekey, Signature                    │
  │  <──────────────────────────────────────────────│
  │                                                  │
  │  3. Verify signature                            │
  │  4. Generate ephemeral keypair                  │
  │  5. Compute shared secrets (DH):                │
  │     DH1 = DH(IK_A, SPK_B)                       │
  │     DH2 = DH(EK_A, IK_B)                        │
  │     DH3 = DH(EK_A, SPK_B)                       │
  │     DH4 = DH(EK_A, OPK_B)                       │
  │  6. Derive root key: HKDF(DH1||DH2||DH3||DH4)  │
  │  7. Initialize Double Ratchet                   │
  │                                                  │
  │  8. Send initial message + EK_A public         │
  │  ──────────────────────────────────────────────>│
  │                                                  │
  │                    9. Bob computes same secrets │
  │                   10. Initializes Double Ratchet│
  │                                                  │

Double Ratchet

Each message uses a unique key derived from the ratchet state:

Root Key ─────┬─────> Chain Key 1 ──> Message Key 1
              │                   ──> Message Key 2
              │
              └─────> Chain Key 2 ──> Message Key 3
                                  ──> Message Key 4
  • Symmetric ratchet: Derives new message keys
  • DH ratchet: Updates root key on each reply
  • Forward secrecy: Old keys deleted after use
  • Break-in recovery: New DH ratchet heals compromise

Quick Start

cd PROJECTS/encrypted-p2p-chat

# Copy environment file
cp .env.example .env

# Start development environment
make dev

# Access at http://localhost:3000

Configuration

Key environment variables:

# PostgreSQL
DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/chat

# SurrealDB
SURREAL_URL=ws://localhost:8000/rpc
SURREAL_NS=chat
SURREAL_DB=messages

# Redis
REDIS_URL=redis://localhost:6379

# WebAuthn
RP_ID=localhost
RP_NAME=Encrypted Chat
RP_ORIGIN=http://localhost:3000

Database Models

Model Database Purpose
User PostgreSQL User accounts
Credential PostgreSQL WebAuthn credentials
IdentityKey PostgreSQL Long-term identity keys
SignedPrekey PostgreSQL Signed prekeys for X3DH
OneTimePrekey PostgreSQL One-time prekeys
RatchetState PostgreSQL Double ratchet state
SkippedMessageKey PostgreSQL Out-of-order handling
Messages SurrealDB Encrypted message storage

Frontend Stores

Store Purpose
authStore Authentication state
messagesStore Message state
roomsStore Chat room state
presenceStore Online/offline status
typingStore Typing indicators
settingsStore User preferences

Development

# Backend
make backend-lint
make backend-test

# Frontend
make frontend-lint
make frontend-build

# Both
make lint
make test

Security Considerations

  • All messages encrypted client-side before transmission
  • Server never sees plaintext messages
  • Keys stored securely, old keys deleted
  • WebAuthn prevents phishing attacks
  • No passwords to steal or crack

Source Code

View on GitHub

Clone this wiki locally