gpg: Implement searching keys via keygrip.
authorWerner Koch <wk@gnupg.org>
Tue, 29 Jan 2019 18:52:08 +0000 (19:52 +0100)
committerWerner Koch <wk@gnupg.org>
Tue, 29 Jan 2019 19:10:11 +0000 (20:10 +0100)
* kbx/keybox-defs.h (struct _keybox_openpgp_key_info): Add field grip.
* kbx/keybox-openpgp.c (struct keyparm_s): New.
(keygrip_from_keyparm): New.
(parse_key): Compute keygrip.
* kbx/keybox-search.c (blob_openpgp_has_grip): New.
(has_keygrip): Call it.
--

This has been marked for too long as not yet working.  However, it is
a pretty useful feature and will come pretty handy when looking for
all keys matching one keygrip.

Can be optimized a lot by storing the keygrip in the meta data.  This
will be done along with the upgrade of KBX for v5 fingerprints.

Signed-off-by: Werner Koch <wk@gnupg.org>
doc/specify-user-id.texi
kbx/kbxutil.c
kbx/keybox-defs.h
kbx/keybox-openpgp.c
kbx/keybox-search.c

index b363c2a..64e354b 100644 (file)
@@ -135,7 +135,7 @@ RFC-2253 encoded DN of the issuer. See note above.
 @item By keygrip.
 This is indicated by an ampersand followed by the 40 hex digits of a
 keygrip.  @command{gpgsm} prints the keygrip when using the command
-@option{--dump-cert}.  It does not yet work for OpenPGP keys.
+@option{--dump-cert}.
 
 @cartouche
 @example
@@ -171,6 +171,3 @@ Using the RFC-2253 format of DNs has the drawback that it is not
 possible to map them back to the original encoding, however we don't
 have to do this because our key database stores this encoding as meta
 data.
-
-
-
index 2cfd070..35f92ab 100644 (file)
@@ -330,6 +330,18 @@ dump_fpr (const unsigned char *buffer, size_t len)
 
 
 static void
+dump_grip (const unsigned char *buffer, size_t len)
+{
+  int i;
+
+  for (i=0; i < len; i++, buffer++)
+    {
+      printf ("%02X", buffer[0]);
+    }
+}
+
+
+static void
 dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
 {
   printf ("pub %2d %02X%02X%02X%02X",
@@ -338,6 +350,9 @@ dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
           info->primary.keyid[6], info->primary.keyid[7] );
   dump_fpr (info->primary.fpr, info->primary.fprlen);
   putchar ('\n');
+  fputs ("grp             ", stdout);
+  dump_grip (info->primary.grip, 20);
+  putchar ('\n');
   if (info->nsubkeys)
     {
       struct _keybox_openpgp_key_info *k;
@@ -351,6 +366,9 @@ dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
                   k->keyid[6], k->keyid[7] );
           dump_fpr (k->fpr, k->fprlen);
           putchar ('\n');
+          fputs ("grp             ", stdout);
+          dump_grip (k->grip, 20);
+          putchar ('\n');
           k = k->next;
         }
       while (k);
index be2dd72..d2b79ba 100644 (file)
@@ -94,11 +94,12 @@ struct keybox_handle {
 };
 
 
-/* Openpgp helper structures. */
+/* OpenPGP helper structures.  */
 struct _keybox_openpgp_key_info
 {
   struct _keybox_openpgp_key_info *next;
   int algo;
+  unsigned char grip[20];
   unsigned char keyid[8];
   int fprlen;  /* Either 16 or 20 */
   unsigned char fpr[20];
index 0ba0b9a..6d6ed77 100644 (file)
 #include "../common/openpgpdefs.h"
 #include "../common/host2net.h"
 
+struct keyparm_s
+{
+  const char *mpi;
+  int len;   /* int to avoid a cast in gcry_sexp_build.  */
+};
+
+
 /* Assume a valid OpenPGP packet at the address pointed to by BUFBTR
    which has a maximum length as stored at BUFLEN.  Return the header
    information of that packet and advance the pointer stored at BUFPTR
@@ -165,6 +172,86 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
 }
 
 
+/* Take a list of key parameters KP for the OpenPGP ALGO and compute
+ * the keygrip which will be stored at GRIP.  GRIP needs to be a
+ * buffer of 20 bytes.  */
+static gpg_error_t
+keygrip_from_keyparm (int algo, struct keyparm_s *kp, unsigned char *grip)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_pkey = NULL;
+
+  switch (algo)
+    {
+    case PUBKEY_ALGO_DSA:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(dsa(p%b)(q%b)(g%b)(y%b)))",
+                             kp[0].len, kp[0].mpi,
+                             kp[1].len, kp[1].mpi,
+                             kp[2].len, kp[2].mpi,
+                             kp[3].len, kp[3].mpi);
+      break;
+
+    case PUBKEY_ALGO_ELGAMAL:
+    case PUBKEY_ALGO_ELGAMAL_E:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(elg(p%b)(g%b)(y%b)))",
+                             kp[0].len, kp[0].mpi,
+                             kp[1].len, kp[1].mpi,
+                             kp[2].len, kp[2].mpi);
+      break;
+
+    case PUBKEY_ALGO_RSA:
+    case PUBKEY_ALGO_RSA_S:
+    case PUBKEY_ALGO_RSA_E:
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(rsa(n%b)(e%b)))",
+                             kp[0].len, kp[0].mpi,
+                             kp[1].len, kp[1].mpi);
+      break;
+
+    case PUBKEY_ALGO_EDDSA:
+    case PUBKEY_ALGO_ECDSA:
+    case PUBKEY_ALGO_ECDH:
+      {
+        char *curve = openpgp_oidbuf_to_str (kp[0].mpi, kp[0].len);
+        if (!curve)
+          err = gpg_error_from_syserror ();
+        else
+          {
+            err = gcry_sexp_build
+              (&s_pkey, NULL,
+               (algo == PUBKEY_ALGO_EDDSA)?
+               "(public-key(ecc(curve%s)(flags eddsa)(q%b)))":
+               (algo == PUBKEY_ALGO_ECDH
+                && openpgp_oidbuf_is_cv25519 (kp[0].mpi, kp[0].len))?
+               "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))":
+               "(public-key(ecc(curve%s)(q%b)))",
+               curve, kp[1].len, kp[1].mpi);
+            xfree (curve);
+          }
+      }
+      break;
+
+    default:
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+      break;
+    }
+
+  if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
+    {
+      log_info ("kbx: error computing keygrip\n");
+      err = gpg_error (GPG_ERR_GENERAL);
+    }
+
+  gcry_sexp_release (s_pkey);
+
+  if (err)
+    memset (grip, 0, 20);
+  return err;
+}
+
+
 /* Parse a key packet and store the information in KI. */
 static gpg_error_t
 parse_key (const unsigned char *data, size_t datalen,
@@ -176,10 +263,10 @@ parse_key (const unsigned char *data, size_t datalen,
   size_t n;
   int npkey;
   unsigned char hashbuffer[768];
-  const unsigned char *mpi_n = NULL;
-  size_t mpi_n_len = 0, mpi_e_len = 0;
   gcry_md_hd_t md;
   int is_ecc = 0;
+  struct keyparm_s keyparm[OPENPGP_MAX_NPKEY];
+  unsigned char *helpmpibuf[OPENPGP_MAX_NPKEY] = { NULL };
 
   if (datalen < 5)
     return gpg_error (GPG_ERR_INV_PACKET);
@@ -245,6 +332,9 @@ parse_key (const unsigned char *data, size_t datalen,
           nbytes++; /* The size byte itself.  */
           if (datalen < nbytes)
             return gpg_error (GPG_ERR_INV_PACKET);
+
+          keyparm[i].mpi = data;
+          keyparm[i].len = nbytes;
         }
       else
         {
@@ -254,21 +344,40 @@ parse_key (const unsigned char *data, size_t datalen,
           nbytes = (nbits+7) / 8;
           if (datalen < nbytes)
             return gpg_error (GPG_ERR_INV_PACKET);
-          /* For use by v3 fingerprint calculation we need to know the RSA
-             modulus and exponent. */
-          if (i==0)
-            {
-              mpi_n = data;
-              mpi_n_len = nbytes;
-            }
-          else if (i==1)
-            mpi_e_len = nbytes;
+
+          keyparm[i].mpi = data;
+          keyparm[i].len = nbytes;
         }
 
       data += nbytes; datalen -= nbytes;
     }
   n = data - data_start;
 
+
+  /* Note: Starting here we need to jump to leave on error. */
+
+  /* Make sure the MPIs are unsigned.  */
+  for (i=0; i < npkey; i++)
+    {
+      if (!keyparm[i].len || (keyparm[i].mpi[0] & 0x80))
+        {
+          helpmpibuf[i] = xtrymalloc (1+keyparm[i].len);
+          if (!helpmpibuf[i])
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          helpmpibuf[i][0] = 0;
+          memcpy (helpmpibuf[i]+1, keyparm[i].mpi, keyparm[i].len);
+          keyparm[i].mpi = helpmpibuf[i];
+          keyparm[i].len++;
+        }
+    }
+
+  err = keygrip_from_keyparm (algorithm, keyparm, ki->grip);
+  if (err)
+    goto leave;
+
   if (version < 4)
     {
       /* We do not support any other algorithm than RSA in v3
@@ -279,20 +388,20 @@ parse_key (const unsigned char *data, size_t datalen,
       err = gcry_md_open (&md, GCRY_MD_MD5, 0);
       if (err)
         return err; /* Oops */
-      gcry_md_write (md, mpi_n, mpi_n_len);
-      gcry_md_write (md, mpi_n+mpi_n_len+2, mpi_e_len);
+      gcry_md_write (md, keyparm[0].mpi, keyparm[0].len);
+      gcry_md_write (md, keyparm[1].mpi, keyparm[1].len);
       memcpy (ki->fpr, gcry_md_read (md, 0), 16);
       gcry_md_close (md);
       ki->fprlen = 16;
 
-      if (mpi_n_len < 8)
+      if (keyparm[0].len < 8)
         {
           /* Moduli less than 64 bit are out of the specs scope.  Zero
              them out because this is what gpg does too. */
           memset (ki->keyid, 0, 8);
         }
       else
-        memcpy (ki->keyid, mpi_n + mpi_n_len - 8, 8);
+        memcpy (ki->keyid, keyparm[0].mpi + keyparm[0].len - 8, 8);
     }
   else
     {
@@ -327,7 +436,11 @@ parse_key (const unsigned char *data, size_t datalen,
       memcpy (ki->keyid, ki->fpr+12, 8);
     }
 
-  return 0;
+ leave:
+  for (i=0; i < npkey; i++)
+    xfree (helpmpibuf[i]);
+
+  return err;
 }
 
 
index 0a050ff..946ef52 100644 (file)
@@ -497,6 +497,58 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr,
 }
 
 
+/* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
+ * We don't have the keygrips as meta data, thus we need to parse the
+ * certificate. Fixme: We might want to return proper error codes
+ * instead of failing a search for invalid certificates etc.  */
+static int
+blob_openpgp_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
+{
+  int rc = 0;
+  const unsigned char *buffer;
+  size_t length;
+  size_t cert_off, cert_len;
+  struct _keybox_openpgp_info info;
+  struct _keybox_openpgp_key_info *k;
+
+  buffer = _keybox_get_blob_image (blob, &length);
+  if (length < 40)
+    return 0; /* Too short. */
+  cert_off = get32 (buffer+8);
+  cert_len = get32 (buffer+12);
+  if ((uint64_t)cert_off+(uint64_t)cert_len > (uint64_t)length)
+    return 0; /* Too short.  */
+
+  if (_keybox_parse_openpgp (buffer + cert_off, cert_len, NULL, &info))
+    return 0; /* Parse error.  */
+
+  if (!memcmp (info.primary.grip, grip, 20))
+    {
+      rc = 1;
+      goto leave;
+    }
+
+  if (info.nsubkeys)
+    {
+      k = &info.subkeys;
+      do
+        {
+          if (!memcmp (k->grip, grip, 20))
+            {
+              rc = 1;
+              goto leave;
+            }
+          k = k->next;
+        }
+      while (k);
+    }
+
+ leave:
+  _keybox_destroy_openpgp_info (&info);
+  return rc;
+}
+
+
 #ifdef KEYBOX_WITH_X509
 /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
    We don't have the keygrips as meta data, thus we need to parse the
@@ -606,12 +658,11 @@ has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr)
 static inline int
 has_keygrip (KEYBOXBLOB blob, const unsigned char *grip)
 {
+  if (blob_get_type (blob) == KEYBOX_BLOBTYPE_PGP)
+    return blob_openpgp_has_grip (blob, grip);
 #ifdef KEYBOX_WITH_X509
   if (blob_get_type (blob) == KEYBOX_BLOBTYPE_X509)
     return blob_x509_has_grip (blob, grip);
-#else
-  (void)blob;
-  (void)grip;
 #endif
   return 0;
 }