Document Proton auth and FIDO2 rewrite milestones
ober
6ceedeec86aa13a83af4a192b00213be0f394346
new file mode 100644 --- /dev/null +++ b/plan.md @@ -0,0 +1,325 @@ +# Jerboa Proton Bridge Rewrite Plan + +## Objective + +Rewrite the useful Proton Mail Bridge client behavior in Jerboa while changing +the local security boundary: + +- Authenticate directly to Proton. +- Use the user's registered YubiKey through Proton's FIDO2/WebAuthn challenge. +- Fetch and decrypt mail locally. +- Avoid a local password-authenticated IMAP service in the default mode. +- Keep reads explicit and YubiKey-gated. + +This is not a drop-in clone of upstream Bridge's default behavior. It is a +security-oriented native Proton Mail client. + +## Upstream Reference + +Local clone: + +```text +/Users/user/mine/proton-bridge-upstream +``` + +Important upstream files: + +- `internal/bridge/user.go` + - `LoginAuth` + - `LoginFull` + - `LoginUser` + - `loginUser` + - refresh/load user paths +- `internal/fido/fido.go` + - hardware-key discovery + - assertion creation + - CLI FIDO flow +- `internal/fido/utils.go` + - WebAuthn client data JSON construction + - FIDO2 request payload submitted to Proton +- `internal/usertypes/keys.go` + - user/address key unlock behavior +- `internal/vault/` + - upstream persisted auth/session/key material +- `internal/services/imapservice/` + - upstream IMAP-facing service +- `pkg/message/` + - message decrypt/build logic + +## Key Upstream Dependencies To Replace Or Wrap + +Upstream Bridge uses: + +- `github.com/ProtonMail/go-proton-api` +- `github.com/ProtonMail/go-srp` +- `github.com/ProtonMail/gopenpgp/v2` +- `github.com/ProtonMail/gluon` +- `github.com/ProtonMail/go-libfido2` + +Jerboa replacement map: + +```text +go-proton-api -> new Jerboa Proton API module +go-srp -> new Jerboa/Rust SRP helper if needed +gopenpgp -> jerboa-pgp, extended as required +gluon -> not used for default mode; no local IMAP server +go-libfido2 -> jerboa-yubikey FIDO2/WebAuthn support, to add +pkg/message/pkg/mime -> jerboa-mail plus Proton-specific decrypt glue +``` + +## Security Difference From Bridge + +Upstream Bridge: + +1. Logs into Proton. +2. Handles 2FA/FIDO2. +3. Stores auth/session/key material in a vault/keychain. +4. Exposes a local IMAP/SMTP service with generated credentials. + +This rewrite: + +1. Logs into Proton. +2. Handles FIDO2 through the registered YubiKey. +3. Avoids storing a reusable local IMAP password. +4. Does not start a password-authenticated IMAP server by default. +5. Requires a YubiKey gate for interactive read sessions. + +If a local protocol is added later, it should use a single-user Unix socket, +short-lived capabilities, and YubiKey-gated session creation. + +## Native Login Flow To Port + +Based on `internal/bridge/user.go` and `internal/fido/`: + +1. Start login: + - username + - account password + - Proton SRP/auth request + - obtain `auth` object +2. Inspect `auth.TwoFA`. +3. If FIDO2 is required: + - extract `auth.TwoFA.FIDO2.AuthenticationOptions` + - read `publicKey.rpId` + - read `publicKey.challenge` + - read `publicKey.allowCredentials` + - construct `clientDataJSON`: + - `type`: `webauthn.get` + - `challenge`: base64url no-padding challenge + - `origin`: `https://` + rpID + - SHA-256 hash `clientDataJSON` + - ask YubiKey for assertion using: + - rpID + - clientDataHash + - allowed credential IDs + - optional PIN + - user-presence required + - decode authenticator data from assertion CBOR if needed + - submit Proton `Auth2FA` FIDO2 payload: + - original authentication options + - base64 client data + - base64 authenticator data + - base64 signature + - credential ID as integer list +4. If TOTP is required instead: + - use `jerboa-yubikey` OATH if the Proton TOTP secret is stored there + - otherwise prompt manually +5. Determine mailbox key password: + - two-password mode: prompt separately + - one-password mode: account password +6. Fetch user object and salts. +7. Salt mailbox key password for the primary key. +8. Unlock user keys. +9. Unlock address keys. +10. Fetch folders/messages. +11. Decrypt selected messages. +12. Render through `jerboa-mail`. + +## Required New Work In `jerboa-yubikey` + +Current `jerboa-yubikey` has OATH and challenge-response APIs, and FIDO is a +planned milestone. For this project we need a focused FIDO2 assertion API: + +```scheme +(yubikey-fido2-assert + rp-id + client-data-hash + credential-ids + pin + user-presence?) +;; => assertion record: +;; credential-id +;; authenticator-data +;; signature +``` + +Implementation options: + +1. Extend the existing native YubiKey backend to support CTAPHID + CBOR. +2. Add a small Rust FFI wrapper around libfido2/go-libfido2-equivalent behavior. +3. Use system `ykman` or another CLI only as a temporary diagnostic tool, not + as the final path. + +Preferred: a small native/Rust FFI helper with a Jerboa API matching +`internal/fido`'s needs. + +## Required New Work In Proton API Layer + +Create modules: + +```text +proton-bridge/api/http.ss +proton-bridge/api/auth.ss +proton-bridge/api/types.ss +proton-bridge/api/json.ss +``` + +Initial API requirements: + +- SRP login start/finish. +- FIDO2 `Auth2FA`. +- TOTP `Auth2FA`. +- refresh-token flow. +- get user. +- get salts. +- get addresses. +- list labels/folders. +- list message metadata. +- fetch message body. + +Open question: whether to port from `go-proton-api` directly or first wrap a +small native helper that exposes only the required calls. + +## Required New Work In Crypto Layer + +Existing `jerboa-pgp` supports whole OpenPGP decrypt for common interop +messages. Proton message/key handling may require deeper operations: + +- unlock armored private keys with salted mailbox key password +- unlock user keyring +- unlock address keyrings +- decrypt message keys/session keys +- decrypt body/attachments +- verify signatures if available + +The first step is fixture-driven: + +1. Export or synthesize encrypted Proton-like payload fixtures. +2. Identify whether `jerboa-pgp` can decrypt them as-is. +3. Add lower-level APIs only where required. + +## Milestones + +### M0: Scaffold + +Done: + +- repo +- CLI +- security policy +- smoke tests +- upstream reference notes + +### M1: YubiKey FIDO2 Assertion Prototype + +Deliverables: + +- `jerboa-yubikey` API for FIDO2 assertion. +- Fixture test using static rpID/challenge/credential ID shape. +- Manual test against a real YubiKey, without Proton. + +Exit criteria: + +- Can produce credential ID, authenticator data, and signature for a WebAuthn + assertion request. + +### M2: Proton Auth Probe + +Deliverables: + +- Proton login start. +- Detect FIDO2 requirement. +- Call `jerboa-yubikey` FIDO2 assertion. +- Submit FIDO2 `Auth2FA`. +- Print authenticated user ID only. + +Exit criteria: + +- Login succeeds with the user's existing Proton YubiKey registration. +- No mail is fetched yet. +- No session is stored by default. + +### M3: Key Unlock Probe + +Deliverables: + +- Fetch user object. +- Fetch salts. +- Salt mailbox password. +- Unlock user keys. +- Unlock address keys. + +Exit criteria: + +- Key unlock succeeds. +- Key material is cleared from memory where APIs allow. +- No plaintext cache. + +### M4: Read-Only Mail Metadata + +Deliverables: + +- List labels/folders. +- List message metadata. +- Render summary table. + +Exit criteria: + +- Can list inbox metadata after FIDO-gated login. + +### M5: Message Decrypt + +Deliverables: + +- Fetch one message. +- Decrypt selected body. +- Render with `jerboa-mail`. + +Exit criteria: + +- `show --folder INBOX --id ...` prints a readable message. + +### M6: Session Policy + +Deliverables: + +- Explicit session lifetime. +- Optional encrypted local cache. +- YubiKey-gated cache unlock. +- No long-lived local IMAP password. + +Exit criteria: + +- User can choose between no-store and encrypted-store modes. +- Any stored material is useless without the YubiKey gate. + +### M7: Local Protocol Decision + +Decide whether this remains a CLI/TUI client or exposes a local protocol. + +Allowed options: + +- CLI only. +- TUI. +- Single-user Unix socket with short-lived capabilities. + +Rejected default: + +- local IMAP server with reusable password. + +## Immediate Next Step + +Start M1 in `jerboa-yubikey`: + +1. Inspect current native backend. +2. Add a minimal FIDO2 assertion interface. +3. Test with a local synthetic WebAuthn request.