as documented on https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service#name-web-key-directory
---
This has been proposed on sr.ht-discuss https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3Cdd965bea-a130-4675-8cdf-742286607079%40app.fastmail.com%3E
> I think that would make sense. File ticket and/or send patch?
It would conflict with https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/40325 (removal of pgpy dependency).
To test locally, a public-key must be added with some identities matching the configured global-domain.
The public key will then be accessible under /.well-known/openpgpkey/hu/<hash>
Example hashes:
- git: e5a4bxki1ktx1jncwco5nkcofedmkxod
- admin: 4y36rkzdjnzmk3oxaekyi5biowgr5kcz
This is my first contribution to sr.ht and I appreciate any feedback!
metasrht/app.py | 2 +metasrht/blueprints/openpgp_webkey.py | 68 +++++++++++++++++++++++++++
2 files changed, 70 insertions(+)
create mode 100644 metasrht/blueprints/openpgp_webkey.py
diff --git a/metasrht/app.py b/metasrht/app.py
index 4f019b7..707e826 100644
--- a/metasrht/app.py+++ b/metasrht/app.py
@@ -25,6 +25,7 @@ class MetaApp(SrhtFlask):
from metasrht.blueprints.profile import profile
from metasrht.blueprints.security import security
from metasrht.blueprints.users import users
+ from metasrht.blueprints.openpgp_webkey import openpgp_webkey from srht.graphql import gql_blueprint
self.register_blueprint(auth)
@@ -36,6 +37,7 @@ class MetaApp(SrhtFlask):
self.register_blueprint(profile)
self.register_blueprint(security)
self.register_blueprint(users)
+ self.register_blueprint(openpgp_webkey) register_api(self)
self.register_blueprint(gql_blueprint)
diff --git a/metasrht/blueprints/openpgp_webkey.py b/metasrht/blueprints/openpgp_webkey.py
new file mode 100644
index 0000000..250d78a
--- /dev/null+++ b/metasrht/blueprints/openpgp_webkey.py
@@ -0,0 +1,68 @@
+from flask import Blueprint, Response+import functools+from hashlib import sha1+import itertools+import pgpy+from srht.config import cfg, get_global_domain++openpgp_webkey = Blueprint("openpgp_webkey", __name__)++"""+Implement https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service+to ease discovery of the public key by clients:++gpg --locate-keys <username>@<global-domain>+"""+++# Function: encode_zbase32+# Source: https://gist.githubusercontent.com/tochev/99f19d9ce062f1c7e203/raw/0077ec38adc350e0fd1207e6a525de482b40df7e/zbase32.py+# Copyright: Tocho Tochev <tocho AT tochev DOT net>+# Licence: MIT+# See http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt+def encode_zbase32(bs):+ """+ Encode bytes bs using zbase32 encoding.+ Returns: bytearray++ >>> encode_zbase32(b'\\xd4z\\x04') == b'4t7ye'+ True+ """+ ALPTHABET = b"ybndrfg8ejkmcpqxot1uwisza345h769"+ result = bytearray()+ for word in itertools.zip_longest(*([iter(bs)] * 5)):+ padding_count = word.count(None)+ n = functools.reduce(lambda x, y: (x << 8) + (y or 0), word, 0)+ for i in range(0, (40 - 8 * padding_count), 5):+ result.append(ALPTHABET[(n >> (35 - i)) & 0x1F])+ return result+++gdomain = get_global_domain("meta.sr.ht")+site_key = cfg("mail", "pgp-pubkey", None)+if site_key:+ site_key, _ = pgpy.PGPKey.from_file(site_key)+ site_key_bytes = site_key.__bytes__()++ def openpgp_webkey_binary_public_key():+ return Response(site_key_bytes, mimetype="application/octet-stream")++ for uid in site_key.userids:+ if "@" not in uid.email:+ continue+ local, domain = uid.email.split("@", 1)+ if domain != gdomain:+ continue+ zbase32 = encode_zbase32(sha1(local.lower().encode("utf-8")).digest()).decode(+ "utf-8"+ )+ openpgp_webkey.add_url_rule(+ "/.well-known/openpgpkey/hu/" + zbase32,+ view_func=openpgp_webkey_binary_public_key,+ )++ @openpgp_webkey.route("/.well-known/openpgpkey/hu/policy")+ def openpgp_webkey_policy():+ return Response(+ "# Policy flags for the OpenPGP Web Key Directory", mimetype="text/plain"+ )
--
2.40.1