summaryrefslogtreecommitdiffstats
path: root/ARCHITECTURE.md
diff options
context:
space:
mode:
Diffstat (limited to 'ARCHITECTURE.md')
-rw-r--r--ARCHITECTURE.md240
1 files changed, 240 insertions, 0 deletions
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
new file mode 100644
index 0000000..412055f
--- /dev/null
+++ b/ARCHITECTURE.md
@@ -0,0 +1,240 @@
+# NymDrop — Architecture v2
+
+## Principio fondante
+
+**La protezione è nel design, non nell'obbligo di installare qualcosa.
+Chi vuole anonimato aggiuntivo usa quello che trova.**
+
+La fonte usa un browser qualsiasi. Il documento viaggia interamente su Nym.
+Il server è un relay cieco — non conserva nulla, non sa cosa ha trasmesso.
+
+---
+
+## Flusso completo
+
+```
+[FONTE] [SERVER NYMDROP] [GIORNALISTA]
+ │ │ │
+ │ HTTPS (browser normale) │ │
+ ├──────────────────────────────▶│ │
+ │ │ │
+ │ WebCrypto nel browser: │
+ │ cifra con chiave pubblica │
+ │ X25519 del giornalista │
+ │ │ │
+ │ POST ciphertext │ │
+ ├──────────────────────────────▶│ │
+ │ │ │
+ │ riceve ciphertext │
+ │ NON lo salva su disco │
+ │ │ │
+ │ nym-client embedded: │
+ │ invia ciphertext al │
+ │ Nym address del giornalista │
+ │ │ │
+ │ │ Nym mixnet (multi-hop) │
+ │ │ cover traffic + pad │
+ │ ├──────────────────────────▶│
+ │ │ │
+ │ cancella ciphertext │
+ │ dalla RAM │
+ │ │ ciphertext arriva
+ │ │ al Nym address
+ │ │ del giornalista
+ │ │ │
+ │ │ giornalista decifra
+ │ │ localmente con
+ │ │ chiave privata X25519
+ │ │ │
+ │ conferma generica "ok" │ │
+ │◀──────────────────────────────│ │
+```
+
+---
+
+## Componenti
+
+### 1. Frontend (browser della fonte)
+
+- HTML + CSS puro, zero framework JS, zero CDN, zero cookie
+- **WebCrypto API** nativa del browser — nessuna libreria esterna:
+ - Chiave pubblica X25519 del giornalista embedded nel HTML al momento del deploy
+ - Genera keypair effimera X25519 per la sessione
+ - ECDH key exchange → shared secret
+ - Deriva chiave ChaCha20-Poly1305 via HKDF
+ - Cifra testo + file nel browser
+ - Manda solo ciphertext + public key effimera al server
+- Il plaintext non lascia mai il browser in chiaro
+- Form: textarea testo libero + upload file opzionale + submit
+- Funziona su qualsiasi browser moderno
+
+### 2. Backend Go — relay cieco
+
+```
+nymdrop/
+├── cmd/
+│ └── nymdrop/
+│ └── main.go ← entrypoint
+├── internal/
+│ ├── handler/
+│ │ ├── submit.go ← POST /submit
+│ │ └── static.go ← serve HTML/CSS/JS
+│ ├── relay/
+│ │ └── relay.go ← ricevi → invia su Nym → cancella
+│ ├── nym/
+│ │ └── client.go ← wrapper nym client embedded
+│ └── nolog/
+│ └── middleware.go ← strip IP, UA, headers — zero log
+├── static/
+│ ├── index.html ← form submission
+│ └── crypto.js ← WebCrypto lato client
+├── go.mod
+└── CLAUDE.md
+```
+
+**Comportamento del relay:**
+```
+ricevi ciphertext da HTTP POST
+ ↓
+passa a nym/client.go → Send(journalistNymAddress, ciphertext)
+ ↓
+Nym mixnet consegna al giornalista
+ ↓
+cancella ciphertext dalla memoria (zeroing)
+ ↓
+rispondi HTTP 200 "ok"
+```
+
+Zero scritture su disco. Zero database. Zero log.
+
+### 3. Nym client embedded lato server
+
+Il server incorpora `nym-socks5-client` come subprocess gestito:
+
+```go
+// nym/client.go
+type NymClient struct {
+ addr string // Nym address del giornalista
+ process *exec.Cmd
+ socks5 string // localhost:1080
+}
+
+func (c *NymClient) Send(payload []byte) error {
+ // connessione SOCKS5 → Nym mixnet → giornalista
+ conn, _ := proxy.SOCKS5("tcp", c.socks5, nil, proxy.Direct)
+ // ...
+}
+```
+
+Evoluzione futura: FFI bindings Rust/Go via `sdk/ffi/shared/` — client in-process, zero subprocess.
+
+### 4. Lato giornalista
+
+Il giornalista ha un **Nym address** dedicato a NymDrop.
+Riceve i documenti cifrati direttamente sul suo client Nym (NymConnect o nym-socks5-client locale).
+Decifra localmente con la sua chiave privata X25519 — il server non è mai coinvolto nella decifrazione.
+
+Nessuna interfaccia admin sul server — il server non ha nulla da mostrare perché non conserva nulla.
+
+---
+
+## No-log by design
+
+```go
+func NoLog(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ r.Header.Del("X-Forwarded-For")
+ r.Header.Del("X-Real-IP")
+ r.Header.Del("User-Agent")
+ r.RemoteAddr = "0.0.0.0:0"
+ next.ServeHTTP(w, r)
+ })
+}
+```
+
+- Zero access log
+- Zero error log con IP
+- Zero timestamp salvati
+- Rate limiting via token bucket anonimo (contatore in RAM, reset periodico)
+
+---
+
+## Threat model
+
+| Minaccia | Protetto? | Come |
+|---|---|---|
+| Server legge documento | ✅ | Cifrato WebCrypto lato client — server vede solo ciphertext |
+| Server logga IP fonte | ✅ | No-log middleware, zero access log |
+| Sequestro server | ✅ | Zero documenti su disco — relay puro, inbox sempre vuota |
+| Traffic analysis fonte→server | ⚠️ | HTTPS normale — fonte usa quello che trova |
+| Traffic analysis server→giornalista | ✅ | Nym mixnet multi-hop, cover traffic, padding |
+| Correlazione timing | ✅ | Nym Loopix delays randomizzati |
+| Intercettazione documento in transito | ✅ | ChaCha20-Poly1305, solo destinatario decifra |
+| Compromissione chiave privata giornalista | ❌ | Fuori scope — responsabilità del giornalista |
+
+**Punto chiave per il grant:** sequestro del server = zero documenti recuperabili. Il server è matematicamente incapace di rivelare il contenuto delle submission — non perché lo voglia, ma perché non lo ha mai avuto.
+
+---
+
+## Deploy single container
+
+```bash
+# Proxmox — CT125
+pct create 125 /var/lib/vz/template/cache/debian-12.tar.zst \
+ --hostname nymdrop --memory 512 --cores 1
+
+# Inside container
+apt install -y golang-go
+git clone https://github.com/gabrix73/nymdrop
+cd nymdrop
+go build ./cmd/nymdrop
+
+# Config
+cat > /etc/nymdrop/config.toml << EOF
+listen = ":443"
+journalist_nym_address = "<NYM_ADDRESS>"
+journalist_pubkey = "<X25519_PUBLIC_KEY_HEX>"
+tls_cert = "/etc/ssl/nymdrop/cert.pem"
+tls_key = "/etc/ssl/nymdrop/key.pem"
+EOF
+
+./nymdrop --config /etc/nymdrop/config.toml
+```
+
+---
+
+## Struttura repo
+
+```
+gabrix73/nymdrop
+├── cmd/nymdrop/
+├── internal/
+├── static/
+├── docs/
+│ ├── ARCHITECTURE.md
+│ ├── THREAT_MODEL.md
+│ └── DEPLOY.md
+├── CLAUDE.md
+├── LICENSE ← AGPL-3.0
+├── README.md
+└── go.mod
+```
+
+---
+
+## Pitch grant (sintesi)
+
+NymDrop è il primo sistema di whistleblowing dove:
+- La fonte usa un browser qualsiasi — zero installazioni
+- Il documento viaggia end-to-end su Nym mixnet
+- Il server è un relay cieco — zero storage, zero metadati
+- Sequestro del server = zero documenti recuperabili
+- Resistente a traffic analysis globale (Loopix delays, cover traffic)
+
+Nessun sistema esistente combina questi quattro punti simultaneamente.
+
+---
+
+## Prossimo passo
+
+Scaffolding Go: `main.go` + `nolog/middleware.go` + `handler/submit.go` + `static/index.html` + `static/crypto.js`