Adds support for detached S/MIME (multipart/signed) verification plus several related UX and correctness fixes. - #8
Open
johannstieger wants to merge 2 commits into
Open
Conversation
…lus several related UX and correctness fixes.
**Detached signature support**
- New `mime-signed.js`: byte-exact splitter for `multipart/signed` bodies (RFC 5751 §3.1), producing the canonicalized signed content and the detached CMS signature.
- `smime-detect.js`: `multipart/signed` + `application/pkcs7-signature` is now marked `supported: true` and exposes its MIME boundary and sub-parts.
- `smime-verify.js`: new `smimeVerifyDetached()` verifies against externally-supplied content (`signedData.verify({ data, ... })`) since detached CMS carries no `eContent`. Also extracts any chain certificates (typically intermediate CAs) bundled alongside the signer's leaf cert.
- `index.js`: new branch in `onRenderEmailBody` wires the above together; fetches the raw (pre-decode) body blob so the CMS-signed bytes aren't mangled by MIME normalization.
**Signer certificate UI**
- Banner now shows expandable certificate details (subject, issuer, email, validity, SHA-256 fingerprint) for both opaque and detached signatures.
- "Copy to clipboard" (PEM) and, when the mail carried extra certs, "Copy with chain" (leaf + intermediates concatenated).
- Fixed a bug where `host.storage.set()` silently drops `ArrayBuffer` values (JSON round-trip) — certificates are now stored as base64 before persisting, with defensive checks against stale/corrupted data.
- Removed a certificate-download button that can never work: the plugin iframe's sandbox lacks `allow-downloads`, which is a host-level restriction, not fixable from plugin code.
**i18n**
- Added translations across 14 locales (en, de, es, fr, it, pt, nl, ru, zh, ja, ko, ar, tr, pl) for the banner, composer toolbar, settings page, and the sign/send unlock dialog.
- `host.i18n.t()` is called first but currently always resolves to English inside this privileged plugin iframe regardless of account language (confirmed via diagnostic logging). Added a self-contained locale-detection + dictionary fallback (`LOCAL_I18N` / `detectLocale()`, clearly marked in `index.js`) that takes priority until the host bug is fixed upstream — safe to delete once it is.
**Theming**
- Fixed hardcoded `color: '#fff'` on the primary/active buttons (Sign/Encrypt toolbar pills, Import key, Unlock), which was unreadable against light themes. Now uses `var(--color-primary-foreground, #fff)`.
## Not in scope
- Outgoing detached signing (plugin still signs opaque only — matches prior behavior and is broadly compatible).
- Encrypted + detached-signed combinations (rare in practice).
## Testing
Manually verified against a live SEPPmail-signed detached message (SPF/DKIM/DMARC pass) in a self-hosted Bulwark instance: signature verifies, signer certificate auto-imports, banner/details/copy actions work, and the plugin dev-loads correctly via `PLUGIN_DEV_DIR`.
…s several related UX and correctness fixes.
**Detached signature support**
- New `mime-signed.js`: byte-exact splitter for `multipart/signed` bodies (RFC 5751 §3.1), producing the canonicalized signed content and the detached CMS signature.
- `smime-detect.js`: `multipart/signed` + `application/pkcs7-signature` is now marked `supported: true` and exposes its MIME boundary and sub-parts.
- `smime-verify.js`: new `smimeVerifyDetached()` verifies against externally-supplied content (`signedData.verify({ data, ... })`) since detached CMS carries no `eContent`. Also extracts any chain certificates (typically intermediate CAs) bundled alongside the signer's leaf cert.
- `index.js`: new branch in `onRenderEmailBody` wires the above together; fetches the raw (pre-decode) body blob so the CMS-signed bytes aren't mangled by MIME normalization.
**Signer certificate UI**
- Banner now shows expandable certificate details (subject, issuer, email, validity, SHA-256 fingerprint) for both opaque and detached signatures.
- "Copy to clipboard" (PEM) and, when the mail carried extra certs, "Copy with chain" (leaf + intermediates concatenated).
- Fixed a bug where `host.storage.set()` silently drops `ArrayBuffer` values (JSON round-trip) — certificates are now stored as base64 before persisting, with defensive checks against stale/corrupted data.
- Removed a certificate-download button that can never work: the plugin iframe's sandbox lacks `allow-downloads`, which is a host-level restriction, not fixable from plugin code.
**i18n**
- Added translations across 14 locales (en, de, es, fr, it, pt, nl, ru, zh, ja, ko, ar, tr, pl) for the banner, composer toolbar, settings page, and the sign/send unlock dialog.
- `host.i18n.t()` is called first but currently always resolves to English inside this privileged plugin iframe regardless of account language (confirmed via diagnostic logging). Added a self-contained locale-detection + dictionary fallback (`LOCAL_I18N` / `detectLocale()`, clearly marked in `index.js`) that takes priority until the host bug is fixed upstream — safe to delete once it is.
**Theming**
- Fixed hardcoded `color: '#fff'` on the primary/active buttons (Sign/Encrypt toolbar pills, Import key, Unlock), which was unreadable against light themes. Now uses `var(--color-primary-foreground, #fff)`.
## Not in scope
- Outgoing detached signing (plugin still signs opaque only — matches prior behavior and is broadly compatible).
- Encrypted + detached-signed combinations (rare in practice).
## Testing
Manually verified against a live SEPPmail-signed detached message (SPF/DKIM/DMARC pass) in a self-hosted Bulwark instance: signature verifies, signer certificate auto-imports, banner/details/copy actions work, and the plugin dev-loads correctly via `PLUGIN_DEV_DIR`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for detached S/MIME (
multipart/signed) verification, which was previously rejected withUnsupported S/MIME type (detached-sig), plus several related UX and correctness fixes.Changes
Detached signature support
mime-signed.js: byte-exact splitter formultipart/signedbodies (RFC 5751 §3.1), producing the canonicalized signed content and the detached CMS signature.smime-detect.js:multipart/signed+application/pkcs7-signatureis now markedsupported: trueand exposes its MIME boundary and sub-parts.smime-verify.js: newsmimeVerifyDetached()verifies against externally-supplied content (signedData.verify({ data, ... })) since detached CMS carries noeContent. Also extracts any chain certificates (typically intermediate CAs) bundled alongside the signer's leaf cert.index.js: new branch inonRenderEmailBodywires the above together; fetches the raw (pre-decode) body blob so the CMS-signed bytes aren't mangled by MIME normalization.Signer certificate UI
host.storage.set()silently dropsArrayBuffervalues (JSON round-trip) — certificates are now stored as base64 before persisting, with defensive checks against stale/corrupted data.allow-downloads, which is a host-level restriction, not fixable from plugin code.i18n
host.i18n.t()is called first but currently always resolves to English inside this privileged plugin iframe regardless of account language (confirmed via diagnostic logging). Added a self-contained locale-detection + dictionary fallback (LOCAL_I18N/detectLocale(), clearly marked inindex.js) that takes priority until the host bug is fixed upstream — safe to delete once it is.Theming
color: '#fff'on the primary/active buttons (Sign/Encrypt toolbar pills, Import key, Unlock), which was unreadable against light themes. Now usesvar(--color-primary-foreground, #fff).Not in scope
Testing
Manually verified against a live SEPPmail-signed detached message (SPF/DKIM/DMARC pass) in a self-hosted Bulwark instance: signature verifies, signer certificate auto-imports, banner/details/copy actions work, and the plugin dev-loads correctly via
PLUGIN_DEV_DIR.