~sircmpwn/sr.ht-dev

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
2 2

[PATCH meta.sr.ht] well-known: support openpgp webkey discovery

Details
Message ID
<20230502215614.161643-1-git@olivier.pfad.fr>
DKIM signature
pass
Download raw message
Patch: +70 -0
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

[meta.sr.ht/patches] build failed

builds.sr.ht <builds@sr.ht>
Details
Message ID
<CSC4ONPSKF6W.1G24OLAX6DQIW@cirno2>
In-Reply-To
<20230502215614.161643-1-git@olivier.pfad.fr> (view parent)
DKIM signature
missing
Download raw message
meta.sr.ht/patches: FAILED in 3m52s

[well-known: support openpgp webkey discovery][0] from [oliverpool][1]

[0]: https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/40794
[1]: git@olivier.pfad.fr

✓ #983465 SUCCESS meta.sr.ht/patches/debian.yml    https://builds.sr.ht/~sircmpwn/job/983465
✓ #983463 SUCCESS meta.sr.ht/patches/alpine.yml    https://builds.sr.ht/~sircmpwn/job/983463
✗ #983464 FAILED  meta.sr.ht/patches/archlinux.yml https://builds.sr.ht/~sircmpwn/job/983464
Details
Message ID
<839e07e5-8648-4f3d-91ae-9c76f65147d4@app.fastmail.com>
In-Reply-To
<20230502215614.161643-1-git@olivier.pfad.fr> (view parent)
DKIM signature
pass
Download raw message
Superseded by https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/40799 (implementation in go)

On Tue, May 2, 2023, at 23:56, oliverpool wrote:
> 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
Reply to thread Export thread (mbox)