Imported Upstream version 0.6.27
[platform/upstream/libsolv.git] / ext / repo_pubkey.c
index 7b196cb..eb83839 100644 (file)
@@ -14,8 +14,6 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <limits.h>
-#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -23,6 +21,7 @@
 #include <assert.h>
 #include <stdint.h>
 #include <errno.h>
+#include <dirent.h>
 
 #include <rpm/rpmio.h>
 #include <rpm/rpmpgp.h>
 #include "queue.h"
 #include "chksum.h"
 #include "repo_rpmdb.h"
+#include "repo_pubkey.h"
 #ifdef ENABLE_PGPVRFY
 #include "solv_pgpvrfy.h"
 #endif
 
-static void 
+static void
 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
 {
   if (str[solv_validutf8(str)])
-    {    
+    {
       char *ustr = solv_latin1toutf8(str);     /* not utf8, assume latin1 */
       repodata_set_str(repodata, handle, tag, ustr);
       solv_free(ustr);
-    }    
-  else 
+    }
+  else
     repodata_set_str(repodata, handle, tag, str);
 }
 
@@ -99,7 +99,7 @@ r64dec1(char *p, unsigned int *vp, int *eofp)
 static unsigned int
 crc24(unsigned char *p, int len)
 {
-  unsigned int crc = 0xb704ceL;
+  unsigned int crc = 0xb704ce;
   int i;
 
   while (len--)
@@ -107,21 +107,25 @@ crc24(unsigned char *p, int len)
       crc ^= (*p++) << 16;
       for (i = 0; i < 8; i++)
         if ((crc <<= 1) & 0x1000000)
-         crc ^= 0x1864cfbL;
+         crc ^= 0x1864cfb;
     }
-  return crc & 0xffffffL;
+  return crc & 0xffffff;
 }
 
-static unsigned char *
-unarmor(char *pubkey, int *pktlp)
+static int
+unarmor(char *pubkey, unsigned char **pktp, int *pktlp, const char *startstr, const char *endstr)
 {
-  char *p;
+  char *p, *pubkeystart = pubkey;
   int l, eof;
   unsigned char *buf, *bp;
   unsigned int v;
 
+  *pktp = 0;
   *pktlp = 0;
-  while (strncmp(pubkey, "-----BEGIN PGP PUBLIC KEY BLOCK-----", 36) != 0)
+  if (!pubkey)
+    return 0;
+  l = strlen(startstr);
+  while (strncmp(pubkey, startstr, l) != 0)
     {
       pubkey = strchr(pubkey, '\n');
       if (!pubkey)
@@ -176,17 +180,62 @@ unarmor(char *pubkey, int *pktlp)
     }
   while (*pubkey == ' ' || *pubkey == '\t' || *pubkey == '\n' || *pubkey == '\r')
     pubkey++;
-  if (strncmp(pubkey, "-----END PGP PUBLIC KEY BLOCK-----", 34) != 0)
+  if (strncmp(pubkey, endstr, strlen(endstr)) != 0)
     {
       solv_free(buf);
       return 0;
     }
+  p = strchr(pubkey, '\n');
+  if (!p)
+    p = pubkey + strlen(pubkey);
+  *pktp = buf;
   *pktlp = bp - buf;
-  return buf;
+  return (p ? p + 1 : pubkey + strlen(pubkey)) - pubkeystart;
 }
 
-struct gpgsig {
+#define ARMOR_NLAFTER  16
+
+static char *
+armor(unsigned char *pkt, int pktl, const char *startstr, const char *endstr, const char *version)
+{
+  static const char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  char *str = solv_malloc(strlen(startstr) + strlen(endstr) + strlen(version) + (pktl / 3) * 4 + (pktl / (ARMOR_NLAFTER * 3)) + 30);
+  char *p = str;
+  int a, b, c, i;
+  unsigned int v;
+
+  v = crc24(pkt, pktl);
+  sprintf(p, "%s\nVersion: %s\n\n", startstr, version);
+  p += strlen(p);
+  for (i = -1; pktl > 0; pktl -= 3)
+    {
+      if (++i == ARMOR_NLAFTER)
+       {
+         i = 0;
+         *p++ = '\n';
+       }
+      a = *pkt++;
+      b = pktl > 1 ? *pkt++ : 0;
+      c = pktl > 2 ? *pkt++ : 0;
+      *p++ = bintoasc[a >> 2];
+      *p++ = bintoasc[(a & 3) << 4 | b >> 4];
+      *p++ = pktl > 1 ? bintoasc[(b & 15) << 2 | c >> 6] : '=';
+      *p++ = pktl > 2 ? bintoasc[c & 63] : '=';
+    }
+  *p++ = '\n';
+  *p++ = '=';
+  *p++ = bintoasc[v >> 18 & 0x3f];
+  *p++ = bintoasc[v >> 12 & 0x3f];
+  *p++ = bintoasc[v >>  6 & 0x3f];
+  *p++ = bintoasc[v       & 0x3f];
+  sprintf(p, "\n%s\n", endstr);
+  return str;
+}
+
+/* internal representation of a signature */
+struct pgpsig {
   int type;
+  Id hashalgo;
   unsigned char issuer[8];
   int haveissuer;
   unsigned int created;
@@ -194,73 +243,152 @@ struct gpgsig {
   unsigned int keyexpires;
   unsigned char *sigdata;
   int sigdatal;
+  int mpioff;
 };
 
+static Id
+pgphashalgo2type(int algo)
+{
+  if (algo == 1)
+    return REPOKEY_TYPE_MD5;
+  if (algo == 2)
+    return REPOKEY_TYPE_SHA1;
+  if (algo == 8)
+    return REPOKEY_TYPE_SHA256;
+  if (algo == 9)
+    return REPOKEY_TYPE_SHA384;
+  if (algo == 10)
+    return REPOKEY_TYPE_SHA512;
+  if (algo == 11)
+    return REPOKEY_TYPE_SHA224;
+  return 0;
+}
+
+/* hash the pubkey/userid data for self-sig verification
+ * hash the final trailer
+ * create a "sigdata" block suitable for a call to solv_pgpverify */
 static void
-parsesigpacket(struct gpgsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl)
+pgpsig_makesigdata(struct pgpsig *sig, unsigned char *p, int l, unsigned char *pubkey, int pubkeyl, unsigned char *userid, int useridl, Chksum *h)
+{
+  int type = sig->type;
+  unsigned char b[6];
+  const unsigned char *cs;
+  int csl;
+
+  if (!h || sig->mpioff < 2 || l <= sig->mpioff)
+    return;
+  if ((type >= 0x10 && type <= 0x13) || type == 0x1f || type == 0x18 || type == 0x20 || type == 0x28)
+    {
+      b[0] = 0x99;
+      b[1] = pubkeyl >> 8;
+      b[2] = pubkeyl;
+      solv_chksum_add(h, b, 3);
+      solv_chksum_add(h, pubkey, pubkeyl);
+    }
+  if ((type >= 0x10 && type <= 0x13))
+    {
+      if (p[0] != 3)
+       {
+         b[0] = 0xb4;
+         b[1] = useridl >> 24;
+         b[2] = useridl >> 16;
+         b[3] = useridl >> 8;
+         b[4] = useridl;
+         solv_chksum_add(h, b, 5);
+       }
+      solv_chksum_add(h, userid, useridl);
+    }
+  /* add trailer */
+  if (p[0] == 3)
+    solv_chksum_add(h, p + 2, 5);
+  else
+    {
+      int hl = 6 + (p[4] << 8 | p[5]);
+      solv_chksum_add(h, p, hl);
+      b[0] = 4;
+      b[1] = 0xff;
+      b[2] = hl >> 24;
+      b[3] = hl >> 16;
+      b[4] = hl >> 8;
+      b[5] = hl;
+      solv_chksum_add(h, b, 6);
+    }
+  cs = solv_chksum_get(h, &csl);
+  if (cs[0] == p[sig->mpioff - 2] && cs[1] == p[sig->mpioff - 1])
+    {
+      int ml = l - sig->mpioff;
+      sig->sigdata = solv_malloc(2 + csl + ml);
+      sig->sigdatal = 2 + csl + ml;
+      sig->sigdata[0] = p[0] == 3 ? p[15] : p[2];
+      sig->sigdata[1] = p[0] == 3 ? p[16] : p[3];
+      memcpy(sig->sigdata + 2, cs, csl);
+      memcpy(sig->sigdata + 2 + csl, p + sig->mpioff, ml);
+    }
+}
+
+/* parse the header of a subpacket contained in a signature packet
+ * returns: length of the packet header, 0 if there was an error
+ * *pktlp is set to the packet length, the tag is the first byte.
+ */
+static inline int
+parsesubpkglength(unsigned char *q, int ql, int *pktlp)
 {
+  int x, sl, hl;
+  /* decode sub-packet length, ql must be > 0 */
+  x = *q++;
+  if (x < 192)
+    {
+      sl = x;
+      hl = 1;
+    }
+  else if (x == 255)
+    {
+      if (ql < 5 || q[0] != 0)
+       return 0;
+      sl = q[1] << 16 | q[2] << 8 | q[3];
+      hl = 5;
+    }
+  else
+    {
+      if (ql < 2)
+       return 0;
+      sl = ((x - 192) << 8) + q[0] + 192;
+      hl = 2;
+    }
+  if (!sl || ql < sl + hl)     /* sub pkg tag is included in length, i.e. sl must not be zero */
+    return 0;
+  *pktlp = sl;
+  return hl;
+}
+
+/* parse a signature packet, initializing the pgpsig struct */
+static void
+pgpsig_init(struct pgpsig *sig, unsigned char *p, int l)
+{
+  memset(sig, 0, sizeof(*sig));
   sig->type = -1;
   if (p[0] == 3)
     {
-      Id htype = 0;
       /* printf("V3 signature packet\n"); */
       if (l <= 19 || p[1] != 5)
        return;
-      if (p[2] != 0x10 && p[2] != 0x11 && p[2] != 0x12 && p[2] != 0x13 && p[2] != 0x1f)
-       return;
       sig->type = p[2];
       sig->haveissuer = 1;
       memcpy(sig->issuer, p + 7, 8);
       sig->created = p[3] << 24 | p[4] << 16 | p[5] << 8 | p[6];
-      if (p[16] == 1)
-       htype = REPOKEY_TYPE_MD5;
-      else if (p[16] == 2)
-       htype = REPOKEY_TYPE_SHA1;
-      else if (p[16] == 8)
-       htype = REPOKEY_TYPE_SHA256;
-      if (htype && pubkey)
-       {
-         void *h = solv_chksum_create(htype);
-         const unsigned char *cs;
-         unsigned char b[3];
-         int csl;
-
-         if ((p[2] >= 0x10 && p[2] <= 0x13) || p[2] == 0x1f || p[2] == 0x18 || p[2] == 0x20 || p[2] == 0x28)
-           {
-             b[0] = 0x99;
-             b[1] = pubkeyl >> 8;
-             b[2] = pubkeyl;
-             solv_chksum_add(h, b, 3);
-             solv_chksum_add(h, pubkey, pubkeyl);
-           }
-         if (p[2] >= 0x10 && p[2] <= 0x13)
-           solv_chksum_add(h, userid, useridl);
-         solv_chksum_add(h, p + 2, 5);
-         cs = solv_chksum_get(h, &csl);
-         if (cs[0] == p[17] && cs[1] == p[18])
-           {
-             sig->sigdata = solv_malloc(2 + csl + l - 19);
-             sig->sigdatal = 2 + csl + l - 19;
-             sig->sigdata[0] = p[15];
-             sig->sigdata[1] = p[16];
-             memcpy(sig->sigdata + 2, cs, csl);
-             memcpy(sig->sigdata + 2 + csl, p + 19, l - 19);
-           }
-         solv_chksum_free(h, 0);
-       }
+      sig->hashalgo = p[16];
+      sig->mpioff = 19;
     }
   else if (p[0] == 4)
     {
       int j, ql, x;
       unsigned char *q;
-      Id htype = 0;
 
       /* printf("V4 signature packet\n"); */
       if (l < 6)
        return;
-      if (p[1] != 0x10 && p[1] != 0x11 && p[1] != 0x12 && p[1] != 0x13 && p[1] != 0x1f)
-       return;
       sig->type = p[1];
+      sig->hashalgo = p[3];
       q = p + 4;
       sig->keyexpires = -1;
       for (j = 0; q && j < 2; j++)
@@ -277,41 +405,18 @@ parsesigpacket(struct gpgsig *sig, unsigned char *p, int l, unsigned char *pubke
              q = 0;
              break;
            }
-         while (ql)
+         while (ql > 0)
            {
-             int sl;
-             /* decode sub-packet length */
-             x = *q++;
-             ql--;
-             if (x < 192)
-               sl = x;
-             else if (x == 255)
-               {
-                 if (ql < 4 || q[0] != 0)
-                   {
-                     q = 0;
-                     break;
-                   }
-                 sl = q[1] << 16 | q[2] << 8 | q[3];
-                 q += 4;
-                 ql -= 4;
-               }
-             else
-               {
-                 if (ql < 1)
-                   {
-                     q = 0;
-                     break;
-                   }
-                 sl = ((x - 192) << 8) + *q++ + 192;
-                 ql--;
-               }
-             if (ql < sl)
+             int sl, hl;
+             hl = parsesubpkglength(q, ql, &sl);
+             if (!hl)
                {
                  q = 0;
                  break;
                }
-             x = q[0] & 127;
+             q += hl;
+             ql -= hl;
+             x = q[0] & 127;   /* strip critical bit */
              /* printf("%d SIGSUB %d %d\n", j, x, sl); */
              if (x == 16 && sl == 9 && !sig->haveissuer)
                {
@@ -328,139 +433,159 @@ parsesigpacket(struct gpgsig *sig, unsigned char *p, int l, unsigned char *pubke
              ql -= sl;
            }
        }
-      if (p[3] == 1)
-       htype = REPOKEY_TYPE_MD5;
-      else if (p[3] == 2)
-       htype = REPOKEY_TYPE_SHA1;
-      else if (p[3] == 8)
-       htype = REPOKEY_TYPE_SHA256;
-      if (sig->haveissuer && htype && pubkey && q && q - p + 2 < l)
-       {
-         void *h = solv_chksum_create(htype);
-         unsigned char b[6];
-         const unsigned char *cs;
-         unsigned int hl;
-         int csl, ml = l - (q - p + 2);
+      if (q && q - p + 2 < l)
+       sig->mpioff = q - p + 2;
+    }
+}
 
-         if ((p[1] >= 0x10 && p[1] <= 0x13) || p[1] == 0x1f || p[1] == 0x18 || p[1] == 0x20 || p[1] == 0x28)
-           {
-             b[0] = 0x99;
-             b[1] = pubkeyl >> 8;
-             b[2] = pubkeyl;
-             solv_chksum_add(h, b, 3);
-             solv_chksum_add(h, pubkey, pubkeyl);
-           }
-         if (p[1] >= 0x10 && p[1] <= 0x13)
-           {
-             b[0] = 0xb4;
-             b[1] = useridl >> 24;
-             b[2] = useridl >> 16;
-             b[3] = useridl >> 8;
-             b[4] = useridl;
-             solv_chksum_add(h, b, 5);
-             solv_chksum_add(h, userid, useridl);
-           }
-         hl = 6 + (p[4] << 8 | p[5]);
-         solv_chksum_add(h, p, hl);
-         b[0] = 4;
-         b[1] = 0xff;
-         b[2] = hl >> 24;
-         b[3] = hl >> 16;
-         b[4] = hl >> 8;
-         b[5] = hl;
-         solv_chksum_add(h, b, 6);
-         cs = solv_chksum_get(h, &csl);
-         if (cs[0] == q[0] && cs[1] == q[1])
-           {
-             sig->sigdata = solv_malloc(2 + csl + ml);
-             sig->sigdatal = 2 + csl + ml;
-             sig->sigdata[0] = p[2];
-             sig->sigdata[1] = p[3];
-             memcpy(sig->sigdata + 2, cs, csl);
-             memcpy(sig->sigdata + 2 + csl, q + 2, ml);
-           }
-         solv_chksum_free(h, 0);
+/* parse a pgp packet header
+ * returns: length of the packet header, 0 if there was an error
+ * *tagp and *pktlp is set to the packet tag and the packet length
+ */
+static int
+parsepkgheader(unsigned char *p, int pl, int *tagp, int *pktlp)
+{
+  unsigned char *op = p;
+  int x, l;
+
+  if (!pl)
+    return 0;
+  x = *p++;
+  pl--;
+  if (!(x & 128) || pl <= 0)
+    return 0;
+  if ((x & 64) == 0)
+    {
+      *tagp = (x & 0x3c) >> 2;         /* old format */
+      x = 1 << (x & 3);
+      if (x > 4 || pl < x || (x == 4 && p[0]))
+       return 0;
+      pl -= x;
+      for (l = 0; x--;)
+       l = l << 8 | *p++;
+    }
+  else
+    {
+      *tagp = (x & 0x3f);              /* new format */
+      x = *p++;
+      pl--;
+      if (x < 192)
+       l = x;
+      else if (x >= 192 && x < 224)
+       {
+         if (pl <= 0)
+           return 0;
+         l = ((x - 192) << 8) + *p++ + 192;
+         pl--;
+       }
+      else if (x == 255)
+       {
+         if (pl <= 4 || p[0] != 0)     /* sanity: p[0] must be zero */
+           return 0;
+         l = p[1] << 16 | p[2] << 8 | p[3];
+         p += 4;
+         pl -= 4;
        }
+      else
+       return 0;
     }
+  if (l > pl)
+    return 0;
+  *pktlp = l;
+  return p - op;
 }
 
-static void
-parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
+/* parse the first pubkey (possible creating new packages for the subkeys)
+ * returns the number of parsed bytes.
+ * if flags contains ADD_WITH_SUBKEYS, all subkeys will be added as new
+ * solvables as well */
+static int
+parsepubkey(Solvable *s, Repodata *data, unsigned char *p, int pl, int flags)
 {
-  int x, tag, l;
+  Repo *repo = s->repo;
+  unsigned char *pstart = p;
+  int tag, l;
   unsigned char keyid[8];
+  char subkeyofstr[17];
   unsigned int kcr = 0, maxex = 0, maxsigcr = 0;
   unsigned char *pubkey = 0;
   int pubkeyl = 0;
+  int insubkey = 0;
   unsigned char *userid = 0;
   int useridl = 0;
   unsigned char *pubdata = 0;
   int pubdatal = 0;
 
-  for (; pl; p += l, pl -= l)
+  *subkeyofstr = 0;
+  for (; ; p += l, pl -= l)
     {
-      x = *p++;
-      pl--;
-      if (!(x & 128) || pl <= 0)
-       return;
-      if ((x & 64) == 0)
+      int hl = parsepkgheader(p, pl, &tag, &l);
+      if (!hl || (pubkey && (tag == 6 || tag == 14)))
        {
-         /* old format */
-         tag = (x & 0x3c) >> 2;
-         x &= 3;
-         if (x == 3)
-           return;
-         l = 1 << x;
-         if (pl < l)
-           return;
-         x = 0;
-         while (l--)
+         /* finish old key */
+         if (kcr)
+           repodata_set_num(data, s - repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
+         if (maxex && maxex != -1)
+           repodata_set_num(data, s - repo->pool->solvables, PUBKEY_EXPIRES, maxex);
+         s->name = pool_str2id(s->repo->pool, insubkey ? "gpg-subkey" : "gpg-pubkey", 1);
+         s->evr = 1;
+         s->arch = 1;
+         if (userid && useridl)
            {
-             x = x << 8 | *p++;
-             pl--;
+             char *useridstr = solv_malloc(useridl + 1);
+             memcpy(useridstr, userid, useridl);
+             useridstr[useridl] = 0;
+             setutf8string(data, s - repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
+             free(useridstr);
            }
-         l = x;
-       }
-      else
-       {
-         tag = (x & 0x3f);
-         x = *p++;
-         pl--;
-         if (x < 192)
-           l = x;
-         else if (x >= 192 && x < 224)
+         if (pubdata)
            {
-             if (pl <= 0)
-               return;
-             l = ((x - 192) << 8) + *p++ + 192;
-             pl--;
+             char keyidstr[17];
+             char evr[8 + 1 + 8 + 1];
+             solv_bin2hex(keyid, 8, keyidstr);
+             repodata_set_str(data, s - repo->pool->solvables, PUBKEY_KEYID, keyidstr);
+             /* build rpm-style evr */
+             strcpy(evr, keyidstr + 8);
+             sprintf(evr + 8, "-%08x", maxsigcr);
+             s->evr = pool_str2id(repo->pool, evr, 1);
            }
-         else if (x == 255)
+         if (insubkey && *subkeyofstr)
+           repodata_set_str(data, s - repo->pool->solvables, PUBKEY_SUBKEYOF, subkeyofstr);
+         if (pubdata)          /* set data blob */
+           repodata_set_binary(data, s - repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
+         if (!pl)
+           break;
+         if (!hl)
            {
-             /* sanity: p[0] must be zero */
-             if (pl <= 4 || p[0] != 0)
-               return;
-             l = p[1] << 16 | p[2] << 8 | p[3];
-             p += 4;
-             pl -= 4;
+             p = 0;    /* parse error */
+             break;
            }
-         else
-           return;
+         if (tag == 6 || (tag == 14 && !(flags & ADD_WITH_SUBKEYS)))
+           break;
+         if (tag == 14 && pubdata && !insubkey)
+           solv_bin2hex(keyid, 8, subkeyofstr);
+         /* create new solvable for subkey */
+         s = pool_id2solvable(repo->pool, repo_add_solvable(repo));
        }
-      if (pl < l)
-       return;
-      if (tag == 6)
+      p += hl;
+      pl -= hl;
+      if (!pubkey && tag != 6)
+       continue;
+      if (tag == 6 || (tag == 14 && (flags & ADD_WITH_SUBKEYS) != 0))          /* Public-Key Packet */
        {
-         if (pubkey)
-           break;      /* one key at a time, please */
-         pubkey = solv_malloc(l);
-         if (l)
-           memcpy(pubkey, p, l);
-         pubkeyl = l;
+         if (tag == 6)
+           {
+             pubkey = solv_memdup(p, l);
+             pubkeyl = l;
+           }
+         else
+           insubkey = 1;
+         pubdata = 0;
+         pubdatal = 0;
          if (p[0] == 3 && l >= 10)
            {
              unsigned int ex;
-             void *h;
+             Chksum *h;
              maxsigcr = kcr = p[1] << 24 | p[2] << 16 | p[3] << 8 | p[4];
              ex = 0;
              if (p[5] || p[6])
@@ -500,7 +625,7 @@ parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
            }
          else if (p[0] == 4 && l >= 6)
            {
-             void *h;
+             Chksum *h;
              unsigned char hdr[3];
              unsigned char fp[20];
              char fpx[40 + 1];
@@ -521,17 +646,23 @@ parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
              pubdatal = l - 5;
            }
        }
-      if (tag == 2)
+      if (tag == 2)            /* Signature Packet */
        {
-         struct gpgsig sig;
+         struct pgpsig sig;
+         Id htype;
          if (!pubdata)
            continue;
-         memset(&sig, 0, sizeof(sig));
-         parsesigpacket(&sig, p, l, pubkey, pubkeyl, userid, useridl);
+         pgpsig_init(&sig, p, l);
          if (!sig.haveissuer || !((sig.type >= 0x10 && sig.type <= 0x13) || sig.type == 0x1f))
+           continue;
+         if (sig.type >= 0x10 && sig.type <= 0x13 && !userid)
+           continue;
+         htype = pgphashalgo2type(sig.hashalgo);
+         if (htype && sig.mpioff)
            {
-             solv_free(sig.sigdata);
-             continue;
+             Chksum *h = solv_chksum_create(htype);
+             pgpsig_makesigdata(&sig, p, l, pubkey, pubkeyl, userid, useridl, h);
+             solv_chksum_free(h, 0);
            }
          if (!memcmp(keyid, sig.issuer, 8))
            {
@@ -551,7 +682,7 @@ parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
                    maxsigcr = sig.created;
                }
            }
-         else
+         else if (flags & ADD_WITH_KEYSIGNATURES)
            {
              char issuerstr[17];
              Id shandle = repodata_new_handle(data);
@@ -567,7 +698,7 @@ parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
            }
          solv_free(sig.sigdata);
        }
-      if (tag == 13)
+      if (tag == 13 && !insubkey)              /* User ID Packet */
        {
          userid = solv_realloc(userid, l);
          if (l)
@@ -575,39 +706,9 @@ parsekeydata(Solvable *s, Repodata *data, unsigned char *p, int pl)
          useridl = l;
        }
     }
-  if (kcr)
-    repodata_set_num(data, s - s->repo->pool->solvables, SOLVABLE_BUILDTIME, kcr);
-  if (maxex && maxex != -1)
-    repodata_set_num(data, s - s->repo->pool->solvables, PUBKEY_EXPIRES, maxex);
-  s->name = pool_str2id(s->repo->pool, "gpg-pubkey", 1);
-  s->evr = 1;
-  s->arch = 1;
-  if (userid && useridl)
-    {
-      char *useridstr = solv_malloc(useridl + 1);
-      memcpy(useridstr, userid, useridl);
-      useridstr[useridl] = 0;
-      setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_SUMMARY, useridstr);
-      free(useridstr);
-    }
-  if (pubdata)
-    {
-      char keyidstr[17];
-      solv_bin2hex(keyid, 8, keyidstr);
-      repodata_set_str(data, s - s->repo->pool->solvables, PUBKEY_KEYID, keyidstr);
-    }
-  if (pubdata)
-    {
-      /* build rpm-style evr */
-      char evr[8 + 1 + 8 + 1];
-      solv_bin2hex(keyid + 4, 4, evr);
-      sprintf(evr + 8, "-%08x", maxsigcr);
-      s->evr = pool_str2id(s->repo->pool, evr, 1);
-      /* set data blob */
-      repodata_set_binary(data, s - s->repo->pool->solvables, PUBKEY_DATA, pubdata, pubdatal);
-    }
   solv_free(pubkey);
   solv_free(userid);
+  return p ? p - pstart : 0;
 }
 
 
@@ -644,7 +745,7 @@ struct pgpDig_s {
 /* only rpm knows how to do the release calculation, we don't dare
  * to recreate all the bugs in libsolv */
 static void
-parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
+parsepubkey_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
 {
   Pool *pool = s->repo->pool;
   struct pgpDigParams_s *digpubkey;
@@ -687,24 +788,43 @@ parsekeydata_rpm(Solvable *s, Repodata *data, unsigned char *pkts, int pktsl)
 
 #endif /* ENABLE_RPMDB */
 
+/* parse an ascii armored pubkey
+ * adds multiple pubkeys if ADD_MULTIPLE_PUBKEYS is set */
 static int
-pubkey2solvable(Solvable *s, Repodata *data, char *pubkey)
+pubkey2solvable(Pool *pool, Id p, Repodata *data, char *pubkey, int flags)
 {
-  unsigned char *pkts;
-  int pktsl;
+  unsigned char *pkts, *pkts_orig;
+  int pktsl, pl = 0, tag, l, hl;
 
-  pkts = unarmor(pubkey, &pktsl);
-  if (!pkts)
+  if (!unarmor(pubkey, &pkts, &pktsl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----"))
     {
-      pool_error(s->repo->pool, 0, "unarmor failure");
+      pool_error(pool, 0, "unarmor failure");
       return 0;
     }
-  setutf8string(data, s - s->repo->pool->solvables, SOLVABLE_DESCRIPTION, pubkey);
-  parsekeydata(s, data, pkts, pktsl);
+  pkts_orig = pkts;
+  tag = 6;
+  while (pktsl)
+    {
+      if (tag == 6)
+       {
+         setutf8string(data, p, SOLVABLE_DESCRIPTION, pubkey);
+         pl = parsepubkey(pool->solvables + p, data, pkts, pktsl, flags);
 #ifdef ENABLE_RPMDB
-  parsekeydata_rpm(s, data, pkts, pktsl);
+         parsepubkey_rpm(pool->solvables + p, data, pkts, pktsl);
 #endif
-  solv_free((void *)pkts);
+         if (!pl || !(flags & ADD_MULTIPLE_PUBKEYS))
+           break;
+       }
+      pkts += pl;
+      pktsl -= pl;
+      hl = parsepkgheader(pkts, pktsl, &tag, &l);
+      if (!hl)
+       break;
+      pl = l + hl;
+      if (tag == 6)
+        p = repo_add_solvable(pool->solvables[p].repo);
+    }
+  solv_free((void *)pkts_orig);
   return 1;
 }
 
@@ -718,7 +838,6 @@ repo_add_rpmdb_pubkeys(Repo *repo, int flags)
   int i;
   char *str;
   Repodata *data;
-  Solvable *s;
   const char *rootdir = 0;
   void *state;
 
@@ -730,6 +849,7 @@ repo_add_rpmdb_pubkeys(Repo *repo, int flags)
   rpm_installedrpmdbids(state, "Name", "gpg-pubkey", &q);
   for (i = 0; i < q.count; i++)
     {
+      Id p, p2;
       void *handle;
       unsigned long long itime;
 
@@ -739,15 +859,23 @@ repo_add_rpmdb_pubkeys(Repo *repo, int flags)
       str = rpm_query(handle, SOLVABLE_DESCRIPTION);
       if (!str)
        continue;
-      s = pool_id2solvable(pool, repo_add_solvable(repo));
-      pubkey2solvable(s, data, str);
+      p = repo_add_solvable(repo);
+      if (!pubkey2solvable(pool, p, data, str, flags))
+       {
+         solv_free(str);
+         repo_free_solvable(repo, p, 1);
+         continue;
+       }
       solv_free(str);
       itime = rpm_query_num(handle, SOLVABLE_INSTALLTIME, 0);
-      if (itime)
-        repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLTIME, itime);
-      if (!repo->rpmdbid)
-       repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
-      repo->rpmdbid[s - pool->solvables - repo->start] = q.elements[i];
+      for (p2 = p; p2 < pool->nsolvables; p2++)
+       {
+         if (itime)
+           repodata_set_num(data, p2, SOLVABLE_INSTALLTIME, itime);
+         if (!repo->rpmdbid)
+           repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
+         repo->rpmdbid[p2 - repo->start] = q.elements[i];
+       }
     }
   queue_free(&q);
   rpm_state_free(state);
@@ -758,25 +886,14 @@ repo_add_rpmdb_pubkeys(Repo *repo, int flags)
 
 #endif
 
-Id
-repo_add_pubkey(Repo *repo, const char *key, int flags)
+static char *
+solv_slurp(FILE *fp, int *lenp)
 {
-  Pool *pool = repo->pool;
-  Repodata *data;
-  Solvable *s;
-  char *buf;
-  int bufl, l, ll;
-  FILE *fp;
+  int l, ll;
+  char *buf = 0;
+  int bufl = 0;
 
-  data = repo_add_repodata(repo, flags);
-  buf = 0;
-  bufl = 0;
-  if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, key) : key, "r")) == 0)
-    {
-      pool_error(pool, -1, "%s: %s", key, strerror(errno));
-      return 0;
-    }
-  for (l = 0; ;)
+  for (l = 0; ; l += ll)
     {
       if (bufl - l < 4096)
        {
@@ -786,26 +903,346 @@ repo_add_pubkey(Repo *repo, const char *key, int flags)
       ll = fread(buf + l, 1, bufl - l, fp);
       if (ll < 0)
        {
-         fclose(fp);
-         pool_error(pool, -1, "%s: %s", key, strerror(errno));
-         return 0;
+         buf = solv_free(buf);
+         l = 0;
+         break;
        }
       if (ll == 0)
-       break;
-      l += ll;
+       {
+         buf[l] = 0;   /* always zero-terminate */
+         break;
+       }
+    }
+  if (lenp)
+    *lenp = l;
+  return buf;
+}
+
+Id
+repo_add_pubkey(Repo *repo, const char *keyfile, int flags)
+{
+  Pool *pool = repo->pool;
+  Repodata *data;
+  Id p;
+  char *buf;
+  FILE *fp;
+
+  data = repo_add_repodata(repo, flags);
+  buf = 0;
+  if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keyfile) : keyfile, "r")) == 0)
+    {
+      pool_error(pool, -1, "%s: %s", keyfile, strerror(errno));
+      return 0;
+    }
+  if ((buf = solv_slurp(fp, 0)) == 0)
+    {
+      pool_error(pool, -1, "%s: %s", keyfile, strerror(errno));
+      fclose(fp);
+      return 0;
     }
-  buf[l] = 0;
   fclose(fp);
-  s = pool_id2solvable(pool, repo_add_solvable(repo));
-  if (!pubkey2solvable(s, data, buf))
+  p = repo_add_solvable(repo);
+  if (!pubkey2solvable(pool, p, data, buf, flags))
     {
-      repo_free_solvable(repo, s - pool->solvables, 1);
+      repo_free_solvable(repo, p, 1);
       solv_free(buf);
       return 0;
     }
+  if (!(flags & REPO_NO_LOCATION))
+    {
+      Id p2;
+      for (p2 = p; p2 < pool->nsolvables; p2++)
+        repodata_set_location(data, p2, 0, 0, keyfile);
+    }
   solv_free(buf);
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
-  return s - pool->solvables;
+  return p;
+}
+
+static int
+is_sig_packet(unsigned char *sig, int sigl)
+{
+  if (!sigl)
+    return 0;
+  if ((sig[0] & 0x80) == 0 || (sig[0] & 0x40 ? sig[0] & 0x3f : sig[0] >> 2 & 0x0f) != 2)
+    return 0;
+  return 1;
+}
+
+static int
+is_pubkey_packet(unsigned char *pkt, int pktl)
+{
+  if (!pktl)
+    return 0;
+  if ((pkt[0] & 0x80) == 0 || (pkt[0] & 0x40 ? pkt[0] & 0x3f : pkt[0] >> 2 & 0x0f) != 6)
+    return 0;
+  return 1;
+}
+
+static void
+add_one_pubkey(Pool *pool, Repo *repo, Repodata *data, unsigned char *pbuf, int pbufl, int flags)
+{
+  Id p = repo_add_solvable(repo);
+  char *solvversion = pool_tmpjoin(pool, "libsolv-", LIBSOLV_VERSION_STRING, 0);
+  char *descr = armor(pbuf, pbufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----", solvversion);
+  setutf8string(data, p, SOLVABLE_DESCRIPTION, descr);
+  parsepubkey(pool->solvables + p, data, pbuf, pbufl, flags);
+#ifdef ENABLE_RPMDB
+  parsepubkey_rpm(pool->solvables + p, data, pbuf, pbufl);
+#endif
 }
 
+int
+repo_add_keyring(Repo *repo, FILE *fp, int flags)
+{
+  Pool *pool = repo->pool;
+  Repodata *data;
+  unsigned char *buf, *p, *pbuf;
+  int bufl, l, pl, pbufl;
+
+  data = repo_add_repodata(repo, flags);
+  buf = (unsigned char *)solv_slurp(fp, &bufl);
+  if (buf && !is_pubkey_packet(buf, bufl))
+    {
+      /* assume ascii armored */
+      unsigned char *nbuf = 0, *ubuf;
+      int nl, ubufl;
+      bufl = 0;
+      for (l = 0; (nl = unarmor((char *)buf + l, &ubuf, &ubufl, "-----BEGIN PGP PUBLIC KEY BLOCK-----", "-----END PGP PUBLIC KEY BLOCK-----")) != 0; l += nl)
+       {
+         /* found another block. concat. */
+         nbuf = solv_realloc(nbuf, bufl + ubufl);
+         if (ubufl)
+           memcpy(nbuf + bufl, ubuf, ubufl);
+          bufl += ubufl;
+         solv_free(ubuf);
+       }
+      solv_free(buf);
+      buf = nbuf;
+    }
+  /* now split into pubkey parts, ignoring the packets we don't know */
+  pbuf = 0;
+  pbufl = 0;
+  for (p = buf; bufl; p += pl, bufl -= pl)
+    {
+      int tag;
+      int hl = parsepkgheader(p, bufl, &tag, &pl);
+      if (!hl)
+       break;
+      pl += hl;
+      if (tag == 6)
+       {
+         /* found new pubkey! flush old */
+         if (pbufl)
+           {
+             add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
+             pbuf = solv_free(pbuf);
+             pbufl = 0;
+           }
+       }
+      if (tag != 6 && !pbufl)
+       continue;
+      if (tag != 6 && tag != 2 && tag != 13 && tag != 14 && tag != 17)
+       continue;
+      /* we want that packet. concat. */
+      pbuf = solv_realloc(pbuf, pbufl + pl);
+      memcpy(pbuf + pbufl, p, pl);
+      pbufl += pl;
+    }
+  if (pbufl)
+    add_one_pubkey(pool, repo, data, pbuf, pbufl, flags);
+  solv_free(pbuf);
+  solv_free(buf);
+  if (!(flags & REPO_NO_INTERNALIZE))
+    repodata_internalize(data);
+  return 0;
+}
+
+int
+repo_add_keydir(Repo *repo, const char *keydir, const char *suffix, int flags)
+{
+  Pool *pool = repo->pool;
+  Repodata *data;
+  int i, nent, sl;
+  struct dirent **namelist;
+  char *rkeydir;
+
+  data = repo_add_repodata(repo, flags);
+  nent = scandir(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, keydir) : keydir, &namelist, 0, alphasort);
+  if (nent == -1)
+    return pool_error(pool, -1, "%s: %s", keydir, strerror(errno));
+  rkeydir = pool_prepend_rootdir(pool, keydir);
+  sl = suffix ? strlen(suffix) : 0;
+  for (i = 0; i < nent; i++)
+    {
+      const char *dn = namelist[i]->d_name;
+      int l;
+      if (*dn == '.' && !(flags & ADD_KEYDIR_WITH_DOTFILES))
+       continue;
+      l = strlen(dn);
+      if (sl && (l < sl || strcmp(dn + l - sl, suffix) != 0))
+       continue;
+      repo_add_pubkey(repo, pool_tmpjoin(pool, rkeydir, "/", dn), flags | REPO_REUSE_REPODATA);
+    }
+  solv_free(rkeydir);
+  for (i = 0; i < nent; i++)
+    solv_free(namelist[i]);
+  solv_free(namelist);
+  if (!(flags & REPO_NO_INTERNALIZE))
+    repodata_internalize(data);
+  return 0;
+}
+
+Solvsig *
+solvsig_create(FILE *fp)
+{
+  Solvsig *ss;
+  unsigned char *sig;
+  int sigl, hl, tag, pktl;
+  struct pgpsig pgpsig;
+
+  if ((sig = (unsigned char *)solv_slurp(fp, &sigl)) == 0)
+    return 0;
+  if (!is_sig_packet(sig, sigl))
+    {
+      /* not a raw sig, check armored */
+      unsigned char *nsig;
+      if (!unarmor((char *)sig, &nsig, &sigl, "-----BEGIN PGP SIGNATURE-----", "-----END PGP SIGNATURE-----"))
+       {
+         solv_free(sig);
+         return 0;
+       }
+      solv_free(sig);
+      sig = nsig;
+      if (!is_sig_packet(sig, sigl))
+       {
+         solv_free(sig);
+         return 0;
+       }
+    }
+  hl = parsepkgheader(sig, sigl, &tag, &pktl);
+  if (!hl || tag != 2 || !pktl)
+    {
+      solv_free(sig);
+      return 0;
+    }
+  pgpsig_init(&pgpsig, sig + hl, pktl);
+  if (pgpsig.type != 0 || !pgpsig.haveissuer)
+    {
+      solv_free(sig);
+      return 0;
+    }
+  ss = solv_calloc(1, sizeof(*ss));
+  ss->sigpkt = solv_memdup(sig + hl, pktl);
+  ss->sigpktl = pktl;
+  solv_free(sig);
+  solv_bin2hex(pgpsig.issuer, 8, ss->keyid);
+  ss->htype = pgphashalgo2type(pgpsig.hashalgo);
+  ss->created = pgpsig.created;
+  ss->expires = pgpsig.expires;
+  return ss;
+}
+
+void
+solvsig_free(Solvsig *ss)
+{
+  solv_free(ss->sigpkt);
+  solv_free(ss);
+}
+
+static int
+repo_find_all_pubkeys_cmp(const void *va, const void *vb, void *dp)
+{
+  Pool *pool = dp;
+  Id a = *(Id *)va;
+  Id b = *(Id *)vb;
+  /* cannot use evrcmp, as rpm says '0' > 'a' */
+  return strcmp(pool_id2str(pool, pool->solvables[b].evr), pool_id2str(pool, pool->solvables[a].evr));
+}
+
+void
+repo_find_all_pubkeys(Repo *repo, const char *keyid, Queue *q)
+{
+  Id p;
+  Solvable *s;
+
+  queue_empty(q);
+  if (!keyid)
+    return;
+  queue_init(q);
+  FOR_REPO_SOLVABLES(repo, p, s)
+    {
+      const char *kidstr, *evr = pool_id2str(s->repo->pool, s->evr);
+
+      if (!evr || strncmp(evr, keyid + 8, 8) != 0)
+       continue;
+      kidstr = solvable_lookup_str(s, PUBKEY_KEYID);
+      if (kidstr && !strcmp(kidstr, keyid))
+        queue_push(q, p);
+    }
+  if (q->count > 1)
+    solv_sort(q->elements, q->count, sizeof(Id), repo_find_all_pubkeys_cmp, repo->pool);
+}
+
+Id
+repo_find_pubkey(Repo *repo, const char *keyid)
+{
+  Queue q;
+  Id p;
+  queue_init(&q);
+  repo_find_all_pubkeys(repo, keyid, &q);
+  p = q.count ? q.elements[0] : 0;
+  queue_free(&q);
+  return p;
+}
+
+#ifdef ENABLE_PGPVRFY
+
+/* warning: does not check key expiry/revokation, same as with gpgv or rpm */
+/* returns the Id of the pubkey that verified the signature */
+Id
+repo_verify_sigdata(Repo *repo, unsigned char *sigdata, int sigdatal, const char *keyid)
+{
+  Id p;
+  Queue q;
+  int i;
+
+  if (!sigdata || !keyid)
+    return 0;
+  queue_init(&q);
+  repo_find_all_pubkeys(repo, keyid, &q);
+  for (i = 0; i < q.count; i++)
+    {
+      int pubdatal;
+      const unsigned char *pubdata = repo_lookup_binary(repo, q.elements[i], PUBKEY_DATA, &pubdatal);
+      if (pubdata && solv_pgpvrfy(pubdata, pubdatal, sigdata, sigdatal))
+       break;
+    }
+  p = i < q.count? q.elements[i] : 0;
+  queue_free(&q);
+  return p;
+}
+
+Id
+solvsig_verify(Solvsig *ss, Repo *repo, Chksum *chk)
+{
+  struct pgpsig pgpsig;
+  void *chk2;
+  Id p;
+
+  if (!chk || solv_chksum_isfinished(chk))
+    return 0;
+  pgpsig_init(&pgpsig, ss->sigpkt, ss->sigpktl);
+  chk2 = solv_chksum_create_clone(chk);
+  pgpsig_makesigdata(&pgpsig, ss->sigpkt, ss->sigpktl, 0, 0, 0, 0, chk2);
+  solv_chksum_free(chk2, 0);
+  if (!pgpsig.sigdata)
+    return 0;
+  p = repo_verify_sigdata(repo, pgpsig.sigdata, pgpsig.sigdatal, ss->keyid);
+  solv_free(pgpsig.sigdata);
+  return p;
+}
+
+#endif
+