This project implements a peer-to-peer, end-to-end encrypted chat application in Rust. The application enables decentralized, direct communication between two users, supporting local encryption and decryption to ensure ubiquitous confidentiality. Specifically, our goals were:
- Developing a peer-to-peer communication protocol using UDP (QUIC)
- Implementing onion routing for secure communication (Kademlia DHT)
- Implementing secure key exchange and secure session management (Ed25519)
- Supporting real-time bidirectional messaging between users through onion-routed circuits
- Implementing a simple TUI (Ratatui)
Contributors:
- Mewski
- aaggupta07
- thealtofwar
This project implements a decentralized, anonymous peer-to-peer chat application using Rust and leveraging the libp2p networking stack. The project provides strong privacy guarantees through onion routing and end-to-end encryption.
Networking Stack (libp2p):
- Transport Layer: QUIC over UDP for encrypted, multiplexed connections with built-in flow control
- Kademlia DHT: Distributed hash table for peer discovery and routing table management
- GossipSub: Publish-subscribe protocol for efficient message propagation across the network
- Custom Onion Routing: Custom multi-hop routing protocol built on top of Kademlia for anonymous communication
Identity and Authentication:
- Each peer generates an Ed25519 keypair on first launch and saves it to disk (or TPM)
- The peer's public key serves as their unique network identity
- Optional DNS TXT record integration allows users to associate human-readable domain names with their Ed25519 public keys (e.g.,
chat-key.example.com->ed25519:DEADBEEFDEADBEEF...) - Peers can share shortened identifiers using their own domains instead of full 256-bit public keys
Onion Routing Protocol: The application implements a custom onion routing system to provide anonymity:
- Source peer queries Kademlia DHT to discover 3-5 random relay nodes
- Source encrypts the message in layers (like an onion), where each layer contains:
- Next hop's peer ID
- Encrypted payload for the next hop
- Each relay node decrypts one layer, forwards to the next hop, and cannot determine if it's the final destination
- Only the final destination can decrypt the innermost layer containing the actual message
- Return path uses the same relay chain in reverse to maintain anonymity
End-to-End Encryption Specification: Messages between two peers are encrypted with a double ratchet-inspired protocol, similar in design to TLS 1.3 but adapted for persistent peer-to-peer sessions. Like TLS, we perform an initial key exchange using asymmetric cryptography (X25519 ECDH) to establish a shared secret, then use symmetric encryption (ChaCha20-Poly1305) for all subsequent messages. However, unlike TLS which is designed for client-server connections, our protocol maintains long-lived sessions with forward secrecy through continuous key ratcheting, inspired by the Signal protocol.
-
Initial Key Exchange (similar to TLS Handshake):
- Peers exchange ephemeral X25519 (Curve25519) public keys
- Both peers perform ECDH to derive a shared secret
- The shared secret is used with HKDF-SHA256 to derive:
root_key: Used for deriving chain keyschain_key_send: For encrypting outgoing messageschain_key_recv: For decrypting incoming messages
-
Message Encryption:
- Each message uses a unique symmetric key derived from the current
chain_key - Encryption: ChaCha20-Poly1305 AEAD
- After each message, the chain key is ratcheted forward:
chain_key_new = HKDF(chain_key_old, constant) - Messages include a counter to handle out-of-order delivery
- Each message uses a unique symmetric key derived from the current
-
Forward Secrecy:
- Chain keys are ratcheted after each message
- Old keys are immediately wiped from memory
- Compromise of current keys does not reveal past messages
-
Onion Layer Independence:
- E2EE encryption happens before onion routing encryption
- Relay nodes can only decrypt their onion layer (routing information)
- Relay nodes cannot decrypt the inner E2EE message content
- Only the destination peer, holding the corresponding X25519 private key, can decrypt the message
User Interface:
- Terminal User Interface (TUI) built with Ratatui
- Features:
- Contact list with online status indicators
- Message composition area
- Network status and routing information panel
- Key fingerprint verification display
- libp2p - Modular peer-to-peer networking stack
- Ratatui - Terminal user interface library
- rust-libp2p Documentation - libp2p protocol specifications
- Kademlia: A Peer-to-peer Information System Based on the XOR Metric - Original Kademlia DHT whitepaper
- Signal Protocol - Inspiration for the double ratchet E2EE protocol
- Tor Project - Onion routing architecture reference
- Age Encryption - Modern file encryption format using X25519
- ed25519_dalek Documentation - Implementation of Ed25519 keys in Rust
- x25519_dalek Documentation - Implementation of X25519 keys in Rust
- RustCrypto - Cryptography functions, including Argon2, ChaChaPoly1305, and HKDF
- libsignal - Some code adapted for chain key ratcheting mechanism
- rust-libp2p - Some code adapted from examples and tests
- Ratatui - Some code adapted from widget examples and tests
- "Duplicate Key Events on Windows" - Resolved a double-press issue on Windows