83 lines
3.9 KiB
Markdown
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
|