Skip to content

feat(email): support inline PGP decryption for Outlook emails#19

Merged
qasim-nylas merged 2 commits intonylas:mainfrom
mqasimca:feat/inline-pgp-decryption
Feb 4, 2026
Merged

feat(email): support inline PGP decryption for Outlook emails#19
qasim-nylas merged 2 commits intonylas:mainfrom
mqasimca:feat/inline-pgp-decryption

Conversation

@mqasimca
Copy link
Contributor

@mqasimca mqasimca commented Feb 4, 2026

Summary

Some email providers (notably Microsoft/Outlook) transform PGP/MIME encrypted messages when storing them. Instead of preserving the standard multipart/encrypted; protocol="application/pgp-encrypted" structure, they convert it to multipart/mixed with the PGP block as inline text.

This PR adds:

  • Inline PGP detection: Fallback detection for -----BEGIN PGP MESSAGE----- blocks when the message is not in standard PGP/MIME format
  • Better error propagation: GPG signing/encryption functions now return descriptive errors instead of generic "failed to build secure message"
  • Comprehensive tests: Unit tests for the new extractInlinePGP function covering various scenarios

Problem

When using nylas email read <id> --decrypt on emails received via Microsoft/Outlook, decryption fails with:

Error: GPG decryption failed: message is not PGP/MIME encrypted (Content-Type: multipart/mixed; ...)

This happens because Outlook transforms the original PGP/MIME structure:

Original (Gmail preserves this):

Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"

After Outlook transformation:

Content-Type: multipart/mixed; boundary="..."

--boundary
Content-Type: text/plain

-----BEGIN PGP MESSAGE-----
...encrypted content...
-----END PGP MESSAGE-----

Solution

The decryptGPGEmail function now:

  1. First checks for standard PGP/MIME format (existing behavior)
  2. If not PGP/MIME, searches for inline PGP blocks in the message body or MIME parts
  3. Extracts and decrypts the inline PGP content

Changes

File Changes
internal/cli/email/read_decrypt.go Added extractInlinePGP() function, updated decryptGPGEmail() to use fallback
internal/cli/email/read_decrypt_test.go Added 10 test cases for extractInlinePGP()
internal/cli/email/send_gpg.go Changed build*Message() functions to return ([]byte, error) for proper error propagation

Test plan

  • Unit tests pass (go test ./internal/cli/email/...)
  • Linting passes (golangci-lint run)
  • CI passes (make ci)
  • Manual test: Decrypt PGP email from Gmail (PGP/MIME) ✓
  • Manual test: Decrypt PGP email from Outlook (inline PGP) ✓

Compatibility

  • Backward compatible: Standard PGP/MIME emails continue to work unchanged
  • No breaking changes to CLI flags or API

mqasimca and others added 2 commits February 4, 2026 10:02
Some email providers (e.g., Microsoft/Outlook) transform PGP/MIME
encrypted messages into multipart/mixed with inline PGP blocks.
This adds fallback detection for inline PGP content when the message
is not in standard PGP/MIME format.

Also improves error propagation in GPG signing/encryption functions
to provide meaningful error messages instead of generic failures.
Outlook/Microsoft transforms PGP/MIME emails into multipart/mixed with
base64-encoded attachments. The extractInlinePGP function now checks
Content-Transfer-Encoding and decodes base64 content before searching
for PGP markers.

- Add base64 decoding for MIME parts with Content-Transfer-Encoding: base64
- Add test for Outlook-style base64-encoded PGP attachments
@qasim-nylas qasim-nylas merged commit 2d6726d into nylas:main Feb 4, 2026
2 checks passed
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.

2 participants