Skip to content

fix(pgp): decrypt bare armored messages - #1716

Open
chiliec wants to merge 1 commit into
floatpane:masterfrom
chiliec:fix/pgp-bare-armor
Open

fix(pgp): decrypt bare armored messages#1716
chiliec wants to merge 1 commit into
floatpane:masterfrom
chiliec:fix/pgp-bare-armor

Conversation

@chiliec

@chiliec chiliec commented Sep 11, 2026

Copy link
Copy Markdown

What?

decryptPGPMessage now handles a bare ASCII-armored payload: when the data starts with -----BEGIN PGP MESSAGE----- it decodes the armor and decrypts with openpgp.ReadMessage directly. Complete PGP/MIME entities still go through pgpmail.Read as before.

Adds fetcher/pgp_test.go covering both paths (bare armor + multipart/encrypted entity) using a throwaway key generated in the test.

Why?

Fixes #1715. checkPart extracts the application/octet-stream part of a multipart/encrypted message and passes just that armored block to decryptPGPMessage, which handed it to pgpmail.Read. pgpmail.Read expects a full MIME entity and runs textproto.ReadHeader first, so the armor header line has no colon and decryption aborted with malformed MIME header line: -----BEGIN PGP MESSAGE----- before the private key was ever used. Server-side encrypted mail (mailbox.org Encrypted Inbox) hits this on every message.

Verification (Go 1.26.5, GOTOOLCHAIN=local):

  • go test ./fetcher/ -run TestDecryptPGP -v — both tests pass.
  • RED→GREEN: with the fix stashed, TestDecryptPGPMessageBareArmor fails with the exact reported error (failed to decrypt PGP message: message: malformed MIME header line: -----BEGIN PGP MESSAGE-----); TestDecryptPGPMessageMIMEEntity passes before and after, so the existing PGP/MIME path is unchanged.
  • go test ./fetcher/ — full package green; go vet ./fetcher/, gofmt -l, golangci-lint run ./fetcher/ (v2.13.2, repo config) — 0 issues.

Not run locally: ./sender tests (need pkg-config/pcsclite for the YubiKey dep on this box) — untouched by this change.

Server-side encrypted mail (e.g. mailbox.org Encrypted Inbox) delivers the
application/octet-stream part as a bare ASCII-armored PGP message. That
payload was passed straight to pgpmail.Read, which expects a full MIME
entity and failed with 'malformed MIME header line'. Decode the armor and
decrypt directly when the payload starts with a PGP MESSAGE block; keep
the pgpmail path for complete PGP/MIME entities.

Fixes floatpane#1715

Signed-off-by: Vladimir Babin <vovababin@gmail.com>
@chiliec
chiliec requested a review from a team as a code owner September 11, 2026 22:21
@floatpanebot floatpanebot added bug Something isn't working area/fetcher IMAP fetch / IDLE / search size/M Diff: 51–200 lines labels Sep 11, 2026
@floatpanebot

Copy link
Copy Markdown
Member

Benchmark report — no significant change

Metrics worse: 0 · better: 0 (threshold: ±3%).

benchstat output
goos: linux
goarch: amd64
pkg: github.com/floatpane/matcha/backend
cpu: INTEL(R) XEON(R) PLATINUM 8573C
                           │    old.txt    │               new.txt                │
                           │    sec/op     │    sec/op      vs base               │
ParseSearchQuery_Simple-4    2.258µ ± 270%   2.221µ ± 379%        ~ (p=1.000 n=6)
ParseSearchQuery_Complex-4   5.980µ ±  52%   6.946µ ±  38%        ~ (p=0.699 n=6)
TokenizeSearchQuery-4        6.805µ ±  57%   3.756µ ±  26%  -44.81% (p=0.041 n=6)
geomean                      4.512µ          3.870µ         -14.24%

                           │  old.txt   │               new.txt               │
                           │    B/op    │    B/op      vs base                │
ParseSearchQuery_Simple-4    26.00 ± 0%   26.00 ± 23%       ~ (p=1.000 n=6)
ParseSearchQuery_Complex-4   762.0 ± 0%   762.0 ±  0%       ~ (p=1.000 n=6) ¹
TokenizeSearchQuery-4        176.0 ± 0%   176.0 ±  0%       ~ (p=1.000 n=6) ¹
geomean                      151.6        151.6        +0.00%
¹ all samples are equal

                           │  old.txt   │              new.txt               │
                           │ allocs/op  │ allocs/op   vs base                │
ParseSearchQuery_Simple-4    2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseSearchQuery_Complex-4   23.00 ± 0%   23.00 ± 0%       ~ (p=1.000 n=6) ¹
TokenizeSearchQuery-4        9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                      7.453        7.453       +0.00%
¹ all samples are equal

pkg: github.com/floatpane/matcha/tui
                    │   old.txt    │              new.txt               │
                    │    sec/op    │    sec/op     vs base              │
LogPanelView-4        133.8µ ±  7%   145.5µ ± 13%       ~ (p=0.180 n=6)
SearchOverlayView-4   140.7µ ± 11%   145.2µ ± 10%       ~ (p=1.000 n=6)
InboxConstruction-4   779.2µ ± 10%   839.7µ ± 15%       ~ (p=0.180 n=6)
geomean               244.8µ         260.8µ        +6.54%

                    │    old.txt    │               new.txt               │
                    │     B/op      │     B/op       vs base              │
LogPanelView-4        44.67Ki ± 51%   44.67Ki ± 51%       ~ (p=1.000 n=6)
SearchOverlayView-4   56.14Ki ± 41%   56.14Ki ± 41%       ~ (p=0.939 n=6)
InboxConstruction-4   874.3Ki ±  0%   874.3Ki ±  0%       ~ (p=0.818 n=6)
geomean               129.9Ki         129.9Ki        -0.00%

                    │   old.txt   │              new.txt              │
                    │  allocs/op  │  allocs/op   vs base              │
LogPanelView-4         714.0 ± 0%    714.0 ± 0%       ~ (p=1.000 n=6)
SearchOverlayView-4    926.0 ± 0%    926.0 ± 0%       ~ (p=1.000 n=6)
InboxConstruction-4   3.478k ± 0%   3.478k ± 0%       ~ (p=1.000 n=6)
geomean               1.320k        1.320k       +0.00%

auto-generated by benchmarks.yml

@chiliec

chiliec commented Sep 11, 2026

Copy link
Copy Markdown
Author

Note on the red govulncheck check: it reports stdlib advisories in go1.26.5 (GO-2026-6218 net/url, GO-2026-6090 crypto/tls, GO-2026-5972 encoding/asn1, fixed in 1.26.6) via call paths in spellcheck, sender, main.go and the existing IMAP dial code — none in this diff. The same job has been failing on master for the last three scheduled runs (Sep 7, Aug 31, Aug 24), so it's a toolchain bump, not this PR. Everything else (build/test on all 3 OSes, lint, gosec, e2e, fuzz) is green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/fetcher IMAP fetch / IDLE / search bug Something isn't working size/M Diff: 51–200 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PGP decryption always fails on server-side encrypted mail (raw armor passed to pgpmail.Read)

2 participants