L'app è ancora in fase di sviluppo, quindi la crittografia dei contenuti va attualmente considerata "best-effort". Prima del lancio pubblico, prevediamo di rendere open source il codice relativo alla logica crittografica.
Di seguito una descrizione dei tre livelli:
Quando installi l'app per la prima volta, il tuo telefono genera una coppia di chiavi asimmetriche X25519 interamente in locale, dentro un modulo Rust isolato dall'app principale. Queste non escono mai dal dispositivo in forma non cifrata.
Chiave privata → rimane nel Secure Storage del telefono
Chiave pubblica → inviata ai server Bho! e associata all'account
X25519 è la stessa curva ellittica usata da Signal e WhatsApp per lo scambio di chiavi. È standardizzata dall'IETF (RFC 7748) e considerata sicura fino a ~128 bit di sicurezza equivalente simmetrica.
Cosa succede se reinstalli l'app? Le chiavi vengono perse e quindi rigenerate. Il server notifica ai Gatekeeper delle tue stanze che un membro ha reinstallato, e loro rieseguono automaticamente la distribuzione delle chiavi di gruppo. Questo, ovviamente, rompe l'accesso a qualsiasi sessione precedente.
Ogni stanza ha una GEK: una chiave ChaCha20-Poly1305 a 256bit generata dal creatore al momento della creazione. La GEK è:
La GEK cifra tutto ciò che la stanza produce: messaggi, post, thumbnail. Il server non la vede mai. Ogni rotazione genera una nuova GEK con epoch crescente. I post più vecchi rimangono decifrabili con la GEK dell'epoca in cui sono stati pubblicati, che viene inclusa nel Welcome Kit dei nuovi membri.
Ogni singolo post ha la sua DEK: una chiave ChaCha20-Poly1305 a 256bit usata esclusivamente per il post. La DEK cifra il file multimediale, poi questa viene cifrata con la GEK e allegata al post come campo delBFF Header.
File grezzo → [ChaCha20-Poly1305 con DEK] → File cifrato
DEK → [ChaCha20-Poly1305 con GEK] → BFF Header
Questa struttura a due strati significa che ruotare la GEK (come nel caso di un cambio di epoch) non richiede di re-cifrare ogni post, ma basta distribuire la vecchia GEK ai nuovi membri per rendere i contenuti visibili ai nuovi arrivati.
| Operazione | Algoritmo | Standard |
|---|---|---|
| Scambio di chiavi | X25519 ECDH | RFC 7748 |
| Derivazione chiave | HKDF-SHA256 | RFC 5869 |
| Cifratura simmetrica | ChaCha20-Poly1305 | RFC 8439 |
| Autenticazione messaggi | POLY1305 (integrato) | — |
| Cifratura file (chunked) | ChaCha20-Poly1305, chunk 64 KB | — |
| Hash numero di telefono | HMAC-SHA256 (blind index) | RFC 2104 |
Perché ChaCha20-Poly1305 invece di AES? Sui dispositivi di test ChaCha20 si è dimostrato più veloce di AES, rimanendo altrettanto sicuro. È usato da TLS 1.3, WireGuard e dal protocollo Signal. Siamo in buona compagnia.
Perché non AES-GCM? AES-GCM è vulnerabile a riusi di nonce se l'implementazione non è attenta. In una prima versione di Bho!, l'implementazione con AES-GCM aveva portato ad una grave falla di sicurezza in questo senso. Abbiamo risolto passando a ChaCha20-Poly1305 e rivedendo l'implementazione.
Quando un Gatekeeper approva la tua richiesta d'accesso, la logica eseguita è la seguente:
1. Il Gatekeeper genera una chiave effimera X25519
2. Poi esegue Diffie-Hellman tra la chiave effimera e la tua chiave pubblica
3. Il segreto condiviso viene espanso con HKDF-SHA256
4. La GEK e l'history bundle (le vecchie GEK) vengono cifrati con ChaCha20-Poly1305
5. Il bundle così creato arriva al server
6. Il server lo consegna al tuo telefono
7. Il telefono esegue Diffie-Hellman inverso e decifra la GEK
8. Il bundle viene cancellato dal server
Il server in tutto ciò non vede mai i contenuti che trasporta, non avendo mai accesso alle chiavi in chiaro.
Quando chiedi di entrare in una stanza, il tuo telefono cifra un messaggio (es. "Ciao, sono Mario") con la chiave pubblica dell'Gatekeeper. Il Gatekeeper è l'unico in grado di decifrarlo.
Mario genera chiave effimera X25519
Diffie-Hellman con chiave pubblica del Gatekeeper → segreto condiviso
HKDF → chiave simmetrica
ChaCha20-Poly1305 cifra il messaggio
Bundle → server → Gatekeeper
I file multimediali vengono cifrati in blocchi da 64 KB, ognuno con un nonce deterministico derivato dall'indice del chunk. Questo permette al player video di decifrare range arbitrari del file senza scaricare tutto, rendendo possibile "andare avanti e indietro" nei video senza bisogno di decifrare l'intero file.
Header [8 byte: dimensione plaintext in LE]
Chunk 0 [64 KB cifrati + 16 byte tag Poly1305]
Chunk 1 [64 KB cifrati + 16 byte tag Poly1305]
...
Chunk N [bytes restanti + 16 byte tag]
Ogni messaggio usa la GEK dell'epoca corrente con un nonce casuale a 12 byte, generato appositamente per ogni messaggio. Limitazione nota: non c'è il Double Ratchet di Signal. Due messaggi consecutivi usano la stessa GEK. La forward secrecy è garantita solo a livello di epoch (rotazione GEK).
I media effimeri seguono lo stesso schema di cifratura dei post normali. La differenza è che il server traccia chi li ha visualizzati e, quando tutti i membri li hanno visti, cancella il file dal disco e il record dal database. Il server non può aprire il contenuto (è cifrato come il resto dei post), ma controlla la logica di "consumo".
| Feature | Bho! | Signal | Telegram (Cloud) |
|---|---|---|---|
| Cifratura contenuti | E2E (GEK+DEK) | E2E (Double Ratchet) | Server-side |
| Cifratura chat gruppi | E2E (GEK) | E2E | Server-side |
| Forward secrecy/msg | ✗ (per epoch) | ✓ | ✗ |
| Metadati al server | Sì | Minimizzati | Sì |
| Open source | Parziale (previsto) | Completo | Solo Client |
Nota Telegram: le chat "normali" e i gruppi di Telegram non sono end-to-end encrypted. Solo le "Secret Chats" lo sono. Bho! invece cifra tutto di default.
Dove Signal è superiore: il Double Ratchet garantisce che compromettere la chiave attuale non espone i messaggi passati. Bho! offre questa garanzia a livello di epoch (cambia chiave all'espulsione).
La nostra ambizione:: ambiamo a diventare "il Signal dei social network", e lavoreremo in questo senso, riducendo nel tempo la traccia che gli utenti lasciano sui nostri server.
Essendo ancora in beta, ci siamo occupati di sviluppare il modello core, per ciò per ora proteggiamo solo il contenuto, non i metadati (ma l'obiettivo rimane rendere crittografati anche questi).
| Scenario | Protetto? | Note |
|---|---|---|
| Server Bho! compromesso | ✓ (contenuto) | Metadati esposti |
| Intercettazione di rete | ✓ | TLS + E2E |
| Ordine legale a Bho! | ✓ (contenuto) | Metadati cedibili |
| Firebase/Google | ✗ (metadati) | FCM vede timing push |