Merge branch 'pEp-standards'

This commit is contained in:
revsuine 2024-11-15 17:59:49 +00:00
commit 52405bf067
Signed by: revsuine
GPG key ID: 3F257B68F5BC9339
2 changed files with 47 additions and 2 deletions

View file

@ -1,7 +1,14 @@
# gpgmymail # gpgmymail
Takes an email from stdin and encrypts it using the recipient's PGP key, Takes an email from stdin and encrypts it to stdout using the recipient's PGP
provided as an argument when calling the script. key, provided as an argument when calling the script.
Leaves a `X-gpgmymail-Status` header on the email, which has the following
statuses:
* `entered` - the email has entered the encryption function, but not been
encrypted
* `encrypted` - the encryption function has encrypted the email
Written to be a Sieve filter to be used with `sieve_extprograms`. Can be used Written to be a Sieve filter to be used with `sieve_extprograms`. Can be used
in a Sieve filter e.g.: in a Sieve filter e.g.:

View file

@ -24,6 +24,12 @@ works well for emails created with this tool. When encrypting, the tool
preserves all headers in the original email in the encrypted part, and preserves all headers in the original email in the encrypted part, and
copies relevant headers to the output. When decrypting, any headers are copies relevant headers to the output. When decrypting, any headers are
ignored, and only the encrypted headers are restored. ignored, and only the encrypted headers are restored.
Emails exiting this script will have the 'X-gpgmymail-Status' header, which has
the following options:
- entered: the email has entered the encrypt() function
- encrypted: the email has been encrypted
""" """
import argparse import argparse
@ -42,6 +48,7 @@ import gnupg
# constants # constants
DEFAULT_ENCODING='utf-8' # default is latin-1 which fails w some unicode chars DEFAULT_ENCODING='utf-8' # default is latin-1 which fails w some unicode chars
PEP_SUBJECT='=?utf-8?Q?p=E2=89=A1p?='
def is_message_encrypted(message: email.message.Message) -> bool: def is_message_encrypted(message: email.message.Message) -> bool:
"""Determines whether or not an email message is encrypted. """Determines whether or not an email message is encrypted.
@ -169,6 +176,27 @@ def decode_email(message: email.message.Message) -> email.message.Message:
return decoded_bytes_to_return_value(decoded_bytes) return decoded_bytes_to_return_value(decoded_bytes)
def set_email_header(
message: email.message.Message,
name: str,
value: str
) -> None:
"""
Set the header of an email Message. Will either replace the first instance
of the header, or if the header is not present, will add the header.
Note: Python passes objects as references, so there is no need for a return
value.
:param message: the Message object to be modified
:param name: the email header to set
:param value: the value to set the header to
"""
if message.get(name):
message.replace_header(name, value)
else:
message.add_header(name, value)
def encrypt( def encrypt(
message: email.message.Message, message: email.message.Message,
recipients: typing.List[str], recipients: typing.List[str],
@ -197,6 +225,9 @@ def encrypt(
blocks. find ignore_errors to see instances where this occurs blocks. find ignore_errors to see instances where this occurs
:return: The encrypted email as a string""" :return: The encrypted email as a string"""
# mark the email as having passed through us
set_email_header(message, 'X-gpgmymail-Status', 'entered')
# exclusion criteria: # exclusion criteria:
# some mail clients like Thunderbird don't like twice-encrypted emails, # some mail clients like Thunderbird don't like twice-encrypted emails,
@ -254,6 +285,13 @@ def encrypt(
if key.lower() not in headers_not_to_override: if key.lower() not in headers_not_to_override:
encmsg[key] = value encmsg[key] = value
# mark as confirming to pEp: https://blog.jak-linux.org/2019/06/13/encrypted-email-storage/#pretty-easy-privacy-pp
set_email_header(encmsg, 'Subject', PEP_SUBJECT)
set_email_header(encmsg, 'X-pEp-Version', '2.1')
# we have encrypted the email, set our gpgmymail header appropriately
set_email_header(encmsg, 'X-gpgmymail-Status', 'encrypted')
return encmsg.as_string() return encmsg.as_string()
def decrypt(message: email.message.Message, *, encoding: str = DEFAULT_ENCODING) -> str: def decrypt(message: email.message.Message, *, encoding: str = DEFAULT_ENCODING) -> str: