Document Proton auth and FIDO2 rewrite milestones

ober

6ceedeec86aa13a83af4a192b00213be0f394346

diff --git a/plan.md b/plan.md
new file mode 100644
index 0000000..e516cea
--- /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.