Zdecentralizowany System Komunikacji P2P z Szyfrowaniem End-to-End w języku Rust

Rustacean pride (trans)

Autorka: Lukrecja Pleskaczyńska

Dane formalne: Paweł Pleskaczyński

Promotor: dr inż. Rafał Brodziak

WSB Merito Poznań
Wydział Finansów i Bankowości
Kierunek: Informatyka
Poznań 2026

Grafika: @aldeka (CC BY)

Plan prezentacji

  • Uzasadnienie problemu i cel projektu
  • Metody i organizacja pracy
  • Architektura i mechanizm mailbox
  • Bezpieczeństwo i interfejsy
  • Wyniki, ograniczenia, wnioski

Uzasadnienie wyboru problemu

Pytanie badawcze: Jak zaprojektować zdecentralizowany komunikator P2P bez serwera aplikacyjnego, z szyfrowaniem end‑to‑end i asynchronicznym dostarczaniem wiadomości?

Dlaczego to istotne?

  • Centralizacja: pojedynczy punkt awarii i kontroli
  • Metadane: możliwość odtwarzania relacji społecznych
  • Prywatność: dane przechowywane u operatora
  • Zależność: komunikacja zależna od dostępności serwera

Cel i zakres projektu

Cel główny

Stworzenie bezpiecznego komunikatora P2P w języku Rust, umożliwiającego wysyłanie wiadomości do użytkowników nieaktywnych w sieci.

Cele szczegółowe

  • Wykrywanie węzłów w sieci lokalnej (mDNS)
  • Mailbox: przechowywanie wiadomości dla nieaktywnych
  • Szyfrowanie end‑to‑end
  • Dwa interfejsy: TUI i Web UI

Zakres

  • Przestrzenny: sieć lokalna (LAN)
  • Funkcjonalny: wiadomości tekstowe 1‑na‑1
  • Architektura: P2P bez serwera aplikacyjnego
  • Persystencja: lokalna baza danych sled

Metody i organizacja pracy

Metody i techniki

  • Przegląd literatury i dokumentacji (P2P, kryptografia)
  • Prototypowanie iteracyjne
  • Testy funkcjonalne w sieci lokalnej
  • Analiza ruchu w Wireshark
  • Proste testy obciążeniowe

Metodyka pracy

  • Inicjacja → planowanie → realizacja
  • Kontrola i kamienie milowe
  • Zamknięcie projektu i wnioski

Realizacja zgodna z wymaganiami pracy projektowej.

Przegląd rozwiązań

System Architektura Dostarczanie do nieaktywnych Szyfrowanie
Signal Scentralizowana Tak (serwer) Double Ratchet
Matrix Federacyjna Tak (serwery) Olm / Megolm
Briar P2P + Tor Częściowo (Bluetooth) Własne E2E
Tox P2P (DHT) Nie NaCl box
PROJEKT P2P (libp2p) Tak (mailbox) Noise + schemat inspirowany HPKE
Wkład: asynchroniczne dostarczanie wiadomości w P2P bez serwera aplikacyjnego.

Architektura systemu

Diagram architektury systemu
  • Warstwa sieciowa: libp2p, mDNS, Kademlia
  • Transport: TCP + Noise + Yamux
  • Persystencja: lokalna baza sled
  • Interfejsy: TUI i Web UI
// network/behaviour.rs
#[derive(NetworkBehaviour)]
pub struct P2PBehaviour {
    pub chat: ChatBehaviour,           // wiadomości bezpośrednie
    pub mailbox: MailboxBehaviour,     // magazyn offline
    pub discovery: DiscoveryBehaviour, // discovery = mDNS + Kademlia
    pub ping: ping::Behaviour,         // utrzymanie połączeń
}

Zachowanie sieciowe łączy czat, mailbox i odkrywanie.

Mechanizm mailbox – przepływ

Przepływ mailbox
  • Wiadomości trafiają do węzłów mailbox, gdy odbiorca jest poza siecią
  • Dane są zaszyfrowane end‑to‑end – mailbox nie widzi treści
  • Po odbiorze następuje potwierdzenie i usunięcie z magazynu
  • Replikacja u co najmniej dwóch dostawców zwiększa niezawodność

Odkrywanie mailboxów przez DHT

1. Węzeł mailbox ogłasza usługę:
   start_providing(klucz_usługi_mailbox)

2. Nadawca pyta DHT:
   get_providers(klucz_usługi_mailbox)
   → [peer_1, peer_2, ...]

3. Trasowanie Kademlii (metryka XOR):
   Nadawca → węzły coraz bliższe kluczowi → dostawcy

4. Nadawca wybiera 2+ najlepsze węzły
   (lokalny ranking) i wysyła zaszyfrowaną
   wiadomość + identyfikator odbiorcy
   (hash klucza publicznego)

DHT zapewnia odkrywanie usług bez centralnego rejestru.

Wybrane fragmenty implementacji

Mailbox: zapis i ogłoszenie

// network/handlers/mailbox.rs
MailboxRequest::Put { recipient, message } => {
    match storage.store_message(recipient, message).await {
        Ok(()) => {
            self.start_providing_for_recipient(recipient)?;
            MailboxResponse::PutResult { success: true }
        }
        Err(_) => MailboxResponse::PutResult { success: false },
    }
}

Po zapisie węzeł ogłasza usługę w DHT.

Ponowienia z jitterem

// sync/retry.rs
pub fn exponential_backoff_with_jitter(&self, attempt: u32) -> Duration {
    let base = self.exponential_backoff(attempt);
    let jitter = rand::random::<u64>() % (base.as_millis() as u64 / 4 + 1);
    Duration::from_millis(base.as_millis() as u64 + jitter)
}

Wygładza skoki obciążenia sieci.

mDNS + Kademlia

// net/discovery.rs
pub fn new(local_peer_id: PeerId) -> Result<Self> {
    let mdns = mdns::tokio::Behaviour::new(mdns::Config::default(), local_peer_id)?;
    let store = kad::store::MemoryStore::new(local_peer_id);
    let mut kademlia = kad::Behaviour::new(local_peer_id, store);
    kademlia.set_mode(Some(kad::Mode::Server));
    Ok(Self { mdns, kademlia })
}

Automatyczne wykrywanie w LAN + DHT.

Web API: wysyłanie

// web/api.rs
let encrypted = node.identity.encrypt_for(&friend.e2e_public_key, req.content.as_bytes())?;
let message = Message { id: Uuid::new_v4(), sender: node.identity.peer_id, recipient: peer_id, ... };
node.history.store_message(message.clone()).await?;
node.outbox.add_pending(message.clone()).await?;
tokio::spawn(async move { network.send_message(peer_id, message).await; });

Walidacja, szyfrowanie, zapis i wysyłka.

Ranking i potwierdzenia mailbox

Ranking dostawców

// sync/engine/discovery/ranking.rs
let mut providers: Vec<_> = candidates
    .into_iter()
    .filter(|peer| self.backoff_manager.can_attempt(peer))
    .collect();

providers.sort_by(|a, b| {
    let sa = self.calculate_mailbox_score(*a);
    let sb = self.calculate_mailbox_score(*b);
    sb.partial_cmp(&sa).unwrap_or(Ordering::Equal)
});

Preferuje węzły niezawodne i szybkie.

ACK z ponowieniami

// sync/engine/mailbox/ack.rs
let retry = RetryPolicy::fast_mailbox();
let ack_result = retry.retry_with_jitter(|| async {
    network.mailbox_ack(peer_id, recipient_hash, msg_ids.clone()).await
}).await;

Zamyka cykl życia wiadomości.

Szyfrowanie w spoczynku

// crypto/storage.rs
let mut key = [0u8; 32];
argon2.hash_password_into(password.as_bytes(), salt, &mut key)?;
let cipher = ChaCha20Poly1305::new(Key::from_slice(&key));
let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, data)?;

Opcjonalne szyfrowanie danych w bazie.

Historia rozmów

// storage/history.rs
let conversation_id = Self::get_conversation_id(&msg.sender, &msg.recipient);
let key = Self::make_composite_key(&conversation_id, msg.timestamp, msg.nonce);
let value = self.serialize_message(&msg)?;
self.tree.insert(key, value)?;
self.tree.flush_async().await?;

Klucze kompozytowe = szybkie zapytania.

Bezpieczeństwo komunikacji

Obrona wielowarstwowa: szyfrowanie transportu (Noise) + szyfrowanie treści (schemat inspirowany HPKE).

Warstwa transportowa (Noise)

  • Uwierzytelniony handshake
  • Poufność postępująca na poziomie sesji
  • ChaCha20‑Poly1305
Zaszyfrowany ruch w Wireshark

Zaszyfrowany ruch w analizie pakietów

Zabezpiecza kanał TCP między węzłami.

Warstwa aplikacyjna (E2E)

  • X25519 + SHA‑256 + ChaCha20‑Poly1305
  • Schemat inspirowany HPKE
  • Mailbox nie ma dostępu do treści
// crypto/hpke.rs (schemat inspirowany)
let shared = sk.diffie_hellman(&pk); // X25519
let key = sha256(shared);           // KDF
let nonce = random_nonce();         // losowy nonce
let ct = aead.encrypt(&nonce, msg); // ChaCha20-Poly1305

Zabezpiecza treść wiadomości niezależnie od transportu.

Interfejsy użytkownika

TUI (crossterm)

TUI
  • Praca w terminalu
  • Małe wymagania sprzętowe
  • Wygodne dla administratorów

Web UI (Vue 3)

Web UI
  • REST API + WebSocket
  • Aktualizacje w czasie rzeczywistym
  • Intuicyjna obsługa

Krótka demonstracja

Materiał pokazuje działanie systemu w praktyce.

Wyniki wydajnościowe

Opóźnienie vs liczba węzłów

Mediana opóźnienia: 2,1 → 4,6 ms (5 → 25 węzłów)

Przepustowość

Przepustowość: 161 → 132 msg/s

Pamięć RAM

Pamięć: 34–43 MB (średnio), do ok. 131 MB

Churn

Skuteczność dostarczeń: 100% przy churn 5–70%

Ograniczenia i kompromisy

Techniczne

  • Brak NAT traversal – praca w LAN
  • Brak czatów grupowych
  • Brak transferu plików
  • Poufność postępująca tylko per‑sesja

Bezpieczeństwo / metadane

  • Metadane (PeerId, czas) są widoczne
  • Brak zewnętrznego audytu bezpieczeństwa
  • Brak ochrony metadanych (np. Tor)
Wniosek: Ograniczenia są świadomymi kompromisami zakresu pracy inżynierskiej.

Wnioski i kierunki rozwoju

Wnioski

  • Asynchroniczne P2P bez serwera jest wykonalne
  • Mailbox zapewnia wysoką skuteczność dostarczeń
  • Dwuwarstwowe szyfrowanie chroni treść wiadomości
  • Architektura jest modularna i rozszerzalna

Kierunki rozwoju

  • NAT traversal (STUN/TURN)
  • Poufność postępująca per wiadomość (Double Ratchet)
  • Czaty grupowe (MLS/TreeKEM)
  • Transfer plików i multimedia
  • Ochrona metadanych (Tor / sieci mieszające)
Dziękuję za uwagę!