tuimail/PROTON.md
2026-02-22 18:57:26 +01:00

83 lines
3.9 KiB
Markdown

# ProtonMail Mini-Bridge
A minimal ProtonMail Bridge implementation in Rust that exposes local IMAP and SMTP servers,
allowing `skim` (and any other standard email client) to connect without code changes.
The full ProtonMail Bridge is ~50,000 lines of Go. This mini-bridge targets only the subset
of functionality that `skim` needs: one account, INBOX only, one concurrent client.
## Components
### 1. Project scaffold
New binary crate with `Cargo.toml` dependencies (`tokio`, `reqwest`, `proton-srp`, `rpgp`,
`serde`, `toml`). Config file format covering ProtonMail credentials and local bind ports.
### 2. ProtonMail authentication
SRP 6a login flow against the Proton API:
- POST `/auth/info` with username → receive modulus, server ephemeral, salt
- Compute SRP proof locally using `proton-srp`
- POST `/auth/login` with client proof → receive access token + encrypted private keys
- Handle optional TOTP 2FA interactively on first run
- Persist session (access token + refresh token) to disk to avoid re-authenticating on restart
### 3. ProtonMail API client
Thin `reqwest`-based HTTP wrapper around the endpoints the bridge needs:
- List messages (with pagination)
- Fetch single message (metadata + encrypted body)
- Delete message
- Fetch recipient public key (for outbound encryption)
- Send message (modelled from the open-source `ProtonMail/proton-bridge` Go implementation)
### 4. Crypto layer
Using `rpgp` or `proton-crypto-rs`:
- Decrypt the user's private key (delivered encrypted by the API, unlocked with the mailbox password)
- Decrypt incoming message bodies with the private key
- Encrypt outbound messages to recipient public keys
### 5. Message store
In-memory (optionally persisted) mapping between IMAP sequence numbers and Proton message IDs.
IMAP uses stable sequential integers; Proton uses opaque string IDs. The store must:
- Assign sequence numbers to messages in order
- Renumber correctly after deletes
- Survive restarts without breaking existing client state
### 6. IMAP server
TCP listener on `localhost:143` (configurable). Implements the nine commands `skim` uses:
| Command | Purpose |
|---------|---------|
| `LOGIN` | Accept local credentials (no real auth needed) |
| `NOOP` | Keepalive / connection check |
| `SELECT INBOX` | Open mailbox, report message count |
| `FETCH range BODY.PEEK[HEADER.FIELDS (SUBJECT FROM DATE)]` | List emails |
| `FETCH seq BODY.PEEK[]` | Fetch full message body |
| `SEARCH OR SUBJECT "..." FROM "..."` | Search by subject or sender |
| `STORE seq +FLAGS (\Deleted)` | Mark for deletion |
| `EXPUNGE` | Delete marked messages |
| `LOGOUT` | Disconnect |
Each command translates to API client + crypto layer calls via the message store.
### 7. SMTP server
TCP listener on `localhost:587` (configurable). Minimal implementation:
- EHLO, AUTH, MAIL FROM, RCPT TO, DATA, QUIT
- On DATA completion: hand the message to the crypto layer to encrypt, then POST via API client
## Build order
Components 2 → 3 → 4 can be built and tested with a simple CLI harness before any
network server exists. Component 5 is pure logic with no I/O. Components 6 and 7 are
the final pieces and can be validated by pointing `tuimail` at localhost.
## References
- [ProtonMail/proton-bridge](https://github.com/ProtonMail/proton-bridge) — official Bridge (Go, open source)
- [ProtonMail/go-proton-api](https://github.com/ProtonMail/go-proton-api) — official Go API client
- [ProtonMail/proton-crypto-rs](https://github.com/ProtonMail/proton-crypto-rs) — official Rust crypto crates
- [ProtonMail/proton-srp](https://github.com/ProtonMail/proton-srp) — official Rust SRP implementation
- [rpgp](https://github.com/rpgp/rpgp) — pure Rust OpenPGP implementation
More work:
- local storge in the proton-bridge. Decide whether to store the message body pgp-encrypted, as it came from the proton server, or
use decrypt it (and serve to the client using imap) and create a AES-256-GCM encrypted cache
- email replies in the tui client have not yet been implemented