feat: upload PGP keys to keys.openpgp.org
This commit is contained in:
parent
d55cf64953
commit
b4d8a5279b
|
|
@ -4,9 +4,9 @@ from deltachat_rpc_client import events, run_bot_cli, EventType, Message
|
|||
from email_validator import validate_email, EmailNotValidError
|
||||
|
||||
from keyserver_bot.wkd import request_from_wkd, WKD_TIMEOUT
|
||||
from keyserver_bot.koo import request_from_koo
|
||||
from keyserver_bot.koo import request_from_koo, upload_to_koo
|
||||
from keyserver_bot.attachment import import_key_from_attachment
|
||||
from keyserver_bot.vcard import construct_vcard, save_vcard
|
||||
from keyserver_bot.vcard import construct_vcard, parse_vcard, save_vcard
|
||||
from keyserver_bot.errors import KeyNotFound
|
||||
|
||||
hooks = events.HookCollection()
|
||||
|
|
@ -19,6 +19,9 @@ HELP_MSG = (
|
|||
"\n\n"
|
||||
"If you send me a public PGP key, "
|
||||
"you will also get a contact for it."
|
||||
"\n\n"
|
||||
"With /publish you can upload your key "
|
||||
"or one of your contacts' keys to keys.openpgp.org."
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -28,8 +31,18 @@ def command(event):
|
|||
|
||||
if snapshot.text == "/generate-invite":
|
||||
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.":
|
||||
elif snapshot.text == "Messages are guaranteed to be end-to-end encrypted from now on.":
|
||||
return
|
||||
elif snapshot.text == "/publish":
|
||||
vcard_to_upload = snapshot.sender.make_vcard()
|
||||
if snapshot.get("file"):
|
||||
with open(snapshot.get("file"), "r") as f:
|
||||
attachment = f.read()
|
||||
if attachment.startswith("BEGIN:VCARD\nVERSION:4.0\n"):
|
||||
vcard_to_upload = attachment
|
||||
email, public_key, _ = parse_vcard(vcard_to_upload)
|
||||
reply = upload_to_koo(email, public_key)
|
||||
return snapshot.chat.send_text(reply)
|
||||
|
||||
try:
|
||||
public_key, email, display_name = handle_command(snapshot.text, snapshot.get("file"))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import base64
|
||||
|
||||
import pgpy
|
||||
import requests
|
||||
|
||||
|
||||
|
|
@ -27,3 +30,32 @@ def request_from_koo(email: str) -> str:
|
|||
if l != "" and not l.startswith("Comment: ") and not l.startswith("-----"):
|
||||
keyparts.append(l)
|
||||
return "".join(keyparts)
|
||||
|
||||
|
||||
def upload_to_koo(email: str, public_key: str) -> str:
|
||||
"""Uploads a key to keys.openpgp.org and requests a verification email to publish it.
|
||||
|
||||
:param email: the email address to publish a key for
|
||||
:param public_key: the PGP key to publish
|
||||
:return: the reply to the user
|
||||
"""
|
||||
if not public_key:
|
||||
return "Sorry, this contact contains no PGP key."
|
||||
|
||||
binary_key = base64.b64decode(public_key)
|
||||
key_object = pgpy.PGPKey().from_blob(binary_key)[0]
|
||||
|
||||
r = requests.post("https://keys.openpgp.org/vks/v1/upload", json={"keytext": str(key_object)})
|
||||
if r.json().get("status"):
|
||||
if r.json().get("status").get(email) == "published":
|
||||
public_key_fpr = key_object.fingerprint
|
||||
if r.json().get("key_fpr") == public_key_fpr:
|
||||
return f"This key is already published for {email} :)"
|
||||
if r.json().get("token"):
|
||||
json = {
|
||||
"token": r.json()["token"],
|
||||
"addresses": [email],
|
||||
}
|
||||
requests.post("https://keys.openpgp.org/vks/v1/request-verify", json=json)
|
||||
return f"Thanks! {email} should receive a confirmation link from keyserver@keys.openpgp.org, use it to publish the key."
|
||||
return str(r.json())
|
||||
|
|
|
|||
|
|
@ -2,15 +2,43 @@ import pytest
|
|||
import keyserver_bot.koo
|
||||
|
||||
|
||||
SYSTEMLI_ADDRESS = "missytake@systemli.org"
|
||||
SYSTEMLI_KEY = "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"
|
||||
NON_EXISTANT_ADDRESS = "adsokasd@ingrdsuf.org"
|
||||
NINE_ADDRESS = "ap3m5cs63@nine.testrun.org"
|
||||
NINE_KEY = "xjMEaAVqNxYJKwYBBAHaRw8BAQdAnQ1KcTZYpcfbGyXkgPHJsCJQn/mn2a4F5SH7tccFNF/NHDxhcDNtNWNzNjNAbmluZS50ZXN0cnVuLm9yZz7CjQQQFggANQIZAQUCaAVqNwIbAwQLCQgHBhUICQoLAgMWAgEBJxYhBJCKqRpWzC6rZWWG76Wa27/U0TWBAAoJEKWa27/U0TWBKV8A/RoUFaB7YYc0zLkZWkJr9xTy5jN8T3VsGNJRi2IN1wQTAQDAsLwZkTf4pax2Hu/S0P11e+hsK+7TqF8/YP/toT5zAc44BGgFajcSCisGAQQBl1UBBQEBB0Dw5cbj6CDXHYKJDHvqfCPE1oDcO0194OYjXPf3foYTGAMBCAfCeAQYFggAIAUCaAVqNwIbDBYhBJCKqRpWzC6rZWWG76Wa27/U0TWBAAoJEKWa27/U0TWB2c8BAON7SIbWGpzhCoLP/pKsVycxdH3lc4bAAKwLP2X/cnFyAQCO43AeusNcRYetQJfvtmI9avQkEKWw54QBfFWIiabpDg=="
|
||||
|
||||
|
||||
@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",
|
||||
SYSTEMLI_ADDRESS,
|
||||
SYSTEMLI_KEY,
|
||||
),
|
||||
("adsokasd@ingrdsuf.org", ""),
|
||||
(NON_EXISTANT_ADDRESS, ""),
|
||||
],
|
||||
)
|
||||
def test_request_by_email(email, public_key):
|
||||
assert public_key == keyserver_bot.koo.request_from_koo(email)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("email", "public_key", "result"),
|
||||
[
|
||||
(SYSTEMLI_ADDRESS, SYSTEMLI_KEY, f"This key is already published for {SYSTEMLI_ADDRESS} :)"),
|
||||
(NON_EXISTANT_ADDRESS, "", "Sorry, this contact contains no PGP key."),
|
||||
(
|
||||
NON_EXISTANT_ADDRESS,
|
||||
SYSTEMLI_KEY,
|
||||
f"Thanks! {NON_EXISTANT_ADDRESS} should receive a confirmation link from keyserver@keys.openpgp.org, use it to publish the key.",
|
||||
),
|
||||
(
|
||||
NINE_ADDRESS,
|
||||
NINE_KEY,
|
||||
f"Thanks! {NINE_ADDRESS} should receive a confirmation link from keyserver@keys.openpgp.org, use it to publish the key.",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_upload_to_koo(email, public_key, result):
|
||||
assert result == keyserver_bot.koo.upload_to_koo(email, public_key)
|
||||
|
|
|
|||
Loading…
Reference in a new issue