From ce4f9ed8f4fccc4d452081e05d969b664e4a4010 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 10 Apr 2025 21:34:02 +0200 Subject: [PATCH] feat: get public key from attachment if it exists --- setup.cfg | 1 + src/keyserver_bot/hooks.py | 38 ++++++++++++++++++++++++++++++++-- tests/assets/missytake-pub.asc | 30 +++++++++++++++++++++++++++ tests/test_hooks.py | 21 ++++++++++++++++++- 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 tests/assets/missytake-pub.asc diff --git a/setup.cfg b/setup.cfg index d76e228..ec5c4d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ install_requires = wkdhash requests email-validator + PGPy [options.packages.find] where = src diff --git a/src/keyserver_bot/hooks.py b/src/keyserver_bot/hooks.py index 5ae05da..53b1f24 100644 --- a/src/keyserver_bot/hooks.py +++ b/src/keyserver_bot/hooks.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import pgpy from deltachat_rpc_client import events, run_bot_cli, EventType, Message from email_validator import validate_email, EmailNotValidError @@ -24,15 +25,23 @@ def command(event): return snapshot.chat.send_text(snapshot.chat.account.get_qr_code()) if snapshot.text == "Messages are guaranteed to be end-to-end encrypted from now on.": return + email = snapshot.text + public_key = None + + if snapshot.get('file'): + public_key, email_from_uid = import_key_from_attachment(snapshot.file) + email = email if email else email_from_uid + try: validate_email(email, check_deliverability=False) except EmailNotValidError: return snapshot.chat.send_text(HELP_MSG + f"\n\n{email} is not an email address :/") - public_key = request_key_by_email(email) if not public_key: - return snapshot.chat.send_text(f"Sorry, I could not find a key for {email}.") + public_key = request_key_by_email(email) + if not public_key: + return snapshot.chat.send_text(f"Sorry, I could not find a key for {email}.") vcard = construct_vcard(email, public_key) vcard_path = f"/tmp/{email}.vcf" @@ -40,6 +49,31 @@ def command(event): return snapshot.chat.send_file(vcard_path) +def import_key_from_attachment(file_path: str) -> (str, str): + """Import a PGP public key from an attachment. + + :param file_path: the path to the attachment + :return: a tuple with the public key and a User ID + """ + try: + key = pgpy.PGPKey.from_file(file_path)[0] + except AttributeError: + return "", "" + except pgpy.errors.PGPError: + return "", "" + ascii_key = str(key) + keyparts = [] + for line in ascii_key.splitlines(): + if line != "" and not line.startswith("Comment: ") and not line.startswith("-----"): + keyparts.append(line) + uid = key.get_uid("").userid + try: + validate_email(uid, check_deliverability=False) + except EmailNotValidError: + uid = key.get_uid("").email + return "".join(keyparts), uid + + def request_key_by_email(email) -> str: domain = email.split("@")[1] public_key = request_from_wkd(email, domain) diff --git a/tests/assets/missytake-pub.asc b/tests/assets/missytake-pub.asc new file mode 100644 index 0000000..8f28a9c --- /dev/null +++ b/tests/assets/missytake-pub.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEXcLIEBYJKwYBBAHaRw8BAQdAmlYU7TEgGL3eq2WXC95tQtZYHjpJOCjb7qq3 +vJd1lG60Im1pc3N5dGFrZSA8bWlzc3l0YWtlQHN5c3RlbWxpLm9yZz6IkAQTFggA +OAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEX/6kI9pCw7MYVUWQTMZlgy +BRjfBQJnDuTLAAoJEATMZlgyBRjfeUUA/0P0E/quL71dn5Zjc4ewsnykT1GODazG +mk+xprSwAvEyAPwJy+uJgJz5pSFdmi2lGRrOERmKUu8AdQ/M7dSm6kt3AIiQBBMW +CAA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEERf/qQj2kLDsxhVRZBMxm +WDIFGN8FAmcO5FkACgkQBMxmWDIFGN+0EAD+PVALMtR4PDB4aaxpJ5L1p6mGmq1l +b8wqZtdAyHK9+7EA/jPU12/e368B6OHknY1YzGxQxyPETcL8hUS26CU6wnUEtBZt +aXNzeXRha2VAc3lzdGVtbGkub3JniJAEExYIADgCGwMFCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AWIQRF/+pCPaQsOzGFVFkEzGZYMgUY3wUCZw7kywAKCRAEzGZYMgUY +37s7AP4uOSeC9cwDKnPnov7L6Sp0axUNncX+n5sjepkPpwgVIQD/Zt85kzYrodvs +x4QdolweWDFrH9DFxTsTSw2GWIIE6Q2IlgQTFggAPhYhBEX/6kI9pCw7MYVUWQTM +ZlgyBRjfBQJdwsgQAhsDBQkJZgGABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ +EATMZlgyBRjfXhwA/0kfgkVlieZJuKX3lOPGPPPupygP0+noJmr/TSGU48PXAQCB +JOsiJ7MWY/9jrfQFvL6/nMrohvRbWTrlr2zYeKGFCYiQBBMWCAA4AhsDBQsJCAcC +BhUKCQgLAgQWAgMBAh4BAheAFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5FkA +CgkQBMxmWDIFGN/eWQEA915dJBj2g87jAe/6lgTl3r7iKPymUDq1JF5qqGd44q0B +AImC4KepwfwD0opn3ufWAgEWcnvGF01M8s+gBtgTUEAHuDgEXcLIEBIKKwYBBAGX +VQEFAQEHQGTy3kLa+rSeHhK35fDN/k46zZFh+LDZQ0a2552FuPJ6AwEIB4h4BBgW +CAAgAhsMFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5P8ACgkQBMxmWDIFGN/j +JgEA5+ESV8PtiUcwxml7GpTGwbIv8GoDA1YlPcUeku/S20QA/iVvzlf8Oj5Wvhet +8VMzU37wWsFJ2n6aAyM2WmOrPVoPuDMEXcLKyhYJKwYBBAHaRw8BAQdAtUIjrOUm +kTbVVkwIXeAkC/s3Z1wrW2/+KIgkQbbi38WIeAQYFggAIAIbIBYhBEX/6kI9pCw7 +MYVUWQTMZlgyBRjfBQJnDuULAAoJEATMZlgyBRjf19ABALR4jIhWK/5e87V3+xuX +9R0MAyBDztZjb8nfXJ3HWK65AQC6S7JybxebS+jHSavTIF0nuaaqBXZx3me1edqw +VVBADw== +=Zqcr +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 146e7c1..399e188 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -2,7 +2,7 @@ import os from email_validator import validate_email, EmailNotValidError import pytest -from keyserver_bot.hooks import request_key_by_email, delete_data +from keyserver_bot.hooks import request_key_by_email, delete_data, import_key_from_attachment from deltachat_rpc_client.pytestplugin import acfactory @@ -60,3 +60,22 @@ def test_validate_email(email, valid): validate_email(email, check_deliverability=False) except EmailNotValidError: assert not valid + + +@pytest.mark.parametrize( + ("file_path", "result"), + [ + ( + "tests/assets/missytake-pub.asc", + ( + "mDMEXcLIEBYJKwYBBAHaRw8BAQdAmlYU7TEgGL3eq2WXC95tQtZYHjpJOCjb7qq3vJd1lG60Fm1pc3N5dGFrZUBzeXN0ZW1saS5vcmeIlgQTFggAPhYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJdwsgQAhsDBQkJZgGABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEATMZlgyBRjfXhwA/0kfgkVlieZJuKX3lOPGPPPupygP0+noJmr/TSGU48PXAQCBJOsiJ7MWY/9jrfQFvL6/nMrohvRbWTrlr2zYeKGFCYiQBBMWCAA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5FkACgkQBMxmWDIFGN/eWQEA915dJBj2g87jAe/6lgTl3r7iKPymUDq1JF5qqGd44q0BAImC4KepwfwD0opn3ufWAgEWcnvGF01M8s+gBtgTUEAHiJAEExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQRF/+pCPaQsOzGFVFkEzGZYMgUY3wUCZw7kywAKCRAEzGZYMgUY37s7AP4uOSeC9cwDKnPnov7L6Sp0axUNncX+n5sjepkPpwgVIQD/Zt85kzYrodvsx4QdolweWDFrH9DFxTsTSw2GWIIE6Q20Im1pc3N5dGFrZSA8bWlzc3l0YWtlQHN5c3RlbWxpLm9yZz6IkAQTFggAOAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuRZAAoJEATMZlgyBRjftBAA/j1QCzLUeDwweGmsaSeS9aephpqtZW/MKmbXQMhyvfuxAP4z1Ndv3t+vAejh5J2NWMxsUMcjxE3C/IVEtuglOsJ1BIiQBBMWCAA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5MsACgkQBMxmWDIFGN95RQD/Q/QT+q4vvV2flmNzh7CyfKRPUY4NrMaaT7GmtLAC8TIA/AnL64mAnPmlIV2aLaUZGs4RGYpS7wB1D8zt1KbqS3cAuDgEXcLIEBIKKwYBBAGXVQEFAQEHQGTy3kLa+rSeHhK35fDN/k46zZFh+LDZQ0a2552FuPJ6AwEIB4h4BBgWCAAgAhsMFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5P8ACgkQBMxmWDIFGN/jJgEA5+ESV8PtiUcwxml7GpTGwbIv8GoDA1YlPcUeku/S20QA/iVvzlf8Oj5Wvhet8VMzU37wWsFJ2n6aAyM2WmOrPVoPuDMEXcLKyhYJKwYBBAHaRw8BAQdAtUIjrOUmkTbVVkwIXeAkC/s3Z1wrW2/+KIgkQbbi38WIeAQYFggAIAIbIBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuULAAoJEATMZlgyBRjf19ABALR4jIhWK/5e87V3+xuX9R0MAyBDztZjb8nfXJ3HWK65AQC6S7JybxebS+jHSavTIF0nuaaqBXZx3me1edqwVVBADw===wNTN", + "missytake@systemli.org", + ), + ), + ("tests/test_hooks.py", ("", "")), + ], +) +def test_import_key_from_attachment(file_path, result): + imported = import_key_from_attachment(file_path) + assert result[0] == imported[0] + assert result[1] == imported[1]