Skip to content

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
bulwarkmail:mainfrom
johannstieger:main
Open

Adds support for detached S/MIME (multipart/signed) verification plus several related UX and correctness fixes.#8
johannstieger wants to merge 2 commits into
bulwarkmail:mainfrom
johannstieger:main

Conversation

@johannstieger

Copy link
Copy Markdown

Summary

Adds support for detached S/MIME (multipart/signed) verification, which was previously rejected with Unsupported S/MIME type (detached-sig), plus several related UX and correctness fixes.

Changes

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.

…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`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant