libotutil: Add helper for GPG WKD update URLs
authorDan Nicholson <nicholson@endlessm.com>
Tue, 27 Aug 2019 16:28:10 +0000 (10:28 -0600)
committerDan Nicholson <dbn@endlessos.org>
Thu, 15 Jul 2021 21:50:04 +0000 (15:50 -0600)
Calculate the advanced and direct update URLs for the key discovery
portion[1] of the OpenPGP Web Key Directory specification, and include
the URLs in the key listing in ostree_repo_remote_get_gpg_keys(). These
URLs can be used to locate updated GPG keys for the remote.

1. https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service#section-3.1

src/libotutil/ot-gpg-utils.c
src/libotutil/ot-gpg-utils.h

index 743d941e375d4213ac811f243478555ab77ce9ce..4dbefdbd464417a19c1925478561ef061c2a5f34 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <gio/gunixoutputstream.h>
 #include "libglnx.h"
+#include "zbase32.h"
 
 /* Like glnx_throw_errno_prefix, but takes @gpg_error */
 gboolean
@@ -538,3 +539,77 @@ ot_gpgme_kill_agent (const char *homedir)
       return;
     }
 }
+
+/* Takes the SHA1 checksum of the local component of an email address and
+ * returns the zbase32 encoding.
+ */
+static char *
+encode_wkd_local (const char *local)
+{
+  g_return_val_if_fail (local != NULL, NULL);
+
+  guint8 digest[20] = { 0 };
+  gsize len = sizeof (digest);
+  g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA1);
+  g_checksum_update (checksum, (const guchar *)local, -1);
+  g_checksum_get_digest (checksum, digest, &len);
+
+  char *encoded = zbase32_encode (digest, len);
+
+  /* If the returned string is NULL, then there must have been a memory
+   * allocation problem. Just exit immediately like g_malloc.
+   */
+  if (encoded == NULL)
+    g_error ("%s: %s", G_STRLOC, g_strerror (errno));
+
+  return encoded;
+}
+
+/* Implementation of OpenPGP Web Key Directory URLs as defined in
+ * https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service
+ */
+gboolean
+ot_gpg_wkd_urls (const char  *email,
+                 char       **out_advanced_url,
+                 char       **out_direct_url,
+                 GError     **error)
+{
+  g_return_val_if_fail (email != NULL, FALSE);
+
+  g_auto(GStrv) email_parts = g_strsplit (email, "@", -1);
+  if (g_strv_length (email_parts) != 2)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                   "Invalid email address \"%s\"", email);
+      return FALSE;
+    }
+
+  g_autofree char *local_lowered = g_ascii_strdown (email_parts[0], -1);
+  g_autofree char *domain_lowered = g_ascii_strdown (email_parts[1], -1);
+  g_autofree char *local_encoded = encode_wkd_local (local_lowered);
+  g_autofree char *local_escaped = g_uri_escape_string (email_parts[0], NULL, FALSE);
+
+  g_autofree char *advanced_url = g_strdup_printf ("https://openpgpkey.%s"
+                                                   "/.well-known/openpgpkey"
+                                                   "/%s/hu/%s?l=%s",
+                                                   email_parts[1],
+                                                   domain_lowered,
+                                                   local_encoded,
+                                                   local_escaped);
+  g_debug ("GPG UID \"%s\" advanced WKD URL: %s", email, advanced_url);
+
+  g_autofree char *direct_url = g_strdup_printf ("https://%s"
+                                                 "/.well-known/openpgpkey"
+                                                 "/hu/%s?l=%s",
+                                                 email_parts[1],
+                                                 local_encoded,
+                                                 local_escaped);
+  g_debug ("GPG UID \"%s\" direct WKD URL: %s", email, direct_url);
+
+  if (out_advanced_url != NULL)
+    *out_advanced_url = g_steal_pointer (&advanced_url);
+  if (out_direct_url != NULL)
+    *out_direct_url = g_steal_pointer (&direct_url);
+
+  return TRUE;
+}
index e8a240b5977637ab79951bd1c6b88704222914cd..b559b69573b7d71f99a0921f8802f32775bcb7e8 100644 (file)
@@ -48,4 +48,9 @@ gpgme_ctx_t ot_gpgme_new_ctx (const char *homedir,
 
 void ot_gpgme_kill_agent (const char *homedir);
 
+gboolean ot_gpg_wkd_urls (const char  *email,
+                          char       **out_advanced_url,
+                          char       **out_direct_url,
+                          GError     **error);
+
 G_END_DECLS