Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Feature request: OpenPGP encryption support #1

Open
2 tasks
spikecodes opened this issue May 31, 2022 · 18 comments
Open
2 tasks

✨ Feature request: OpenPGP encryption support #1

spikecodes opened this issue May 31, 2022 · 18 comments

Comments

@spikecodes
Copy link

Introduction

Hi! I love this project idea and really appreciate the dedication to modern security standards (such as DKIM support) as well as mail-send's RFC compliance. The current feature set would serve as a fantastic base for a security-oriented Rust-written mail client if that is something Stalwart Labs would be interested in pursuing.

The final component of mail-send that would make it a serious security contender (in terms of client features) to the likes of ProtonMail and Tutanota would be support for OpenPGP, the open standard for email encryption.

Proposal

Standards

mail-send could implement OpenPGP support by following these Internet Message Format standards:

  • RFC 4880 OpenPGP Message Format
  • RFC 3156 MIME* Security with OpenPGP

There are two strong libraries that have implemented RFC 4880: rpgp and sequoia.

*Comparison of OpenPGP vs. PGP/MIME and a table of major email client support for them

Implementation

The loading of a public PGP key could be done in a similar syntax to the implementation of DKIM in this crate:

// Set up DKIM signer
let pgp_key = PGP::from_pkcs1_asc_file("./key.asc")
    .unwrap()

^ I'm not very familiar with PGP so not sure if any other inputs besides the file path should be provided

// Build a encrypted text message with a single attachment
let message = MessageBuilder::new()
    .from(("John Doe", "john@example.com"))
    .to("jane@example.com")
    .subject("Howdy!")
    .text_body("These pretzels are making me thirsty.")
    .binary_attachment("image/png", "pretzels.png", [1, 2, 3, 4].as_ref())
    .pgp_encrypt(pgp_key);

Interested to hear your thoughts! Let me know if this issue would be better suited at mail-builder.

@mdecimus
Copy link
Member

Thank you for the suggestion. At the moment I am writing a mail server in Rust so I won't be able to work on adding PGP right away. However, OpenPGP is a feature that I was definitely planning to add to the library (and also the server) so I'll keep this issue open. Hopefully I'll be able to work on this in the next few weeks.

@soywod
Copy link
Contributor

soywod commented Jun 3, 2023

I think I can help!

I have a project that allows users to compile MML-based email templates into MIME Messages using your awesome mail-builder. Users can encrypt and sign parts using PGP shell commands (mostly used with gpg), so I already implemented the glue to add encrypted and signed parts to the builder. Recently I implemented the opposite: interpret raw MIME Messages into MML-base template, and users can decrypt and verify parts using PGP shell commands as well. So I already implemented the glue to decrypt parts (and parse new subparts) and verify parts from parsed emails (using your mail-parser).

The next task I am going to work on is to bring native PGP support using rPGP (instead of shell commands). I also plan to implement the autocrypt standard to simplify encryption/decryption process, which would allow us to use the syntax @spikecodes was proposing:

// Build a encrypted text message with a single attachment
let message = MessageBuilder::new()
    .from(("John Doe", "john@example.com"))
    .to("jane@example.com")
    .subject("Howdy!")
    .text_body("These pretzels are making me thirsty.")
    .binary_attachment("image/png", "pretzels.png", [1, 2, 3, 4].as_ref())
    // we could also add encryption/signing with PGP shell commands:
    // .pgp_encrypt_cmd(cmd)
    .pgp_encrypt(pgp_key);

Would you be interested in a PR?

(In fact it could be one PR for mail-builder (encrypt/sign) and another one for mail-parser (decrypt/verify))

@mdecimus
Copy link
Member

mdecimus commented Jun 4, 2023

Thanks for the help @soywod but to minimize duplicating efforts please let's discuss this again at the end of the year as I have plans to implement encryption at rest on the JMAP/IMAP servers and probably the JMAP for S/MIME draft (although S/MIME does not seem to be used by lots of people and Let's Encrypt does not even plan to support it).

@soywod
Copy link
Contributor

soywod commented Jun 4, 2023 via email

@mdecimus
Copy link
Member

mdecimus commented Jun 5, 2023

Unfortunately this is a topic that I haven't researched much and I am a bit behind my nlnet deliverables to do that now. Probably it would be better to create a separate library for encrypting/decrypting emails rather than adding support to mail-builder/mail-parser. Would it make sense that you use rPGP for Himalaya and once I finish my nlnet part we see how we can integrate the code? None of my tasks involve encryption so there won't be any overlap. Adding encryption is something that I plan to do once I finish with all the nlnet deliverables.

@soywod
Copy link
Contributor

soywod commented Jun 5, 2023 via email

@soywod
Copy link
Contributor

soywod commented Mar 9, 2024

let's discuss this again at the end of the year

Is it still sth you are interested in? Since our last discussion, I developed PGP operations based on native rPGP and on GPG lib. I would be glad to integrate it to your libs.

@mdecimus
Copy link
Member

Hi @soywod

Yes, OpenPGP support would be nice to have. We just need to make sure that the license of those libraries is compatible with the MIT/Apache license of mail-send.
Also, regarding rpgp, I was using this library in Stalwart and had to switch to sequoia (AGPL license) because the emails signed with rpgp could not be opened in the latest versions of Thunderbird. I forgot what it was exactly but it was related to rpgp using a certificate of the chain that had a flag indicating that it could not be used to sign messages.

@soywod
Copy link
Contributor

soywod commented Mar 14, 2024 via email

@mdecimus
Copy link
Member

Let me try from my side, I will let you know.

I think there was an open issue in the rpgp repo about this and back then it seemed like it was not a priority to fix it.

Why this issue was opened on mail-send?

I agree that it should be in mail-builder.

@soywod
Copy link
Contributor

soywod commented Mar 14, 2024

Are you referencing this issue? rpgp/rpgp#184 (comment)

@mdecimus
Copy link
Member

Are you referencing this issue? rpgp/rpgp#184 (comment)

Yes, that's the one. It looks like it's not a priority for them but it makes the crate unusable. At least not with Thunderbird.

@soywod
Copy link
Contributor

soywod commented Mar 15, 2024

Here how I set up my tests:

I generated 2 pair keys: one using GPG and one using pgp-lib (the rPGP wrapper I implemented for Himalaya). I added secret keys to Thunderbird, and published public keys to https://keys.openpgp.org/. I was able to send from Himalaya an encrypted mime part in a message and to decrypt it from Thunderbird:

image

I encountered few issues, but it was not related to PGP directly: the encrypted part was encoded to base64 by your builder (I was able to force it to 7bit), and the ASCII-armored message was missing carriage returns. But overall it works! The difference with your previous implementation is that I manually select the key for encryption:

https://git.sr.ht/~soywod/pimalaya/tree/master/item/pgp/src/encrypt.rs#L99

If I remember well, I took this from Delta Chat.

I will quickly test signing, but should work as expected as well.

Would you like me to propose PRs based on pgp-lib for mail-builder and mail-parser, and discuss there?

@mdecimus
Copy link
Member

I generated 2 pair keys: one using GPG and one using pgp-lib (the rPGP wrapper I implemented for Himalaya). I added secret keys to Thunderbird, and published public keys to https://keys.openpgp.org/. I was able to send from Himalaya an encrypted mime part in a message and to decrypt it from Thunderbird:

Can you try generating the keys with the latest Thunderbird and then encrypting the message using those keys? I think the problem was that Thunderbird was including certificates marked as "not for encryption" and rPGP was using them anyway.
I remember that it was possible to read that flag but what was not possible is to tell rpgp which certs to use when signing.

Would you like me to propose PRs based on pgp-lib for mail-builder and mail-parser, and discuss there?

Sure, but first we need to make sure that rpgp works. I had to drop it because of the issue mentioned before.
Also, I took a quick look at pgp-lib and it is async, both mail-parser and mail-parser are sync.

@soywod
Copy link
Contributor

soywod commented Mar 15, 2024

I generated a new key pair using Thunderbird, downloaded the pub key, uploaded it to key.openpgp.org then sent an encrypted message using Himalaya:

image

Is there any scenario you have in mind to test before opening PRs?

Also, I took a quick look at pgp-lib and it is async, both mail-parser and mail-parser are sync.

I can separate sync and not sync APIs in different modules, or use sth like https://docs.rs/maybe-async/latest/maybe_async/.

@soywod
Copy link
Contributor

soywod commented Mar 15, 2024

I have issues with signing, not sure if it comes from rpgp or if it comes from the way I sign MIME parts.

@soywod
Copy link
Contributor

soywod commented Mar 15, 2024

I have issues with signing, not sure if it comes from rpgp or if it comes from the way I sign MIME parts.

Ok I made it work, it was an issue with micalg, looks like Thunderbird does not appreciate SHA1, I switched to SHA256 and signing works.

image

EDIT: I also needed to select the right secret key for signing, see cd2b2.

@mdecimus
Copy link
Member

I am going to retest this then.

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

No branches or pull requests

3 participants