From b23c2c486949eb76c22d3dbd371e43875482ee41 Mon Sep 17 00:00:00 2001
From: Frank Sauerburger <frank@sauerburger.com>
Date: Sun, 21 Feb 2021 14:45:38 +0100
Subject: [PATCH] Display open pgp key data

---
 keys/settings.py                        |  1 -
 keys/urls.py                            |  1 +
 pgp/models.py                           | 43 ++++++++++++++++++++++++-
 pgp/templates/pgp/publickey_detail.html | 39 +++++++++++++++++++++-
 pgp/views.py                            |  7 ++++
 5 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/keys/settings.py b/keys/settings.py
index e2be4f2..5cbb30e 100644
--- a/keys/settings.py
+++ b/keys/settings.py
@@ -15,7 +15,6 @@ from pathlib import Path
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
 BASE_DIR = Path(__file__).resolve().parent.parent
 
-
 # Quick-start development settings - unsuitable for production
 # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
 
diff --git a/keys/urls.py b/keys/urls.py
index 7acaa72..fe4c047 100644
--- a/keys/urls.py
+++ b/keys/urls.py
@@ -20,5 +20,6 @@ urlpatterns = [
     path('pki/', include('owlca.urls')),
     path('pgp/', include('pgp.urls')),
     path('ssh/', include('ssh.urls')),
+    path('pks/', include('hkp.urls')),
     path('admin/', admin.site.urls),
 ]
diff --git a/pgp/models.py b/pgp/models.py
index 75671f3..c244617 100644
--- a/pgp/models.py
+++ b/pgp/models.py
@@ -31,6 +31,47 @@ class PublicKey(models.Model):
 
     def details(self):
         self._decode()
-        return self.decoded
 
+        def get_exp(created, expires_at):
+            if not expires_at:
+                return None
+            return created + expires_at
 
+        data = {
+            "userids": [{
+                "email": userid.email,
+                "name": userid.name,
+                "comment": userid.comment,
+                "signatures": [{
+                    "created": sig.created,
+                    "expires_at": get_exp(sig.created, sig.expires_at),
+                    "signer": sig.signer,
+                } for sig in userid._signatures],
+            } for userid in self.decoded.userids],
+            "fingerprint": self.decoded.fingerprint,
+            "id": re.sub("\s+", "", self.decoded.fingerprint[-8:]),
+            "created": self.decoded.created,
+            "expires_at": self.decoded.expires_at,
+            "signatures": [{
+                "created": sig.created,
+                "expires_at": get_exp(sig.created, sig.expires_at),
+                "signer": sig.signer,
+            } for sig in self.decoded._signatures],
+            "subkeys": [{
+                "fingerprint": subkey.fingerprint, 
+                "created": subkey.created,
+                "expires_at": subkey.expires_at,
+                "signatures": [{
+                    "created": sig.created,
+                    "expires_at": get_exp(subkey.created, sig.key_expiration),
+                    "signer": sig.signer,
+                }
+                for sig in subkey._signatures
+                if sig.type == pgpy.constants.SignatureType.Subkey_Binding],
+            } for id, subkey in self.decoded.subkeys.items()],
+        }
+        return data
+
+    def __str__(self):
+        keyid = f" (0x{self.keyid[-8:]})" if self.keyid else ""
+        return f"{self.email}{keyid}"
diff --git a/pgp/templates/pgp/publickey_detail.html b/pgp/templates/pgp/publickey_detail.html
index 05e0ac3..f5d42f1 100644
--- a/pgp/templates/pgp/publickey_detail.html
+++ b/pgp/templates/pgp/publickey_detail.html
@@ -2,5 +2,42 @@
 
 {% block content %}
 <h2>Public key for: {{ publickey.email }}</h2>
-<pre>{{ publickey.armor }}</pre>
+<pre>
+gpg2 --keyserver hkp://{{ request.get_host }} --recv-key 0x{{ publickey.details.id }}
+</pre>
+<p>
+<span style="font-family: monospace; font-weight: bold">{{ publickey.details.fingerprint }}
+{% for sig in publickey.details.signatures %}
+    <span style="font-family: monospace">{{ sig.signer }}</span>
+    ({{ sig.created|date:"Y-m-d" }} &ndash; {{ sig.expires_at|date:"Y-m-d"|default:"never" }})<br />
+{% endfor %}
+</p>
+<h3>User Ids</h3>
+<ul>
+{% for uid in publickey.details.userids %}
+    <li>
+    {{ uid.name }} 
+    {% if uid.comment %}({{ uid.comment }}) {% endif %}
+    &lt;<a href="mailto:{{ uid.email }}">{{ uid.email }}</a>&gt;<br>
+    {% for sig in uid.signatures %}
+        <span style="font-family: monospace">{{ sig.signer }}</span>
+        ({{ sig.created|date:"Y-m-d" }} &ndash; {{ sig.expires_at|date:"Y-m-d"|default:"never" }})<br />
+    {% endfor %}
+    </li>
+{% endfor %}
+</ul>
+<h3>Subkeys</h3>
+<ul>
+{% for key in publickey.details.subkeys %}
+    <li>
+      <span style="font-family: monospace; font-weight: bold">{{ key.fingerprint }}</span><br />
+      {% for sig in key.signatures %}
+        <span style="font-family: monospace">{{ sig.signer }}</span>
+        ({{ sig.created|date:"Y-m-d" }} &ndash; {{ sig.expires_at|date:"Y-m-d"|default:"never" }})<br />
+      {% endfor %}
+    </li>
+{% endfor %}
+</ul>
+<h3>Key data</h3>
+<textarea cols="80" rows="20" readonly>{{ publickey.armor }}</textarea>
 {% endblock %}
diff --git a/pgp/views.py b/pgp/views.py
index 88cb030..bf6513e 100644
--- a/pgp/views.py
+++ b/pgp/views.py
@@ -8,6 +8,13 @@ from guardian.utils import get_anonymous_user
 from . import models
 from . import forms
 
+from django import template
+
+register = template.Library()
+@register.simple_tag
+def get_private_attribute(model_instance, attrib_name):
+    return getattr(model_instance, attrib_name, '')
+
 class PublicKeyListView(PermissionListMixin, ListView):
     model = models.PublicKey
     permission_required = ['view_publickey']
-- 
GitLab