diff --git a/app/models.py b/app/models.py index 263a3822..bfa58b9f 100644 --- a/app/models.py +++ b/app/models.py @@ -1278,7 +1278,7 @@ class EmailLog(db.Model, ModelMixin): # for ex if alias is disabled, this forwarding is blocked blocked = db.Column(db.Boolean, nullable=False, default=False) - # can happen when user email service refuses the forwarded email + # can happen when user mailbox refuses the forwarded email # usually because the forwarded email is too spammy bounced = db.Column(db.Boolean, nullable=False, default=False, server_default="0") diff --git a/app/pgp_utils.py b/app/pgp_utils.py index 47f05273..11c3f050 100644 --- a/app/pgp_utils.py +++ b/app/pgp_utils.py @@ -3,11 +3,13 @@ from io import BytesIO import gnupg from memory_profiler import memory_usage +from pgpy import PGPMessage from app.config import GNUPGHOME from app.log import LOG from app.models import Mailbox, Contact from app.utils import random_string +import pgpy gpg = gnupg.GPG(gnupghome=GNUPGHOME) gpg.encoding = "utf-8" @@ -92,3 +94,12 @@ def encrypt_file(data: BytesIO, fingerprint: str) -> str: raise PGPException(f"Cannot encrypt, status: {r.status}") return str(r) + + +def encrypt_file_with_pgpy(data: bytes, public_key: str) -> PGPMessage: + key = pgpy.PGPKey() + key.parse(public_key) + msg = pgpy.PGPMessage.new(data, encoding="utf-8") + r = key.encrypt(msg) + + return r diff --git a/email_handler.py b/email_handler.py index 0b4f914c..a21661de 100644 --- a/email_handler.py +++ b/email_handler.py @@ -34,6 +34,7 @@ import argparse import asyncio import email import os +import random import time import uuid from email import encoders @@ -392,7 +393,7 @@ _MIME_HEADERS = [ _MIME_HEADERS = [h.lower() for h in _MIME_HEADERS] -def prepare_pgp_message(orig_msg: Message, pgp_fingerprint: str): +def prepare_pgp_message(orig_msg: Message, pgp_fingerprint: str, public_key: str): msg = MIMEMultipart("encrypted", protocol="application/pgp-encrypted") # copy all headers from original message except all standard MIME headers @@ -417,11 +418,23 @@ def prepare_pgp_message(orig_msg: Message, pgp_fingerprint: str): "octet-stream", _encoder=encoders.encode_7or8bit, name="encrypted.asc" ) second.add_header("Content-Disposition", 'inline; filename="encrypted.asc"') + # encrypt original message - encrypted_data = pgp_utils.encrypt_file( - BytesIO(orig_msg.as_bytes()), pgp_fingerprint - ) - second.set_payload(encrypted_data) + # ABTest between pgpy and python-gnupg + x = random.randint(0, 9) + if x >= 5: + LOG.d("encrypt using python-gnupg") + encrypted_data = pgp_utils.encrypt_file( + BytesIO(orig_msg.as_bytes()), pgp_fingerprint + ) + second.set_payload(encrypted_data) + else: + LOG.d("encrypt using pgpy") + encrypted_data = pgp_utils.encrypt_file_with_pgpy( + orig_msg.as_bytes(), public_key + ) + second.set_payload(str(encrypted_data)) + msg.attach(second) return msg @@ -632,7 +645,9 @@ def forward_email_to_mailbox( if mailbox.pgp_finger_print and user.is_premium() and not alias.disable_pgp: LOG.d("Encrypt message using mailbox %s", mailbox) try: - msg = prepare_pgp_message(msg, mailbox.pgp_finger_print) + msg = prepare_pgp_message( + msg, mailbox.pgp_finger_print, mailbox.pgp_public_key + ) except PGPException: LOG.exception( "Cannot encrypt message %s -> %s. %s %s", contact, alias, mailbox, user @@ -901,7 +916,9 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): if contact.pgp_finger_print and user.is_premium(): LOG.d("Encrypt message for contact %s", contact) try: - msg = prepare_pgp_message(msg, contact.pgp_finger_print) + msg = prepare_pgp_message( + msg, contact.pgp_finger_print, contact.pgp_public_key + ) except PGPException: LOG.exception( "Cannot encrypt message %s -> %s. %s %s", alias, contact, mailbox, user diff --git a/local_data/private-pgp.asc b/local_data/private-pgp.asc new file mode 100644 index 00000000..bf008b7e --- /dev/null +++ b/local_data/private-pgp.asc @@ -0,0 +1,57 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +0GzRi859Yrp2jhdAzkXArMVq6CS8G+8H5w== +=1B0F +-----END PGP PRIVATE KEY BLOCK----- diff --git a/local_data/public-pgp.asc b/local_data/public-pgp.asc new file mode 100644 index 00000000..803c57c5 --- /dev/null +++ b/local_data/public-pgp.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBF+YdvUBCAC6ztR1r2A5Sl8MuHrNavIHPI2/GKYe61GnR8h8HVocJ8Q7j6dd +XeeaMkfiZWwvP1ya3b4F+irQXNXtsWqV3F3QLBF7NYtqW96JJsKZIuvehI8UXPIL +4ibA/ptGd8Cd6713PhByZGUPAQsdFKjsVRgdIm6aJpvFZpGktn2T3Yx6sJZ0CXQS +WEbRv+ld9sE9Pe0CRwJ/R29ktFSZ8A7hHABXXoLtawgtsa6EIvbn+XbsQxs5WpyK +Zr7XNrPCflkSOHihjOnbFsmEsGIyMFZvwrsc5akqF9dPIJNWp/j+SXuRuMFj+7jd +LQHitppBuloPMtCja41xEAyan4mbF795FK4VABEBAAG0F1Rlc3QgPHRlc3RAZXhh +bXBsZS5vcmc+iQFOBBMBCAA4FiEE3tfq+zchw8t7RxNrI+oMxASbosIFAl+YdvUC +GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQI+oMxASbosKdcwgAjwnmkHPG +nS1tuTJrlZjqtH7Apvwv0HBUe4HEVJv1TivmTvDwQ0fsSc/57tdUqgd2Vcq00qSP +Gq1Un/7e7WMIlZP7vtj2FWkowxcAFI1Nf5CgKiADlay0p4KSmTpZoPTLgfraRPlj +O7xjjiXjEmYrk1GugUN8LbeTP2evYx3RbsEibDT0kWY5kaqcG2OY4sKWEPHdKXEe +DzDU5f6DBequ/rhpmBQj5Y5qbRR8TCCgAlLhPo5favos09HyPKGBGz4PLxBP9UbV +Qs2yjxJH9M2KC4omkNJQjVbocGjUVnsYk70JYjymB0DHvqUPW2c0WpEQDBIFadaa +tkdDQBiCZ7KcrLkBDQRfmHb1AQgAtInXbwTh0QwZC3yXv55dN5We1tCgLQXDaJlV +r7k4eJ54lYBn0+8xYvWJD6JZ3TPVsmf/vFVQLjMI1X5CLGS1BiWNtfQxGZ42gyXK +MGg7WoTJ4iHlbmIsAPL60/lt3j8aS6x+sBJlXVV0pklBMasMUXn+AvPDz4EbhMc4 +oLp9L4lqRb5759aMUoAYOp06HxfxbhM5EqE60DzPAKEP+WZ7fJea/o9fJCHeBuA7 +2AuJHOBcFPQPeqhFY1Bht3WMd5L5OhGbmR1mCsR7up9yVvdqaPTzE6aCDXEvbeZ9 +y/QSwpCAGFECUNIZbmiJrKL6HVVto6VozomkYISNVwSJ0SoI2QARAQABiQE2BBgB +CAAgFiEE3tfq+zchw8t7RxNrI+oMxASbosIFAl+YdvUCGwwACgkQI+oMxASbosJh +FQgAs/g+srMLJO4PZYx2vCrZXvi5M2YeIr4LZyyo8wu1vLRIyZixhQgEaDAz8Af/ +jnhhJaXb/xIvokspRHbV8+WckL+RDChibngqo5NMzsnjGThLWnmQ/Ys8q0ZF5kPD +QjR+CvR5seTF5a232YzgX2AoRONqle7SzxbkyzM0iTaT4cabzSvBd5CK7jRurAmK +C6+WlunG2mkfJ6t01Rx/aKoWJpSc2Frw7SCTBYhRq0qDdiRsDGHGlb5UdqEfR1b1 +DlR05txblUYSFun369BjpuPA5MWAEaAH7SI9ThCbfQ1vjUH1iIBxT4kX0GzRi859 +Yrp2jhdAzkXArMVq6CS8G+8H5w== +=/FAe +-----END PGP PUBLIC KEY BLOCK----- diff --git a/poetry.lock b/poetry.lock index ca4b6f6c..acf9701f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -983,6 +983,19 @@ version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" +[[package]] +category = "main" +description = "Pretty Good Privacy for Python" +name = "pgpy" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "0.5.3" + +[package.dependencies] +cryptography = ">=2.6" +pyasn1 = "*" +six = ">=1.9.0" + [[package]] category = "main" description = "a port of the serialize and unserialize functions of php to python." @@ -1688,7 +1701,7 @@ test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] -content-hash = "0e3ade53af79b805e718ebf5e7a047ff0d067ec4ed32154d77255579b1940e83" +content-hash = "16dc20a48cada11374ca312b3cbcccfe3b4b93380c6d8b3964eb6a64d37c1fe5" lock-version = "1.0" python-versions = "^3.7" @@ -2161,6 +2174,10 @@ pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, ] +pgpy = [ + {file = "PGPy-0.5.3-py2.py3-none-any.whl", hash = "sha256:cba6fbbb44a896a8a4f5807b3d8d4943a8f7a6607be11587f4a27734c711c1dd"}, + {file = "PGPy-0.5.3.tar.gz", hash = "sha256:a49c269cedcaf82ac6999bcae5fd3f543ecb1c759f9d48a15ad8d8fa4ac03987"}, +] phpserialize = [ {file = "phpserialize-1.3.tar.gz", hash = "sha256:bf672d312d203d09a84c26366fab8f438a3ffb355c407e69974b7ef2d39a0fa7"}, ] diff --git a/pyproject.toml b/pyproject.toml index 0cfc8c92..ee7ba22a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ memory_profiler = "^0.57.0" gevent = "^20.9.0" aiospamc = "^0.6.1" email_validator = "^1.1.1" +PGPy = "^0.5.3" [tool.poetry.dev-dependencies] pytest = "^6.1.0" diff --git a/tests/test_pgp_utils.py b/tests/test_pgp_utils.py index 8ec5bb5c..f72c79f4 100644 --- a/tests/test_pgp_utils.py +++ b/tests/test_pgp_utils.py @@ -1,107 +1,53 @@ +import os from io import BytesIO -from app.pgp_utils import load_public_key, gpg, encrypt_file +import pgpy +from pgpy import PGPMessage -pubkey = """-----BEGIN PGP PUBLIC KEY BLOCK----- -Version: Keybase OpenPGP v1.0.0 -Comment: https://keybase.io/crypto - -xo0EXlqP9wEEALoJsLHZA5W4yGQf+TIlIuYjj72SGEXbZyvMJxDk89YE8SWHAP+L -+GkyNBfiPidJ1putLBOTDuxjDroDa6zMmjxCORUYdtq35RIDo/raamAaYg32X/TI -3WyL3lgVf7K+VhXntG2V3OfM1r5nt3C1sy8Rsvzbih3p+eHpE3xCImg7ABEBAAHN -FFRlc3QgPHRlc3RAc2wubG9jYWw+wq0EEwEKABcFAl5aj/cCGy8DCwkHAxUKCAIe -AQIXgAAKCRCtrxG3FC3nSGhDA/wMT4PM8pWCsbsGA32SMN0j0MRsmc6KT4BGX8qd -CwTv7s5DvZlkFL9uJQxcKFe+yYpjnrPvW0p81ispj7pVJqUTyx4brZHiWFi/vODz -YyzTXNJvWJOp27G4YzWPeEeSKuGjF1CQScmZJA5luay7mkI5gttw4q3iqJlcDDFq -1sz2486NBF5aj/cBBADA7KbOa8klxOC8Oact0zzc30SCGxtCLuFQCBI/dIrnv2KC -lIbUd+CDlmD+cKCIu7MlrYPhCLF24MYnUXVFDbT3fP8YVy2HZTfk4Q64tj0S17ve -E9H1G1W6FqdDUhMCU1EmJgd8sKOrNOFtz4+b3IHJhtJIoUILDkiMjfUCHmQaqQAR -AQABwsCDBBgBCgAPBQJeWo/3BQkPCZwAAhsuAKgJEK2vEbcULedInSAEGQEKAAYF -Al5aj/cACgkQDjygQt7BuGFtPQQAmzUJqXB4UWo9HPZfutqSU6GElSMwZq1Dlf8S -Stjq7cYK+HSfcyw4wSBMRxMtG2zmbyhWlYTqx3fAAjgE32dBI/Rq8ku60u6SGEiE -egKCcm0lyR1TVUTYEsfjiYD5AmGWng8tTavz1ANdEoE66wGApkETfmTM7hOuQrKm -BjXpmembUQP5AXln8rWuDkeXVXhBa5RR3NgoD/fos2QJ5NxkZdfPmM57EwQkEXKv -S3c5rlvvhIupElSyJkxOzfykNlJewVrLxCicj+JPSt7ly6YlkMQglyevntI46y1l -2Msf0oeQZ3uedURGQiGQalC7nzPFnOARbNffFEJI3cJhcLkr2UFdL0rOjQReWo/3 -AQQA+MJeovqVVVrE1Vsc3M/BuG5ao7xyP1y7YhgmJg3gi8HR7b4/ySJtKnCYAmLg -wwjfCUWed/GZ+3bGw48x8Fmn+6QTPG04j8RUOMUgVt9jc+TxC8VWSvqH1Taho3MK -6ZQpCwXPO0FmWc5ybp0AJzqy2YS4eZwue1WH3zFzjXrOBd0AEQEAAcLAgwQYAQoA -DwUCXlqP9wUJDwmcAAIbLgCoCRCtrxG3FC3nSJ0gBBkBCgAGBQJeWo/3AAoJEFJq -ki+hZCNMgH4EAPiamTuezRtMIEWpjEjYGjpRF+2uj5VmU6N2E6+5Nh73HUKNCVRj -AWeRarplye/CqZyhzPgotDNzAPzE4smo0N0vvc4zi6toqMiO4ODjR313d0y0v4iP -+n576QwpfGw/ddlTEL7Iv28dzdKJArjNc2/jRxefHrAYSzjEunl/GUq+ToQD/izQ -mPo6SWhlODsIy4eR/u3NpKtQQcs40XWLVci6M66ntyl5XmBGgFFu0WHIYeDOnTRc -qL1W5yEYaaJhaEbmNGk3tf26Ns9cTl91S2eylO9nWGOnqFg58jP63TZVR7q3jIq1 -e5DKgszG2Vvye+bbK6qMKmaIXEMhnjw9eZuW6MGf -=yDVI ------END PGP PUBLIC KEY BLOCK----- -""" - -private_key = """-----BEGIN PGP PRIVATE KEY BLOCK----- -Version: Keybase OpenPGP v1.0.0 -Comment: https://keybase.io/crypto - -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 -U9jtqNTsHrr0XFzA2zno+zcopiZZOoPXcwxLf+pCetjN5EOkpMgqZTtV2nCppQRB -d3ZpsguOO4OVexEW6gWGOuas5+/qa846it9VMo+nlqtLyIAFbj2P02Zk/QrUnPF3 -PEjKDJssrOEnZWlpAdEDfFhC1OrBVlG0lkD1qHDCNO9MTeT2dRMghbFGxlno9z2K -wnnB+Ep4UULuvbh08GsVflQPaA0a59IFDbOzYc7puS5kpJ5fQWwdXZvjNc/jOeQX -BHaLfQKmWYW3pCxs0BqKRhAnZ9E+kkIL6xU6MlJPs/NGO7aAykrFv8BdmBJQ8s00 -9LGlgSUhdEdIsn5h3Kdn0f/7FXXWwsCDBBgBCgAPBQJeWo/3BQkPCZwAAhsuAKgJ -EK2vEbcULedInSAEGQEKAAYFAl5aj/cACgkQUmqSL6FkI0yAfgQA+JqZO57NG0wg -RamMSNgaOlEX7a6PlWZTo3YTr7k2HvcdQo0JVGMBZ5FqumXJ78KpnKHM+Ci0M3MA -/MTiyajQ3S+9zjOLq2ioyI7g4ONHfXd3TLS/iI/6fnvpDCl8bD912VMQvsi/bx3N -0okCuM1zb+NHF58esBhLOMS6eX8ZSr5OhAP+LNCY+jpJaGU4OwjLh5H+7c2kq1BB -yzjRdYtVyLozrqe3KXleYEaAUW7RYchh4M6dNFyovVbnIRhpomFoRuY0aTe1/bo2 -z1xOX3VLZ7KU72dYY6eoWDnyM/rdNlVHureMirV7kMqCzMbZW/J75tsrqowqZohc -QyGePD15m5bowZ8= -=4OSo ------END PGP PRIVATE KEY BLOCK-----""" +from app.config import ROOT_DIR +from app.pgp_utils import ( + load_public_key, + gpg, + encrypt_file, + encrypt_file_with_pgpy, +) def test_load_public_key(): - load_public_key(pubkey) + public_key_path = os.path.join(ROOT_DIR, "local_data/public-pgp.asc") + public_key = open(public_key_path).read() + load_public_key(public_key) assert len(gpg.list_keys()) == 1 def test_encrypt(): - fingerprint = load_public_key(pubkey) + public_key_path = os.path.join(ROOT_DIR, "local_data/public-pgp.asc") + public_key = open(public_key_path).read() + fingerprint = load_public_key(public_key) secret = encrypt_file(BytesIO(b"abcd"), fingerprint) assert secret != "" + + +def test_encrypt_file_with_pgpy(): + encrypt_decrypt_text("heyhey") + encrypt_decrypt_text("👍💪") + encrypt_decrypt_text("éèù") + encrypt_decrypt_text("片仮名") + + +def encrypt_decrypt_text(text: str): + public_key_path = os.path.join(ROOT_DIR, "local_data/public-pgp.asc") + public_key = open(public_key_path).read() + + encrypted: PGPMessage = encrypt_file_with_pgpy(text.encode(), public_key) + + # decrypt + private_key_path = os.path.join(ROOT_DIR, "local_data/private-pgp.asc") + private_key = open(private_key_path).read() + priv = pgpy.PGPKey() + priv.parse(private_key) + decrypted = priv.decrypt(encrypted).message + if type(decrypted) == str: + assert decrypted == text + elif type(decrypted) == bytearray: + assert decrypted.decode() == text