Skip to content

Commit 0802e64

Browse files
docs(smime): API reference, architecture, threat model, user guide, provider guide
Adds end-to-end documentation for the S/MIME companion integration: Library docs (`plugins/smime-api/`) - README — client-side tutorial (bind, execute, result + PendingIntent flow) and action-by-action reference. Mirrors openpgp-api-lib/README. - CHANGELOG — Version 1 inventory of actions / extras / Parcelables. - LICENSE — Apache 2.0 (matches openpgp-api-lib). mdbook docs - architecture/adr/0009 — Companion App + AIDL Service for S/MIME: decision record covering the three alternatives (in-process library, embedded crypto core, companion app) and why the companion-app model was chosen. Includes a Mermaid sequence diagram for the sign+encrypt + passphrase-unlock flow. - security/smime-companion-threat-model — STRIDE pass on the IPC trust boundary: provider discovery, binding, request/result tampering, PendingIntent hijacking, pipe DoS, cert-lookup honesty. Risks ranked, residual-risk notes for the two trade-offs inherent to the model. - user-guide/setup/enabling-smime — end-user walkthrough (install provider, set keystore passphrase, import certificate, enable S/MIME on the account, first send/receive). Includes a compose lock-icon state reference and a translator's inventory of the new string resources. - developer/writing-smime-provider — normative spec for implementing an alternative S/MIME provider: manifest declarations, AIDL contract, per-action behaviour and edge cases, the user-interaction handshake, EXTRA_API_VERSION negotiation, EXTRA_FROM for per-identity signing, security obligations (caller identity, no outbound network, trust-signal honesty), a testing checklist. - SUMMARY.md — all four new documents wired into the mdbook TOC.
1 parent 04e4235 commit 0802e64

8 files changed

Lines changed: 1220 additions & 0 deletions

File tree

docs/SUMMARY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,21 @@ generator, in this case, **mdbook**. It defines the structure and navigation of
3939
- [0006 - White Label Architecture](architecture/adr/0006-white-label-architecture.md)
4040
- [0007 - Project Structure](architecture/adr/0007-project-structure.md)
4141
- [0008 - Change Shared Module package to `net.thunderbird`](architecture/adr/0008-change-shared-modules-package-name.md)
42+
- [0009 - Use a Companion App + AIDL Service for S/MIME](architecture/adr/0009-smime-companion-app-architecture.md)
4243
- [Proposed]()
4344
- [Rejected]()
4445
- [User Guide]()
4546
- [Setup]()
4647
- [Installing ADB](user-guide/setup/installing-adb.md)
48+
- [Enabling S/MIME via CipherMail](user-guide/setup/enabling-smime.md)
4749
- [Troubleshooting]()
4850
- [Collecting Debug Logs](user-guide/troubleshooting/collecting-debug-logs.md)
4951
- [Find your app version](user-guide/troubleshooting/find-your-app-version.md)
5052
- [Developer](developer/README.md)
5153
- [Database Migration Checklist](developer/db-migration-checklist.md)
5254
- [Foldable Device Support](developer/foldable-device-support.md)
5355
- [Preference Migration Guide](developer/preference-migration-guide.md)
56+
- [Writing an S/MIME Provider](developer/writing-smime-provider.md)
5457
- [Release]()
5558
- [Release Process](release/RELEASE.md)
5659
- [Release Automation](release/AUTOMATION.md)
@@ -59,6 +62,7 @@ generator, in this case, **mdbook**. It defines the structure and navigation of
5962
- [Manual Release (historical)](release/HISTORICAL_RELEASE.md)
6063
- [Security]()
6164
- [Threat Modeling Guide](security/threat-modeling-guide.md)
65+
- [S/MIME Companion Threat Model](security/smime-companion-threat-model.md)
6266

6367
---
6468

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Use a Companion App + AIDL Service for S/MIME
2+
3+
## Status
4+
5+
- **Accepted**
6+
7+
## Context
8+
9+
Thunderbird for Android needs to support end-to-end encryption with S/MIME, in addition to its existing OpenPGP support.
10+
S/MIME has the same broad shape as OpenPGP — sign, encrypt, decrypt, verify, certificate lookup — but uses a different
11+
trust model (X.509 certificates and CAs) and a substantially different implementation stack (Bouncy Castle's CMS layer,
12+
PKCS#12 import, OCSP, CRL distribution, key-store management with passphrase entry).
13+
14+
We evaluated three integration strategies:
15+
16+
- **Option A — in-process library.** Bundle a full S/MIME implementation directly into Thunderbird, alongside the
17+
existing `legacy/crypto-openpgp` module. Tight integration, no IPC, but a very large surface area to maintain:
18+
certificate stores, OCSP, CRL fetching, PKCS#12 import, keystore UI, passphrase entry, etc. Doubles Thunderbird's
19+
crypto-attack surface and significantly enlarges the app's dependency footprint.
20+
21+
- **Option B — embed a third-party crypto core.** Pull in an existing S/MIME library (e.g. the CipherMail core) as a
22+
JAR/AAR. Smaller than Option A but still leaves Thunderbird responsible for keystore lifecycle, certificate UI, and
23+
passphrase prompts. License compatibility (the reference S/MIME stack is GPL) is also a hard blocker for Thunderbird's
24+
app distribution.
25+
26+
- **Option C — companion app over AIDL.** Thunderbird depends only on a small AIDL API and binds, at runtime, to a
27+
separate S/MIME provider app (CipherMail) that owns all key material, certificate stores, and crypto operations.
28+
This mirrors how OpenKeychain already provides OpenPGP for Thunderbird via `plugins/openpgp-api-lib`.
29+
30+
Option C provides the cleanest symmetry with the existing OpenPGP integration, isolates the GPL'd crypto core in a
31+
separate process and a separate app distribution, and keeps Thunderbird's binary size and dependency graph essentially
32+
unchanged.
33+
34+
## Decision
35+
36+
We will integrate S/MIME via a companion-app architecture, paralleling the existing OpenPGP / OpenKeychain integration.
37+
38+
Specifically:
39+
40+
- A new module `plugins/smime-api/smime-api/` defines the AIDL service contract and its Parcelable result types.
41+
It mirrors `plugins/openpgp-api-lib/openpgp-api/` in layout and licensing (Apache 2.0).
42+
- The reference provider is **CipherMail** (`com.ciphermail.android`), an existing standalone Android app maintained
43+
in a separate repository, which adds an `SmimeService` bound service implementing `ISmimeService`.
44+
- Thunderbird discovers providers at runtime via an `<intent-filter>` query for `com.ciphermail.smime.api.ISmimeService`.
45+
When more than one provider is installed, `SmimeAppSelectDialog` lets the user choose.
46+
- The service contract exposes five actions: `CHECK_PERMISSION`, `DECRYPT_VERIFY`, `SIGN_AND_ENCRYPT`,
47+
`GET_CERTIFICATES`, `IMPORT_CERTIFICATE`. Bulk MIME data streams through `ParcelFileDescriptor` pipes rather than
48+
Intent extras, keeping Binder transactions small.
49+
- Cross-process user interaction (e.g. keystore-passphrase entry) follows the same `RESULT_CODE_USER_INTERACTION_REQUIRED`
50+
+ `PendingIntent` pattern OpenKeychain already uses. The provider's passphrase dialog runs in the provider's process;
51+
Thunderbird launches it via `startIntentSenderForResult` and retries the operation on `RESULT_OK`.
52+
- Per-account configuration mirrors OpenPGP: a new `smimeProvider` field on `LegacyAccount` selects which installed
53+
provider that account uses, surfaced as a Preference under Settings → S/MIME.
54+
55+
### Request flow — sign and encrypt (with passphrase unlock)
56+
57+
```mermaid
58+
sequenceDiagram
59+
autonumber
60+
participant TB as Thunderbird
61+
participant SVC as Provider service<br/>(ISmimeService)
62+
participant DIA as Provider passphrase<br/>dialog
63+
participant KEY as Provider keystore
64+
65+
TB->>SVC: createOutputPipe(pipeId)
66+
SVC-->>TB: ParcelFileDescriptor (write end)
67+
TB->>SVC: execute(SIGN_AND_ENCRYPT, inputPipe, pipeId)
68+
SVC->>KEY: isUnlocked()
69+
KEY-->>SVC: false
70+
SVC-->>TB: RESULT_CODE_USER_INTERACTION_REQUIRED<br/>+ immutable PendingIntent
71+
72+
TB->>DIA: startIntentSenderForResult(PendingIntent)
73+
Note over DIA: user enters passphrase
74+
DIA-)KEY: unlock (provider-internal)
75+
DIA-->>TB: RESULT_OK
76+
77+
TB->>SVC: execute(SIGN_AND_ENCRYPT, inputPipe, pipeId) [retry]
78+
SVC->>KEY: sign + encrypt
79+
SVC->>TB: stream wrapped MIME bytes via output pipe
80+
SVC-->>TB: RESULT_CODE_SUCCESS
81+
```
82+
83+
The `USER_INTERACTION_REQUIRED` reply returns within tens of milliseconds.
84+
The retry succeeds without further user input because the provider's
85+
keystore is now unlocked. Decrypt + verify follows the same shape, with
86+
`SmimeDecryptionResult` and `SmimeSignatureResult` returned as result
87+
Parcelables in step 11.
88+
89+
## Consequences
90+
91+
### Positive Consequences
92+
93+
- S/MIME implementation, key material, and certificate stores stay outside the Thunderbird process and APK.
94+
- License isolation: the GPL crypto core ships in a separate app with its own distribution channel.
95+
- Symmetry with OpenPGP makes the integration easy to reason about; existing patterns (`MessageCryptoHelper`,
96+
`RecipientPresenter`, `MessageCompose`) extend directly.
97+
- Multi-provider support is free: any app that declares the AIDL service appears in `SmimeAppSelectDialog`.
98+
- Thunderbird's binary size and dependency graph are essentially unchanged — only the small AIDL stub is added.
99+
100+
### Negative Consequences
101+
102+
- Users must install two apps to use S/MIME. CipherMail is published on Google Play (and is planned for F-Droid once
103+
this companion API lands upstream), so the install is a single search-and-install step, but it is still a separate
104+
user action that Thunderbird cannot perform automatically. The S/MIME preference row surfaces an explanatory message
105+
when no provider is installed.
106+
- Cross-process round-trips add latency on the first call after process start (provider cold-start, keystore unlock).
107+
Subsequent calls are fast.
108+
- The cross-process passphrase-unlock dance is more involved than an in-process prompt and was a source of early bugs
109+
(cached-passphrase singletons, broadcast receivers, AndroidKeyStore key loss after reinstall).
110+
- The contract between Thunderbird and the provider becomes a versioned external API; breaking changes require an
111+
`EXTRA_API_VERSION` bump and explicit incompatibility handling in `SmimeError.INCOMPATIBLE_API_VERSIONS`.
112+
- Two repositories must be kept in sync: `plugins/smime-api/smime-api/` here, and the mirrored module in the CipherMail
113+
repository at `smime-api/`.

0 commit comments

Comments
 (0)