From 61e120465d17c69e26f67142bc3bc6eb5b6d6456 Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 5 Apr 2025 13:58:38 +0200 Subject: [PATCH] fetch keys from keys.openpgp.org --- src/keyserver_bot/hooks.py | 29 +++++++++++++++++------------ src/keyserver_bot/koo.py | 19 +++++++++++++++++++ src/keyserver_bot/wkd.py | 10 +++++++--- tests/test_hooks.py | 13 +++++++++++++ tests/test_koo.py | 13 +++++++++++++ tests/test_wkd.py | 3 ++- 6 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 src/keyserver_bot/koo.py create mode 100644 tests/test_hooks.py create mode 100644 tests/test_koo.py diff --git a/src/keyserver_bot/hooks.py b/src/keyserver_bot/hooks.py index 46fad0f..0120a70 100644 --- a/src/keyserver_bot/hooks.py +++ b/src/keyserver_bot/hooks.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 from deltachat_rpc_client import events, run_bot_cli -import wkd -import koo -import vcard +from keyserver_bot.wkd import request_from_wkd +from keyserver_bot.koo import request_from_koo +from keyserver_bot.vcard import construct_vcard, save_vcard hooks = events.HookCollection() @@ -17,20 +17,25 @@ def log_event(event): def echo(event): snapshot = event.message_snapshot email = snapshot - domain = email.split("@")[1] - public_key = wkd.request_by_email(email, domain) + public_key = request_key_by_email(email) if not public_key: - public_key = wkd.request_by_email(email, f"openpgpkey.{domain}") - if not public_key: - public_key = wkd.request_by_email(email, "wkd.keys.openpgp.org") - if not public_key: - snapshot.chat.send_text("Sorry, I could not find a key for this user.") - return + snapshot.chat.send_text("Sorry, I could not find a key for this user.") - vcard_path = vcard.save_vcard(public_key) + vcard = construct_vcard(email, public_key) + vcard_path = save_vcard(vcard) snapshot.chat.send_file(vcard_path) +def request_key_by_email(email) -> str: + domain = email.split("@")[1] + public_key = request_from_wkd(email, domain) + if not public_key: + public_key = request_from_wkd(email, f"openpgpkey.{domain}") + if not public_key: + public_key = request_from_koo(email) + return public_key + + def main(): run_bot_cli(hooks) diff --git a/src/keyserver_bot/koo.py b/src/keyserver_bot/koo.py new file mode 100644 index 0000000..c813292 --- /dev/null +++ b/src/keyserver_bot/koo.py @@ -0,0 +1,19 @@ +import requests + + +def request_from_koo(email: str) -> str: + """Request the public key from keys.openpgp.org by email + + :param email: an RFC 5322 email address + :return: the public key of the user, or "" if it isn't found + """ + koo_url = f"https://keys.openpgp.org/vks/v1/by-email/{email}" + r = requests.get(koo_url) + if "No key found for email address" in r.content.decode(): + return "" + keyparts = [] + for line in r.content.splitlines(): + l = line.decode() + if l != "" and not l.startswith("Comment: ") and not l.startswith("-----"): + keyparts.append(l) + return "".join(keyparts) diff --git a/src/keyserver_bot/wkd.py b/src/keyserver_bot/wkd.py index 5f3a00e..0305591 100644 --- a/src/keyserver_bot/wkd.py +++ b/src/keyserver_bot/wkd.py @@ -3,7 +3,7 @@ import requests import base64 -def request_by_email(email: str, server: str) -> str: +def request_from_wkd(email: str, server: str) -> str: """Request the public key from WKD by email :param email: an RFC 5322 email address @@ -12,7 +12,11 @@ def request_by_email(email: str, server: str) -> str: localpart, domain = email.split("@") wkd_hash = wkdhash.userid_to_wkd_hash(email) wkd_url = f"https://{server}/.well-known/openpgpkey/{domain}/hu/{wkd_hash}?l={localpart}" - r = requests.get(wkd_url) - if b"The requested URL was not found on this server." in r.content: + try: + r = requests.get(wkd_url) + except requests.exceptions.SSLError: + print(f"SSL Error when querying {wkd_url}") + return "" + if r.status_code >= 400: return "" return base64.b64encode(r.content).decode().strip() diff --git a/tests/test_hooks.py b/tests/test_hooks.py new file mode 100644 index 0000000..7c1bb4f --- /dev/null +++ b/tests/test_hooks.py @@ -0,0 +1,13 @@ +import pytest +from keyserver_bot.hooks import request_key_by_email + +@pytest.mark.parametrize( + ("email", "public_key"), + [ + ("missytake@systemli.org", "xjMEXcLIEBYJKwYBBAHaRw8BAQdAmlYU7TEgGL3eq2WXC95tQtZYHjpJOCjb7qq3vJd1lG7NIm1pc3N5dGFrZSA8bWlzc3l0YWtlQHN5c3RlbWxpLm9yZz7CkAQTFggAOAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuTLAAoJEATMZlgyBRjfeUUA/0P0E/quL71dn5Zjc4ewsnykT1GODazGmk+xprSwAvEyAPwJy+uJgJz5pSFdmi2lGRrOERmKUu8AdQ/M7dSm6kt3AMKQBBMWCAA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5FkACgkQBMxmWDIFGN+0EAD+PVALMtR4PDB4aaxpJ5L1p6mGmq1lb8wqZtdAyHK9+7EA/jPU12/e368B6OHknY1YzGxQxyPETcL8hUS26CU6wnUEzRZtaXNzeXRha2VAc3lzdGVtbGkub3JnwpAEExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQRF/+pCPaQsOzGFVFkEzGZYMgUY3wUCZw7kywAKCRAEzGZYMgUY37s7AP4uOSeC9cwDKnPnov7L6Sp0axUNncX+n5sjepkPpwgVIQD/Zt85kzYrodvsx4QdolweWDFrH9DFxTsTSw2GWIIE6Q3CkAQTFggAOAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuRZAAoJEATMZlgyBRjf3lkBAPdeXSQY9oPO4wHv+pYE5d6+4ij8plA6tSReaqhneOKtAQCJguCnqcH8A9KKZ97n1gIBFnJ7xhdNTPLPoAbYE1BAB8KWBBMWCAA+FiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAl3CyBACGwMFCQlmAYAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQBMxmWDIFGN9eHAD/SR+CRWWJ5km4pfeU48Y88+6nKA/T6egmav9NIZTjw9cBAIEk6yInsxZj/2Ot9AW8vr+cyuiG9FtZOuWvbNh4oYUJzjMEXcLKyhYJKwYBBAHaRw8BAQdAtUIjrOUmkTbVVkwIXeAkC/s3Z1wrW2/+KIgkQbbi38XCeAQYFggAIAIbIBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuULAAoJEATMZlgyBRjf19ABALR4jIhWK/5e87V3+xuX9R0MAyBDztZjb8nfXJ3HWK65AQC6S7JybxebS+jHSavTIF0nuaaqBXZx3me1edqwVVBAD8J+BBgWCAAmFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAl3CysoCGyAFCQlmAYAACgkQBMxmWDIFGN/IQgEA8WwKDEEtbyIiCr5pLD/eqJ2m1xIsKKP/0sH0ADPMwjEA/jvMjOjbrh5WZuUnf+ddcVB7GStu3SZtenkB/rK1+s0EzjgEXcLIEBIKKwYBBAGXVQEFAQEHQGTy3kLa+rSeHhK35fDN/k46zZFh+LDZQ0a2552FuPJ6AwEIB8J4BBgWCAAgAhsMFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5P8ACgkQBMxmWDIFGN/jJgEA5+ESV8PtiUcwxml7GpTGwbIv8GoDA1YlPcUeku/S20QA/iVvzlf8Oj5Wvhet8VMzU37wWsFJ2n6aAyM2WmOrPVoPwn4EGBYIACYWIQRF/+pCPaQsOzGFVFkEzGZYMgUY3wUCXcLIEAIbDAUJCWYBgAAKCRAEzGZYMgUY3zZnAP9JMp+PI+1H4x3D62Qg4udjL6zypFKTrcrUnyWNcoWzoQD/W3uJ7M2sbOdAdHcj246koYP32BrR/7Wtc0/7yJPfqQk==oePr"), + ("adsokasd@ingrdsuf.org", ""), + ("sdjif2mlij@protonmail.com", "xjMEZ+/HahYJKwYBBAHaRw8BAQdAzRZOcQEkaPGwUYBVHsKTVX+4nP05ZwLQ3wYUod1IQyjNNXNkamlmMm1saWpAcHJvdG9ubWFpbC5jb20gPHNkamlmMm1saWpAcHJvdG9ubWFpbC5jb20+wsARBBMWCgCDBYJn78dqAwsJBwmQ9od3MCAJzKNFFAAAAAAAHAAgc2FsdEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ3e7fGtn2OkXwNFoBOAEgwoRygRlwr41fcO4LsJ/1HcyAxUKCAQWAAIBAhkBApsDAh4BFiEERU5l1enHSVCTOUnc9od3MCAJzKMAAAuPAQCQ0PFlbngJRtJ36yloWfHwTY168TDlMGeL+nAxJ1GUQAEA31H+NukIwEzCwQWh7aoFeY9eJHXVoaeh0swfvJA5yQ/CwB4EEBYIAJAFgmfvx6cFgwDtTgAJENgGwa9ZeOjHNRQAAAAAABwAEHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmfU5CAfgrGEZMQfk/lTyXT2LBxvcGVucGdwLWNhQHByb3Rvbi5tZSA8b3BlbnBncC1jYUBwcm90b24ubWU+FiEECoZS/l1TOGBXiZ/p2AbBr1l46McAAJCrAP4tMWMSLKAykI/JnR+94aE2+E4dCTslkW/svb1o7zGLbwEAg/fo6sOxa3wHr9xnCbUUrxZ0Dnh/21zJ3atL6zMpYQHOOARn78dqEgorBgEEAZdVAQUBAQdATm5zMhEn2/S+UB3qKfeV424+iyvIr2p3FEzxuzUQKQEDAQgHwr4EGBYKAHAFgmfvx2oJkPaHdzAgCcyjRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmf94HNvbCT5aX/ifcaDK2bIjzmS69JdQo1AcOt3X8AwFQKbDBYhBEVOZdXpx0lQkzlJ3PaHdzAgCcyjAACVUgEA8Agw7R9+a72WsKOq691JIYPGDfNYCKNHFllsQAfHHHYA/3x+rG9YQTbdAuuFi1ciGvgo9DAWVv8Bt7ibBqEVAuQN"), + ], +) +def test_request_by_email(email, public_key): + assert public_key == request_key_by_email(email) diff --git a/tests/test_koo.py b/tests/test_koo.py new file mode 100644 index 0000000..387aefc --- /dev/null +++ b/tests/test_koo.py @@ -0,0 +1,13 @@ +import pytest +import keyserver_bot.koo + + +@pytest.mark.parametrize( + ("email", "public_key"), + [ + ("missytake@systemli.org", "xjMEXcLIEBYJKwYBBAHaRw8BAQdAmlYU7TEgGL3eq2WXC95tQtZYHjpJOCjb7qq3vJd1lG7NIm1pc3N5dGFrZSA8bWlzc3l0YWtlQHN5c3RlbWxpLm9yZz7CkAQTFggAOAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuTLAAoJEATMZlgyBRjfeUUA/0P0E/quL71dn5Zjc4ewsnykT1GODazGmk+xprSwAvEyAPwJy+uJgJz5pSFdmi2lGRrOERmKUu8AdQ/M7dSm6kt3AMKQBBMWCAA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5FkACgkQBMxmWDIFGN+0EAD+PVALMtR4PDB4aaxpJ5L1p6mGmq1lb8wqZtdAyHK9+7EA/jPU12/e368B6OHknY1YzGxQxyPETcL8hUS26CU6wnUEzRZtaXNzeXRha2VAc3lzdGVtbGkub3JnwpAEExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQRF/+pCPaQsOzGFVFkEzGZYMgUY3wUCZw7kywAKCRAEzGZYMgUY37s7AP4uOSeC9cwDKnPnov7L6Sp0axUNncX+n5sjepkPpwgVIQD/Zt85kzYrodvsx4QdolweWDFrH9DFxTsTSw2GWIIE6Q3CkAQTFggAOAIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuRZAAoJEATMZlgyBRjf3lkBAPdeXSQY9oPO4wHv+pYE5d6+4ij8plA6tSReaqhneOKtAQCJguCnqcH8A9KKZ97n1gIBFnJ7xhdNTPLPoAbYE1BAB8KWBBMWCAA+FiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAl3CyBACGwMFCQlmAYAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQBMxmWDIFGN9eHAD/SR+CRWWJ5km4pfeU48Y88+6nKA/T6egmav9NIZTjw9cBAIEk6yInsxZj/2Ot9AW8vr+cyuiG9FtZOuWvbNh4oYUJzjMEXcLKyhYJKwYBBAHaRw8BAQdAtUIjrOUmkTbVVkwIXeAkC/s3Z1wrW2/+KIgkQbbi38XCeAQYFggAIAIbIBYhBEX/6kI9pCw7MYVUWQTMZlgyBRjfBQJnDuULAAoJEATMZlgyBRjf19ABALR4jIhWK/5e87V3+xuX9R0MAyBDztZjb8nfXJ3HWK65AQC6S7JybxebS+jHSavTIF0nuaaqBXZx3me1edqwVVBAD8J+BBgWCAAmFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAl3CysoCGyAFCQlmAYAACgkQBMxmWDIFGN/IQgEA8WwKDEEtbyIiCr5pLD/eqJ2m1xIsKKP/0sH0ADPMwjEA/jvMjOjbrh5WZuUnf+ddcVB7GStu3SZtenkB/rK1+s0EzjgEXcLIEBIKKwYBBAGXVQEFAQEHQGTy3kLa+rSeHhK35fDN/k46zZFh+LDZQ0a2552FuPJ6AwEIB8J4BBgWCAAgAhsMFiEERf/qQj2kLDsxhVRZBMxmWDIFGN8FAmcO5P8ACgkQBMxmWDIFGN/jJgEA5+ESV8PtiUcwxml7GpTGwbIv8GoDA1YlPcUeku/S20QA/iVvzlf8Oj5Wvhet8VMzU37wWsFJ2n6aAyM2WmOrPVoPwn4EGBYIACYWIQRF/+pCPaQsOzGFVFkEzGZYMgUY3wUCXcLIEAIbDAUJCWYBgAAKCRAEzGZYMgUY3zZnAP9JMp+PI+1H4x3D62Qg4udjL6zypFKTrcrUnyWNcoWzoQD/W3uJ7M2sbOdAdHcj246koYP32BrR/7Wtc0/7yJPfqQk==oePr"), + ("adsokasd@ingrdsuf.org", ""), + ], +) +def test_request_by_email(email, public_key): + assert public_key == keyserver_bot.koo.request_from_koo(email) diff --git a/tests/test_wkd.py b/tests/test_wkd.py index 8e243c8..a23d99d 100644 --- a/tests/test_wkd.py +++ b/tests/test_wkd.py @@ -5,9 +5,10 @@ import keyserver_bot.wkd @pytest.mark.parametrize( ("email", "server", "public_key"), [ + ("sdjif2mlij@protonmail.com", "protonmail.com", ""), ("sdjif2mlij@protonmail.com", "openpgpkey.protonmail.com", "xjMEZ+/HahYJKwYBBAHaRw8BAQdAzRZOcQEkaPGwUYBVHsKTVX+4nP05ZwLQ3wYUod1IQyjNNXNkamlmMm1saWpAcHJvdG9ubWFpbC5jb20gPHNkamlmMm1saWpAcHJvdG9ubWFpbC5jb20+wsARBBMWCgCDBYJn78dqAwsJBwmQ9od3MCAJzKNFFAAAAAAAHAAgc2FsdEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ3e7fGtn2OkXwNFoBOAEgwoRygRlwr41fcO4LsJ/1HcyAxUKCAQWAAIBAhkBApsDAh4BFiEERU5l1enHSVCTOUnc9od3MCAJzKMAAAuPAQCQ0PFlbngJRtJ36yloWfHwTY168TDlMGeL+nAxJ1GUQAEA31H+NukIwEzCwQWh7aoFeY9eJHXVoaeh0swfvJA5yQ/CwB4EEBYIAJAFgmfvx6cFgwDtTgAJENgGwa9ZeOjHNRQAAAAAABwAEHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmfU5CAfgrGEZMQfk/lTyXT2LBxvcGVucGdwLWNhQHByb3Rvbi5tZSA8b3BlbnBncC1jYUBwcm90b24ubWU+FiEECoZS/l1TOGBXiZ/p2AbBr1l46McAAJCrAP4tMWMSLKAykI/JnR+94aE2+E4dCTslkW/svb1o7zGLbwEAg/fo6sOxa3wHr9xnCbUUrxZ0Dnh/21zJ3atL6zMpYQHOOARn78dqEgorBgEEAZdVAQUBAQdATm5zMhEn2/S+UB3qKfeV424+iyvIr2p3FEzxuzUQKQEDAQgHwr4EGBYKAHAFgmfvx2oJkPaHdzAgCcyjRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmf94HNvbCT5aX/ifcaDK2bIjzmS69JdQo1AcOt3X8AwFQKbDBYhBEVOZdXpx0lQkzlJ3PaHdzAgCcyjAACVUgEA8Agw7R9+a72WsKOq691JIYPGDfNYCKNHFllsQAfHHHYA/3x+rG9YQTbdAuuFi1ciGvgo9DAWVv8Bt7ibBqEVAuQN"), ("missytake@systemli.org", "openpgpkey.systemli.org", "") ], ) def test_request_by_email(email, server, public_key): - assert public_key == keyserver_bot.wkd.request_by_email(email, server) + assert public_key == keyserver_bot.wkd.request_from_wkd(email, server)