initial commit
This commit is contained in:
commit
9b86d14d6e
3 changed files with 815 additions and 0 deletions
116
gpgmymail
Executable file
116
gpgmymail
Executable file
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# COPYRIGHT 2024 revsuine <pid1@revsuine.xyz>
|
||||
# Copyright 2019 Julian Andres Klode <jak@jak-linux.org>
|
||||
#
|
||||
# Licensed under the GNU General Public License version 3, which is available
|
||||
# at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
"""
|
||||
SOURCE: Based on the following:
|
||||
https://github.com/julian-klode/ansible.jak-linux.org/blob/49cd62c6fa2109678c751ae5c2a7e696dd761e8e/roles/mailserver/files/usr/local/lib/dovecot-sieve-filters/gpgmymail
|
||||
https://blog.jak-linux.org/2019/06/13/encrypted-email-storage/
|
||||
|
||||
You may also want to reference the following resources:
|
||||
https://www.grepular.com/Automatically_Encrypting_all_Incoming_Email
|
||||
https://perot.me/encrypt-specific-incoming-emails-using-dovecot-and-sieve
|
||||
|
||||
---
|
||||
|
||||
Encrypt/Decrypt GPG/MIME messages.
|
||||
|
||||
This tool can encrypt and decrypt emails using PGP/MIME. Decryption only
|
||||
works well for emails created with this tool. When encrypting, the tool
|
||||
preserves all headers in the original email in the encrypted part, and
|
||||
copies relevant headers to the output. When decrypting, any headers are
|
||||
ignored, and only the encrypted headers are restored.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import email.encoders
|
||||
import email.message
|
||||
import email.mime.application
|
||||
import email.mime.multipart
|
||||
import email.mime.message
|
||||
import typing
|
||||
|
||||
# see: https://gnupg.readthedocs.io/en/latest/
|
||||
import gnupg
|
||||
|
||||
def is_message_encrypted(message: email.message.Message) -> bool:
|
||||
"""Determines whether or not an email message is encrypted.
|
||||
|
||||
Currently just does it by checking the content type header:
|
||||
https://stackoverflow.com/questions/18819126/checking-encryption-status-of-email"""
|
||||
|
||||
return message.get_content_type() == "multipart/encrypted"
|
||||
|
||||
def encrypt(message: email.message.Message, recipients: typing.List[str]) -> str:
|
||||
"""Encrypt given message"""
|
||||
|
||||
# some mail clients like Thunderbird don't like twice-encrypted emails,
|
||||
# so we return the message as-is if it's already encrypted
|
||||
if is_message_encrypted(message):
|
||||
return message.as_string()
|
||||
|
||||
encrypted_content = gnupg.GPG().encrypt(message.as_string(), recipients, armor=True)
|
||||
if not encrypted_content:
|
||||
raise ValueError(encrypted_content.status)
|
||||
|
||||
# Build the parts
|
||||
enc = email.mime.application.MIMEApplication(
|
||||
_data=str(encrypted_content).encode(),
|
||||
_subtype="octet-stream",
|
||||
_encoder=email.encoders.encode_7or8bit
|
||||
)
|
||||
|
||||
control = email.mime.application.MIMEApplication(
|
||||
_data=b'Version: 1\n',
|
||||
_subtype='pgp-encrypted; name="msg.asc"',
|
||||
_encoder=email.encoders.encode_7or8bit
|
||||
)
|
||||
control['Content-Disposition'] = 'inline; filename="msg.asc"'
|
||||
|
||||
# Put the parts together
|
||||
encmsg = email.mime.multipart.MIMEMultipart(
|
||||
'encrypted',
|
||||
protocol='application/pgp-encrypted'
|
||||
)
|
||||
encmsg.attach(control)
|
||||
encmsg.attach(enc)
|
||||
|
||||
# Copy headers
|
||||
headers_not_to_override = {key.lower() for key in encmsg.keys()}
|
||||
|
||||
for key, value in message.items():
|
||||
if key.lower() not in headers_not_to_override:
|
||||
encmsg[key] = value
|
||||
|
||||
return encmsg.as_string()
|
||||
|
||||
def decrypt(message: email.message.Message) -> str:
|
||||
"""Decrypt the given message
|
||||
Likely won't work on this server as I don't store private keys"""
|
||||
return str(gnupg.GPG().decrypt(message.as_string()))
|
||||
|
||||
def main() -> None:
|
||||
"""Program entry"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Encrypt/decrypt mail using GPG/MIME"
|
||||
)
|
||||
parser.add_argument('-d', '--decrypt', action="store_true",
|
||||
help="Decrypt rather than encrypt")
|
||||
parser.add_argument('recipient', nargs='*',
|
||||
help="Key ID or email of keys to encrypt for")
|
||||
args = parser.parse_args()
|
||||
msg = email.message_from_file(sys.stdin)
|
||||
|
||||
if args.decrypt:
|
||||
sys.stdout.write(decrypt(msg))
|
||||
else:
|
||||
sys.stdout.write(encrypt(msg, args.recipient))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue