summaryrefslogtreecommitdiff
path: root/rpmio/rpmpgp.c
diff options
context:
space:
mode:
Diffstat (limited to 'rpmio/rpmpgp.c')
-rw-r--r--rpmio/rpmpgp.c1669
1 files changed, 1669 insertions, 0 deletions
diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
new file mode 100644
index 0000000..04472de
--- /dev/null
+++ b/rpmio/rpmpgp.c
@@ -0,0 +1,1669 @@
+/** \ingroup rpmio signature
+ * \file rpmio/rpmpgp.c
+ * Routines to handle RFC-2440 detached signatures.
+ */
+
+#include "system.h"
+
+#include <pthread.h>
+
+#include <rpm/rpmstring.h>
+#include <rpm/rpmlog.h>
+
+#include "rpmio/digest.h"
+#include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
+
+#include "debug.h"
+
+
+static int _debug = 0;
+
+static int _print = 0;
+
+static int _crypto_initialized = 0;
+static int _new_process = 1;
+
+typedef const struct pgpValTbl_s {
+ int val;
+ char const * const str;
+} * pgpValTbl;
+
+static struct pgpValTbl_s const pgpSigTypeTbl[] = {
+ { PGPSIGTYPE_BINARY, "Binary document signature" },
+ { PGPSIGTYPE_TEXT, "Text document signature" },
+ { PGPSIGTYPE_STANDALONE, "Standalone signature" },
+ { PGPSIGTYPE_GENERIC_CERT, "Generic certification of a User ID and Public Key" },
+ { PGPSIGTYPE_PERSONA_CERT, "Persona certification of a User ID and Public Key" },
+ { PGPSIGTYPE_CASUAL_CERT, "Casual certification of a User ID and Public Key" },
+ { PGPSIGTYPE_POSITIVE_CERT, "Positive certification of a User ID and Public Key" },
+ { PGPSIGTYPE_SUBKEY_BINDING,"Subkey Binding Signature" },
+ { PGPSIGTYPE_SIGNED_KEY, "Signature directly on a key" },
+ { PGPSIGTYPE_KEY_REVOKE, "Key revocation signature" },
+ { PGPSIGTYPE_SUBKEY_REVOKE, "Subkey revocation signature" },
+ { PGPSIGTYPE_CERT_REVOKE, "Certification revocation signature" },
+ { PGPSIGTYPE_TIMESTAMP, "Timestamp signature" },
+ { -1, "Unknown signature type" },
+};
+
+static struct pgpValTbl_s const pgpPubkeyTbl[] = {
+ { PGPPUBKEYALGO_RSA, "RSA" },
+ { PGPPUBKEYALGO_RSA_ENCRYPT,"RSA(Encrypt-Only)" },
+ { PGPPUBKEYALGO_RSA_SIGN, "RSA(Sign-Only)" },
+ { PGPPUBKEYALGO_ELGAMAL_ENCRYPT,"Elgamal(Encrypt-Only)" },
+ { PGPPUBKEYALGO_DSA, "DSA" },
+ { PGPPUBKEYALGO_EC, "Elliptic Curve" },
+ { PGPPUBKEYALGO_ECDSA, "ECDSA" },
+ { PGPPUBKEYALGO_ELGAMAL, "Elgamal" },
+ { PGPPUBKEYALGO_DH, "Diffie-Hellman (X9.42)" },
+ { -1, "Unknown public key algorithm" },
+};
+
+static struct pgpValTbl_s const pgpSymkeyTbl[] = {
+ { PGPSYMKEYALGO_PLAINTEXT, "Plaintext" },
+ { PGPSYMKEYALGO_IDEA, "IDEA" },
+ { PGPSYMKEYALGO_TRIPLE_DES, "3DES" },
+ { PGPSYMKEYALGO_CAST5, "CAST5" },
+ { PGPSYMKEYALGO_BLOWFISH, "BLOWFISH" },
+ { PGPSYMKEYALGO_SAFER, "SAFER" },
+ { PGPSYMKEYALGO_DES_SK, "DES/SK" },
+ { PGPSYMKEYALGO_AES_128, "AES(128-bit key)" },
+ { PGPSYMKEYALGO_AES_192, "AES(192-bit key)" },
+ { PGPSYMKEYALGO_AES_256, "AES(256-bit key)" },
+ { PGPSYMKEYALGO_TWOFISH, "TWOFISH(256-bit key)" },
+ { PGPSYMKEYALGO_NOENCRYPT, "no encryption" },
+ { -1, "Unknown symmetric key algorithm" },
+};
+
+static struct pgpValTbl_s const pgpCompressionTbl[] = {
+ { PGPCOMPRESSALGO_NONE, "Uncompressed" },
+ { PGPCOMPRESSALGO_ZIP, "ZIP" },
+ { PGPCOMPRESSALGO_ZLIB, "ZLIB" },
+ { PGPCOMPRESSALGO_BZIP2, "BZIP2" },
+ { -1, "Unknown compression algorithm" },
+};
+
+static struct pgpValTbl_s const pgpHashTbl[] = {
+ { PGPHASHALGO_MD5, "MD5" },
+ { PGPHASHALGO_SHA1, "SHA1" },
+ { PGPHASHALGO_RIPEMD160, "RIPEMD160" },
+ { PGPHASHALGO_MD2, "MD2" },
+ { PGPHASHALGO_TIGER192, "TIGER192" },
+ { PGPHASHALGO_HAVAL_5_160, "HAVAL-5-160" },
+ { PGPHASHALGO_SHA256, "SHA256" },
+ { PGPHASHALGO_SHA384, "SHA384" },
+ { PGPHASHALGO_SHA512, "SHA512" },
+ { PGPHASHALGO_SHA224, "SHA224" },
+ { -1, "Unknown hash algorithm" },
+};
+
+static struct pgpValTbl_s const pgpKeyServerPrefsTbl[] = {
+ { 0x80, "No-modify" },
+ { -1, "Unknown key server preference" },
+};
+
+static struct pgpValTbl_s const pgpSubTypeTbl[] = {
+ { PGPSUBTYPE_SIG_CREATE_TIME,"signature creation time" },
+ { PGPSUBTYPE_SIG_EXPIRE_TIME,"signature expiration time" },
+ { PGPSUBTYPE_EXPORTABLE_CERT,"exportable certification" },
+ { PGPSUBTYPE_TRUST_SIG, "trust signature" },
+ { PGPSUBTYPE_REGEX, "regular expression" },
+ { PGPSUBTYPE_REVOCABLE, "revocable" },
+ { PGPSUBTYPE_KEY_EXPIRE_TIME,"key expiration time" },
+ { PGPSUBTYPE_ARR, "additional recipient request" },
+ { PGPSUBTYPE_PREFER_SYMKEY, "preferred symmetric algorithms" },
+ { PGPSUBTYPE_REVOKE_KEY, "revocation key" },
+ { PGPSUBTYPE_ISSUER_KEYID, "issuer key ID" },
+ { PGPSUBTYPE_NOTATION, "notation data" },
+ { PGPSUBTYPE_PREFER_HASH, "preferred hash algorithms" },
+ { PGPSUBTYPE_PREFER_COMPRESS,"preferred compression algorithms" },
+ { PGPSUBTYPE_KEYSERVER_PREFERS,"key server preferences" },
+ { PGPSUBTYPE_PREFER_KEYSERVER,"preferred key server" },
+ { PGPSUBTYPE_PRIMARY_USERID,"primary user id" },
+ { PGPSUBTYPE_POLICY_URL, "policy URL" },
+ { PGPSUBTYPE_KEY_FLAGS, "key flags" },
+ { PGPSUBTYPE_SIGNER_USERID, "signer's user id" },
+ { PGPSUBTYPE_REVOKE_REASON, "reason for revocation" },
+ { PGPSUBTYPE_FEATURES, "features" },
+ { PGPSUBTYPE_EMBEDDED_SIG, "embedded signature" },
+
+ { PGPSUBTYPE_INTERNAL_100, "internal subpkt type 100" },
+ { PGPSUBTYPE_INTERNAL_101, "internal subpkt type 101" },
+ { PGPSUBTYPE_INTERNAL_102, "internal subpkt type 102" },
+ { PGPSUBTYPE_INTERNAL_103, "internal subpkt type 103" },
+ { PGPSUBTYPE_INTERNAL_104, "internal subpkt type 104" },
+ { PGPSUBTYPE_INTERNAL_105, "internal subpkt type 105" },
+ { PGPSUBTYPE_INTERNAL_106, "internal subpkt type 106" },
+ { PGPSUBTYPE_INTERNAL_107, "internal subpkt type 107" },
+ { PGPSUBTYPE_INTERNAL_108, "internal subpkt type 108" },
+ { PGPSUBTYPE_INTERNAL_109, "internal subpkt type 109" },
+ { PGPSUBTYPE_INTERNAL_110, "internal subpkt type 110" },
+ { -1, "Unknown signature subkey type" },
+};
+
+static struct pgpValTbl_s const pgpTagTbl[] = {
+ { PGPTAG_PUBLIC_SESSION_KEY,"Public-Key Encrypted Session Key" },
+ { PGPTAG_SIGNATURE, "Signature" },
+ { PGPTAG_SYMMETRIC_SESSION_KEY,"Symmetric-Key Encrypted Session Key" },
+ { PGPTAG_ONEPASS_SIGNATURE, "One-Pass Signature" },
+ { PGPTAG_SECRET_KEY, "Secret Key" },
+ { PGPTAG_PUBLIC_KEY, "Public Key" },
+ { PGPTAG_SECRET_SUBKEY, "Secret Subkey" },
+ { PGPTAG_COMPRESSED_DATA, "Compressed Data" },
+ { PGPTAG_SYMMETRIC_DATA, "Symmetrically Encrypted Data" },
+ { PGPTAG_MARKER, "Marker" },
+ { PGPTAG_LITERAL_DATA, "Literal Data" },
+ { PGPTAG_TRUST, "Trust" },
+ { PGPTAG_USER_ID, "User ID" },
+ { PGPTAG_PUBLIC_SUBKEY, "Public Subkey" },
+ { PGPTAG_COMMENT_OLD, "Comment (from OpenPGP draft)" },
+ { PGPTAG_PHOTOID, "PGP's photo ID" },
+ { PGPTAG_ENCRYPTED_MDC, "Integrity protected encrypted data" },
+ { PGPTAG_MDC, "Manipulaion detection code packet" },
+ { PGPTAG_PRIVATE_60, "Private #60" },
+ { PGPTAG_COMMENT, "Comment" },
+ { PGPTAG_PRIVATE_62, "Private #62" },
+ { PGPTAG_CONTROL, "Control (GPG)" },
+ { -1, "Unknown packet tag" },
+};
+
+static struct pgpValTbl_s const pgpArmorTbl[] = {
+ { PGPARMOR_MESSAGE, "MESSAGE" },
+ { PGPARMOR_PUBKEY, "PUBLIC KEY BLOCK" },
+ { PGPARMOR_SIGNATURE, "SIGNATURE" },
+ { PGPARMOR_SIGNED_MESSAGE, "SIGNED MESSAGE" },
+ { PGPARMOR_FILE, "ARMORED FILE" },
+ { PGPARMOR_PRIVKEY, "PRIVATE KEY BLOCK" },
+ { PGPARMOR_SECKEY, "SECRET KEY BLOCK" },
+ { -1, "Unknown armor block" }
+};
+
+static struct pgpValTbl_s const pgpArmorKeyTbl[] = {
+ { PGPARMORKEY_VERSION, "Version: " },
+ { PGPARMORKEY_COMMENT, "Comment: " },
+ { PGPARMORKEY_MESSAGEID, "MessageID: " },
+ { PGPARMORKEY_HASH, "Hash: " },
+ { PGPARMORKEY_CHARSET, "Charset: " },
+ { -1, "Unknown armor key" }
+};
+
+static void pgpPrtNL(void)
+{
+ if (!_print) return;
+ fprintf(stderr, "\n");
+}
+
+static void pgpPrtInt(const char *pre, int i)
+{
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ fprintf(stderr, " %d", i);
+}
+
+static void pgpPrtStr(const char *pre, const char *s)
+{
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ fprintf(stderr, " %s", s);
+}
+
+static const char * pgpValStr(pgpValTbl vs, uint8_t val)
+{
+ do {
+ if (vs->val == val)
+ break;
+ } while ((++vs)->val != -1);
+ return vs->str;
+}
+
+static pgpValTbl pgpValTable(pgpValType type)
+{
+ switch (type) {
+ case PGPVAL_TAG: return pgpTagTbl;
+ case PGPVAL_ARMORBLOCK: return pgpArmorTbl;
+ case PGPVAL_ARMORKEY: return pgpArmorKeyTbl;
+ case PGPVAL_SIGTYPE: return pgpSigTypeTbl;
+ case PGPVAL_SUBTYPE: return pgpSubTypeTbl;
+ case PGPVAL_PUBKEYALGO: return pgpPubkeyTbl;
+ case PGPVAL_SYMKEYALGO: return pgpSymkeyTbl;
+ case PGPVAL_COMPRESSALGO: return pgpCompressionTbl;
+ case PGPVAL_HASHALGO: return pgpHashTbl;
+ case PGPVAL_SERVERPREFS: return pgpKeyServerPrefsTbl;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+const char * pgpValString(pgpValType type, uint8_t val)
+{
+ pgpValTbl tbl = pgpValTable(type);
+ return (tbl != NULL) ? pgpValStr(tbl, val) : NULL;
+}
+
+static void pgpPrtHex(const char *pre, const uint8_t *p, size_t plen)
+{
+ char *hex = NULL;
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ hex = pgpHexStr(p, plen);
+ fprintf(stderr, " %s", hex);
+ free(hex);
+}
+
+static void pgpPrtVal(const char * pre, pgpValTbl vs, uint8_t val)
+{
+ if (!_print) return;
+ if (pre && *pre)
+ fprintf(stderr, "%s", pre);
+ fprintf(stderr, "%s(%u)", pgpValStr(vs, val), (unsigned)val);
+}
+
+/** \ingroup rpmpgp
+ * Return no. of bits in a multiprecision integer.
+ * @param p pointer to multiprecision integer
+ * @return no. of bits
+ */
+static
+unsigned int pgpMpiBits(const uint8_t *p)
+{
+ return ((p[0] << 8) | p[1]);
+}
+
+/** \ingroup rpmpgp
+ * Return no. of bytes in a multiprecision integer.
+ * @param p pointer to multiprecision integer
+ * @return no. of bytes
+ */
+static
+size_t pgpMpiLen(const uint8_t *p)
+{
+ return (2 + ((pgpMpiBits(p)+7)>>3));
+}
+
+/** \ingroup rpmpgp
+ * Return hex formatted representation of a multiprecision integer.
+ * @param p bytes
+ * @return hex formatted string (malloc'ed)
+ */
+static inline
+char * pgpMpiStr(const uint8_t *p)
+{
+ char *str = NULL;
+ char *hex = pgpHexStr(p+2, pgpMpiLen(p)-2);
+ rasprintf(&str, "[%4u]: %s", pgpGrab(p, (size_t) 2), hex);
+ free(hex);
+ return str;
+}
+
+/** \ingroup rpmpgp
+ * Return value of an OpenPGP string.
+ * @param vs table of (string,value) pairs
+ * @param s string token to lookup
+ * @param se end-of-string address
+ * @return byte value
+ */
+static inline
+int pgpValTok(pgpValTbl vs, const char * s, const char * se)
+{
+ do {
+ size_t vlen = strlen(vs->str);
+ if (vlen <= (se-s) && rstreqn(s, vs->str, vlen))
+ break;
+ } while ((++vs)->val != -1);
+ return vs->val;
+}
+/**
+ * @return 0 on success
+ */
+static int pgpMpiSet(const char * pre, unsigned int lbits,
+ uint8_t *dest, const uint8_t * p, const uint8_t * pend)
+{
+ unsigned int mbits = pgpMpiBits(p);
+ unsigned int nbits;
+ size_t nbytes;
+ uint8_t *t = dest;
+ unsigned int ix;
+
+ if ((p + ((mbits+7) >> 3)) > pend)
+ return 1;
+
+ if (mbits > lbits)
+ return 1;
+
+ nbits = (lbits > mbits ? lbits : mbits);
+ nbytes = ((nbits + 7) >> 3);
+ ix = (nbits - mbits) >> 3;
+
+if (_debug)
+fprintf(stderr, "*** mbits %u nbits %u nbytes %zu ix %u\n", mbits, nbits, nbytes, ix);
+ if (ix > 0) memset(t, '\0', ix);
+ memcpy(t+ix, p+2, nbytes-ix);
+if (_debug)
+fprintf(stderr, "*** %s %s\n", pre, pgpHexStr(dest, nbytes));
+
+ return 0;
+}
+
+/**
+ * @return NULL on error
+ */
+static SECItem *pgpMpiItem(PRArenaPool *arena, SECItem *item, const uint8_t *p)
+{
+ size_t nbytes = pgpMpiLen(p)-2;
+
+ if (item == NULL) {
+ if ((item=SECITEM_AllocItem(arena, item, nbytes)) == NULL)
+ return item;
+ } else {
+ if (arena != NULL)
+ item->data = PORT_ArenaGrow(arena, item->data, item->len, nbytes);
+ else
+ item->data = PORT_Realloc(item->data, nbytes);
+
+ if (item->data == NULL) {
+ if (arena == NULL)
+ SECITEM_FreeItem(item, PR_TRUE);
+ return NULL;
+ }
+ }
+
+ memcpy(item->data, p+2, nbytes);
+ item->len = nbytes;
+ return item;
+}
+/*@=boundswrite@*/
+
+static SECKEYPublicKey *pgpNewPublicKey(KeyType type)
+{
+ PRArenaPool *arena;
+ SECKEYPublicKey *key;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ key = PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
+
+ if (key == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ key->keyType = type;
+ key->pkcs11ID = CK_INVALID_HANDLE;
+ key->pkcs11Slot = NULL;
+ key->arena = arena;
+ return key;
+}
+
+/** \ingroup rpmpgp
+ * Is buffer at beginning of an OpenPGP packet?
+ * @param p buffer
+ * @return 1 if an OpenPGP packet, 0 otherwise
+ */
+static inline
+int pgpIsPkt(const uint8_t * p)
+{
+ unsigned int val = *p++;
+ pgpTag tag;
+ int rc;
+
+ /* XXX can't deal with these. */
+ if (!(val & 0x80))
+ return 0;
+
+ if (val & 0x40)
+ tag = (pgpTag)(val & 0x3f);
+ else
+ tag = (pgpTag)((val >> 2) & 0xf);
+
+ switch (tag) {
+ case PGPTAG_MARKER:
+ case PGPTAG_SYMMETRIC_SESSION_KEY:
+ case PGPTAG_ONEPASS_SIGNATURE:
+ case PGPTAG_PUBLIC_KEY:
+ case PGPTAG_SECRET_KEY:
+ case PGPTAG_PUBLIC_SESSION_KEY:
+ case PGPTAG_SIGNATURE:
+ case PGPTAG_COMMENT:
+ case PGPTAG_COMMENT_OLD:
+ case PGPTAG_LITERAL_DATA:
+ case PGPTAG_COMPRESSED_DATA:
+ case PGPTAG_SYMMETRIC_DATA:
+ rc = 1;
+ break;
+ case PGPTAG_PUBLIC_SUBKEY:
+ case PGPTAG_SECRET_SUBKEY:
+ case PGPTAG_USER_ID:
+ case PGPTAG_RESERVED:
+ case PGPTAG_TRUST:
+ case PGPTAG_PHOTOID:
+ case PGPTAG_ENCRYPTED_MDC:
+ case PGPTAG_MDC:
+ case PGPTAG_PRIVATE_60:
+ case PGPTAG_PRIVATE_62:
+ case PGPTAG_CONTROL:
+ default:
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+#define CRC24_INIT 0xb704ce
+#define CRC24_POLY 0x1864cfb
+
+/** \ingroup rpmpgp
+ * Return CRC of a buffer.
+ * @param octets bytes
+ * @param len no. of bytes
+ * @return crc of buffer
+ */
+static inline
+unsigned int pgpCRC(const uint8_t *octets, size_t len)
+{
+ unsigned int crc = CRC24_INIT;
+ size_t i;
+
+ while (len--) {
+ crc ^= (*octets++) << 16;
+ for (i = 0; i < 8; i++) {
+ crc <<= 1;
+ if (crc & 0x1000000)
+ crc ^= CRC24_POLY;
+ }
+ }
+ return crc & 0xffffff;
+}
+
+static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype,
+ pgpDigParams _digp)
+{
+ const uint8_t *p = h;
+ size_t plen, i;
+
+ while (hlen > 0) {
+ i = pgpLen(p, &plen);
+ p += i;
+ hlen -= i;
+
+ pgpPrtVal(" ", pgpSubTypeTbl, (p[0]&(~PGPSUBTYPE_CRITICAL)));
+ if (p[0] & PGPSUBTYPE_CRITICAL)
+ if (_print)
+ fprintf(stderr, " *CRITICAL*");
+ switch (*p) {
+ case PGPSUBTYPE_PREFER_SYMKEY: /* preferred symmetric algorithms */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpSymkeyTbl, p[i]);
+ break;
+ case PGPSUBTYPE_PREFER_HASH: /* preferred hash algorithms */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpHashTbl, p[i]);
+ break;
+ case PGPSUBTYPE_PREFER_COMPRESS:/* preferred compression algorithms */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpCompressionTbl, p[i]);
+ break;
+ case PGPSUBTYPE_KEYSERVER_PREFERS:/* key server preferences */
+ for (i = 1; i < plen; i++)
+ pgpPrtVal(" ", pgpKeyServerPrefsTbl, p[i]);
+ break;
+ case PGPSUBTYPE_SIG_CREATE_TIME:
+ if (_digp && !(_digp->saved & PGPDIG_SAVED_TIME) &&
+ (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE))
+ {
+ _digp->saved |= PGPDIG_SAVED_TIME;
+ memcpy(_digp->time, p+1, sizeof(_digp->time));
+ }
+ case PGPSUBTYPE_SIG_EXPIRE_TIME:
+ case PGPSUBTYPE_KEY_EXPIRE_TIME:
+ if ((plen - 1) == 4) {
+ time_t t = pgpGrab(p+1, plen-1);
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ } else
+ pgpPrtHex("", p+1, plen-1);
+ break;
+
+ case PGPSUBTYPE_ISSUER_KEYID: /* issuer key ID */
+ if (_digp && !(_digp->saved & PGPDIG_SAVED_ID) &&
+ (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE))
+ {
+ _digp->saved |= PGPDIG_SAVED_ID;
+ memcpy(_digp->signid, p+1, sizeof(_digp->signid));
+ }
+ case PGPSUBTYPE_EXPORTABLE_CERT:
+ case PGPSUBTYPE_TRUST_SIG:
+ case PGPSUBTYPE_REGEX:
+ case PGPSUBTYPE_REVOCABLE:
+ case PGPSUBTYPE_ARR:
+ case PGPSUBTYPE_REVOKE_KEY:
+ case PGPSUBTYPE_NOTATION:
+ case PGPSUBTYPE_PREFER_KEYSERVER:
+ case PGPSUBTYPE_PRIMARY_USERID:
+ case PGPSUBTYPE_POLICY_URL:
+ case PGPSUBTYPE_KEY_FLAGS:
+ case PGPSUBTYPE_SIGNER_USERID:
+ case PGPSUBTYPE_REVOKE_REASON:
+ case PGPSUBTYPE_FEATURES:
+ case PGPSUBTYPE_EMBEDDED_SIG:
+ case PGPSUBTYPE_INTERNAL_100:
+ case PGPSUBTYPE_INTERNAL_101:
+ case PGPSUBTYPE_INTERNAL_102:
+ case PGPSUBTYPE_INTERNAL_103:
+ case PGPSUBTYPE_INTERNAL_104:
+ case PGPSUBTYPE_INTERNAL_105:
+ case PGPSUBTYPE_INTERNAL_106:
+ case PGPSUBTYPE_INTERNAL_107:
+ case PGPSUBTYPE_INTERNAL_108:
+ case PGPSUBTYPE_INTERNAL_109:
+ case PGPSUBTYPE_INTERNAL_110:
+ default:
+ pgpPrtHex("", p+1, plen-1);
+ break;
+ }
+ pgpPrtNL();
+ p += plen;
+ hlen -= plen;
+ }
+ return 0;
+}
+
+static const char * const pgpSigRSA[] = {
+ " m**d =",
+ NULL,
+};
+
+static const char * const pgpSigDSA[] = {
+ " r =",
+ " s =",
+ NULL,
+};
+
+#ifndef DSA_SUBPRIME_LEN
+#define DSA_SUBPRIME_LEN 20
+#endif
+
+static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, uint8_t sigtype,
+ const uint8_t *p, const uint8_t *h, size_t hlen, pgpDig _dig)
+{
+ const uint8_t * pend = h + hlen;
+ size_t i;
+ SECItem dsaraw;
+ unsigned char dsabuf[2*DSA_SUBPRIME_LEN];
+ char *mpi;
+
+ dsaraw.type = 0;
+ dsaraw.data = dsabuf;
+ dsaraw.len = sizeof(dsabuf);
+
+ for (i = 0; p < pend; i++, p += pgpMpiLen(p)) {
+ if (pubkey_algo == PGPPUBKEYALGO_RSA) {
+ if (i >= 1) break;
+ if (_dig &&
+ (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT))
+ {
+ switch (i) {
+ case 0: /* m**d */
+ _dig->sigdata = pgpMpiItem(NULL, _dig->sigdata, p);
+ if (_dig->sigdata == NULL)
+ return 1;
+ break;
+ default:
+ break;
+ }
+ }
+ pgpPrtStr("", pgpSigRSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_DSA) {
+ if (i >= 2) break;
+ if (_dig &&
+ (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT))
+ {
+ int xx;
+ xx = 0;
+ switch (i) {
+ case 0:
+ memset(dsaraw.data, '\0', 2*DSA_SUBPRIME_LEN);
+ /* r */
+ xx = pgpMpiSet(pgpSigDSA[i], DSA_SUBPRIME_LEN*8, dsaraw.data, p, pend);
+ break;
+ case 1: /* s */
+ xx = pgpMpiSet(pgpSigDSA[i], DSA_SUBPRIME_LEN*8, dsaraw.data + DSA_SUBPRIME_LEN, p, pend);
+ if (_dig->sigdata != NULL)
+ SECITEM_FreeItem(_dig->sigdata, PR_FALSE);
+ else if ((_dig->sigdata=SECITEM_AllocItem(NULL, NULL, 0)) == NULL) {
+ xx = 1;
+ break;
+ }
+ if (DSAU_EncodeDerSig(_dig->sigdata, &dsaraw) != SECSuccess)
+ xx = 1;
+ break;
+ default:
+ xx = 1;
+ break;
+ }
+ if (xx) return xx;
+ }
+ pgpPrtStr("", pgpSigDSA[i]);
+ } else {
+ if (_print)
+ fprintf(stderr, "%7zd", i);
+ }
+ mpi = pgpMpiStr(p);
+ pgpPrtStr("", mpi);
+ free(mpi);
+ pgpPrtNL();
+ }
+
+ return 0;
+}
+
+static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,
+ pgpDig _dig, pgpDigParams _digp)
+{
+ uint8_t version = h[0];
+ uint8_t * p;
+ size_t plen;
+ int rc;
+
+ switch (version) {
+ case 3:
+ { pgpPktSigV3 v = (pgpPktSigV3)h;
+ time_t t;
+
+ if (v->hashlen != 5)
+ return 1;
+
+ pgpPrtVal("V3 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ pgpPrtVal(" ", pgpHashTbl, v->hash_algo);
+ pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype);
+ pgpPrtNL();
+ t = pgpGrab(v->time, sizeof(v->time));
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ pgpPrtNL();
+ pgpPrtHex(" signer keyid", v->signid, sizeof(v->signid));
+ plen = pgpGrab(v->signhash16, sizeof(v->signhash16));
+ pgpPrtHex(" signhash16", v->signhash16, sizeof(v->signhash16));
+ pgpPrtNL();
+
+ if (_digp && _digp->pubkey_algo == 0) {
+ _digp->version = v->version;
+ _digp->hashlen = v->hashlen;
+ _digp->sigtype = v->sigtype;
+ _digp->hash = memcpy(xmalloc(v->hashlen), &v->sigtype, v->hashlen);
+ memcpy(_digp->time, v->time, sizeof(_digp->time));
+ memcpy(_digp->signid, v->signid, sizeof(_digp->signid));
+ _digp->pubkey_algo = v->pubkey_algo;
+ _digp->hash_algo = v->hash_algo;
+ memcpy(_digp->signhash16, v->signhash16, sizeof(_digp->signhash16));
+ }
+
+ p = ((uint8_t *)v) + sizeof(*v);
+ rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _dig);
+ } break;
+ case 4:
+ { pgpPktSigV4 v = (pgpPktSigV4)h;
+
+ pgpPrtVal("V4 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ pgpPrtVal(" ", pgpHashTbl, v->hash_algo);
+ pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype);
+ pgpPrtNL();
+
+ p = &v->hashlen[0];
+ plen = pgpGrab(v->hashlen, sizeof(v->hashlen));
+ p += sizeof(v->hashlen);
+
+ if ((p + plen) > (h + hlen))
+ return 1;
+
+if (_debug && _print)
+fprintf(stderr, " hash[%zu] -- %s\n", plen, pgpHexStr(p, plen));
+ if (_digp && _digp->pubkey_algo == 0) {
+ _digp->hashlen = sizeof(*v) + plen;
+ _digp->hash = memcpy(xmalloc(_digp->hashlen), v, _digp->hashlen);
+ }
+ (void) pgpPrtSubType(p, plen, v->sigtype, _digp);
+ p += plen;
+
+ plen = pgpGrab(p,2);
+ p += 2;
+
+ if ((p + plen) > (h + hlen))
+ return 1;
+
+if (_debug && _print)
+fprintf(stderr, " unhash[%zu] -- %s\n", plen, pgpHexStr(p, plen));
+ (void) pgpPrtSubType(p, plen, v->sigtype, _digp);
+ p += plen;
+
+ plen = pgpGrab(p,2);
+ pgpPrtHex(" signhash16", p, 2);
+ pgpPrtNL();
+
+ if (_digp && _digp->pubkey_algo == 0) {
+ _digp->version = v->version;
+ _digp->sigtype = v->sigtype;
+ _digp->pubkey_algo = v->pubkey_algo;
+ _digp->hash_algo = v->hash_algo;
+ memcpy(_digp->signhash16, p, sizeof(_digp->signhash16));
+ }
+
+ p += 2;
+ if (p > (h + hlen))
+ return 1;
+
+ rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _dig);
+ } break;
+ default:
+ rc = 1;
+ break;
+ }
+ return rc;
+}
+
+static const char * const pgpPublicRSA[] = {
+ " n =",
+ " e =",
+ NULL,
+};
+
+#ifdef NOTYET
+static const char * const pgpSecretRSA[] = {
+ " d =",
+ " p =",
+ " q =",
+ " u =",
+ NULL,
+};
+#endif
+
+static const char * const pgpPublicDSA[] = {
+ " p =",
+ " q =",
+ " g =",
+ " y =",
+ NULL,
+};
+
+#ifdef NOTYET
+static const char * const pgpSecretDSA[] = {
+ " x =",
+ NULL,
+};
+#endif
+
+static const char * const pgpPublicELGAMAL[] = {
+ " p =",
+ " g =",
+ " y =",
+ NULL,
+};
+
+#ifdef NOTYET
+static const char * const pgpSecretELGAMAL[] = {
+ " x =",
+ NULL,
+};
+#endif
+
+char * pgpHexStr(const uint8_t *p, size_t plen)
+{
+ char *t, *str;
+ str = t = xmalloc(plen * 2 + 1);
+ static char const hex[] = "0123456789abcdef";
+ while (plen-- > 0) {
+ size_t i;
+ i = *p++;
+ *t++ = hex[ (i >> 4) & 0xf ];
+ *t++ = hex[ (i ) & 0xf ];
+ }
+ *t = '\0';
+ return str;
+}
+
+static const uint8_t * pgpPrtPubkeyParams(uint8_t pubkey_algo,
+ const uint8_t *p, const uint8_t *h, size_t hlen,
+ pgpDig _dig)
+{
+ size_t i;
+
+ /* XXX we can't handle more than one key in a packet, error out */
+ if (_dig && _dig->keydata)
+ return NULL;
+
+ for (i = 0; p < &h[hlen]; i++, p += pgpMpiLen(p)) {
+ char * mpi;
+ if (pubkey_algo == PGPPUBKEYALGO_RSA) {
+ if (i >= 2) break;
+ if (_dig) {
+ if (_dig->keydata == NULL) {
+ _dig->keydata = pgpNewPublicKey(rsaKey);
+ if (_dig->keydata == NULL)
+ return NULL;
+ }
+ switch (i) {
+ case 0: /* n */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.rsa.modulus, p);
+ break;
+ case 1: /* e */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.rsa.publicExponent, p);
+ break;
+ default:
+ break;
+ }
+ }
+ pgpPrtStr("", pgpPublicRSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_DSA) {
+ if (i >= 4) break;
+ if (_dig) {
+ if (_dig->keydata == NULL) {
+ _dig->keydata = pgpNewPublicKey(dsaKey);
+ if (_dig->keydata == NULL)
+ return NULL;
+ }
+ switch (i) {
+ case 0: /* p */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.prime, p);
+ break;
+ case 1: /* q */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.subPrime, p);
+ break;
+ case 2: /* g */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.params.base, p);
+ break;
+ case 3: /* y */
+ pgpMpiItem(_dig->keydata->arena, &_dig->keydata->u.dsa.publicValue, p);
+ break;
+ default:
+ break;
+ }
+ }
+ pgpPrtStr("", pgpPublicDSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_ELGAMAL_ENCRYPT) {
+ if (i >= 3) break;
+ pgpPrtStr("", pgpPublicELGAMAL[i]);
+ } else {
+ if (_print)
+ fprintf(stderr, "%7zd", i);
+ }
+ mpi = pgpMpiStr(p);
+ pgpPrtStr("", mpi);
+ free(mpi);
+ pgpPrtNL();
+ }
+
+ return p;
+}
+
+static const uint8_t * pgpPrtSeckeyParams(uint8_t pubkey_algo,
+ const uint8_t *p, const uint8_t *h, size_t hlen)
+{
+ size_t i;
+
+ switch (*p) {
+ case 0:
+ pgpPrtVal(" ", pgpSymkeyTbl, *p);
+ break;
+ case 255:
+ p++;
+ pgpPrtVal(" ", pgpSymkeyTbl, *p);
+ switch (p[1]) {
+ case 0x00:
+ pgpPrtVal(" simple ", pgpHashTbl, p[2]);
+ p += 2;
+ break;
+ case 0x01:
+ pgpPrtVal(" salted ", pgpHashTbl, p[2]);
+ pgpPrtHex("", p+3, 8);
+ p += 10;
+ break;
+ case 0x03:
+ pgpPrtVal(" iterated/salted ", pgpHashTbl, p[2]);
+ /* FIX: unsigned cast */
+ i = (16 + (p[11] & 0xf)) << ((p[11] >> 4) + 6);
+ pgpPrtHex("", p+3, 8);
+ pgpPrtInt(" iter", i);
+ p += 11;
+ break;
+ }
+ break;
+ default:
+ pgpPrtVal(" ", pgpSymkeyTbl, *p);
+ pgpPrtHex(" IV", p+1, 8);
+ p += 8;
+ break;
+ }
+ pgpPrtNL();
+
+ p++;
+
+#ifdef NOTYET /* XXX encrypted MPI's need to be handled. */
+ for (i = 0; p < &h[hlen]; i++, p += pgpMpiLen(p)) {
+ char *mpi;
+ if (pubkey_algo == PGPPUBKEYALGO_RSA) {
+ if (pgpSecretRSA[i] == NULL) break;
+ pgpPrtStr("", pgpSecretRSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_DSA) {
+ if (pgpSecretDSA[i] == NULL) break;
+ pgpPrtStr("", pgpSecretDSA[i]);
+ } else if (pubkey_algo == PGPPUBKEYALGO_ELGAMAL_ENCRYPT) {
+ if (pgpSecretELGAMAL[i] == NULL) break;
+ pgpPrtStr("", pgpSecretELGAMAL[i]);
+ } else {
+ if (_print)
+ fprintf(stderr, "%7d", i);
+ }
+ mpi = pgpMpiStr(p);
+ pgpPrtStr("", mpi);
+ free(mpi);
+ pgpPrtNL();
+ }
+#else
+ pgpPrtHex(" secret", p, (hlen - (p - h) - 2));
+ pgpPrtNL();
+ p += (hlen - (p - h) - 2);
+#endif
+ pgpPrtHex(" checksum", p, 2);
+ pgpPrtNL();
+
+ return p;
+}
+
+static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen,
+ pgpDig _dig, pgpDigParams _digp)
+{
+ uint8_t version = *h;
+ const uint8_t * p;
+ size_t plen;
+ time_t t;
+ int rc;
+
+ switch (version) {
+ case 3:
+ { pgpPktKeyV3 v = (pgpPktKeyV3)h;
+ pgpPrtVal("V3 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ t = pgpGrab(v->time, sizeof(v->time));
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ plen = pgpGrab(v->valid, sizeof(v->valid));
+ if (plen != 0)
+ fprintf(stderr, " valid %zu days", plen);
+ pgpPrtNL();
+
+ if (_digp && _digp->tag == tag) {
+ _digp->version = v->version;
+ memcpy(_digp->time, v->time, sizeof(_digp->time));
+ _digp->pubkey_algo = v->pubkey_algo;
+ }
+
+ p = ((uint8_t *)v) + sizeof(*v);
+ p = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _dig);
+ rc = (p == NULL);
+ } break;
+ case 4:
+ { pgpPktKeyV4 v = (pgpPktKeyV4)h;
+ pgpPrtVal("V4 ", pgpTagTbl, tag);
+ pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo);
+ t = pgpGrab(v->time, sizeof(v->time));
+ if (_print)
+ fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t);
+ pgpPrtNL();
+
+ if (_digp && _digp->tag == tag) {
+ _digp->version = v->version;
+ memcpy(_digp->time, v->time, sizeof(_digp->time));
+ _digp->pubkey_algo = v->pubkey_algo;
+ }
+
+ p = ((uint8_t *)v) + sizeof(*v);
+ p = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _dig);
+ if (!(tag == PGPTAG_PUBLIC_KEY || tag == PGPTAG_PUBLIC_SUBKEY))
+ p = pgpPrtSeckeyParams(v->pubkey_algo, p, h, hlen);
+ rc = (p == NULL);
+ } break;
+ default:
+ rc = 1;
+ break;
+ }
+ return rc;
+}
+
+static int pgpPrtUserID(pgpTag tag, const uint8_t *h, size_t hlen,
+ pgpDigParams _digp)
+{
+ pgpPrtVal("", pgpTagTbl, tag);
+ if (_print)
+ fprintf(stderr, " \"%.*s\"", (int)hlen, (const char *)h);
+ pgpPrtNL();
+ if (_digp) {
+ free(_digp->userid);
+ _digp->userid = memcpy(xmalloc(hlen+1), h, hlen);
+ _digp->userid[hlen] = '\0';
+ }
+ return 0;
+}
+
+static int pgpPrtComment(pgpTag tag, const uint8_t *h, size_t hlen)
+{
+ size_t i = hlen;
+
+ pgpPrtVal("", pgpTagTbl, tag);
+ if (_print)
+ fprintf(stderr, " ");
+ while (i > 0) {
+ size_t j;
+ if (*h >= ' ' && *h <= 'z') {
+ if (_print)
+ fprintf(stderr, "%s", (const char *)h);
+ j = strlen((const char*)h);
+ while (h[j] == '\0')
+ j++;
+ } else {
+ pgpPrtHex("", h, i);
+ j = i;
+ }
+ i -= j;
+ h += j;
+ }
+ pgpPrtNL();
+ return 0;
+}
+
+int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid)
+{
+ unsigned int val = *pkt;
+ size_t plen, hlen;
+ pgpTag tag;
+ const uint8_t *se, *h;
+ DIGEST_CTX ctx;
+ int rc = -1; /* assume failure. */
+
+ if (!(val & 0x80))
+ return rc;
+
+ if (val & 0x40) {
+ tag = (val & 0x3f);
+ plen = pgpLen(pkt+1, &hlen);
+ } else {
+ tag = (val >> 2) & 0xf;
+ plen = (1 << (val & 0x3));
+ hlen = pgpGrab(pkt+1, plen);
+ }
+ if (pktlen > 0 && 1 + plen + hlen > pktlen)
+ return rc;
+
+ h = pkt + 1 + plen;
+
+ switch (h[0]) {
+ case 3:
+ { pgpPktKeyV3 v = (pgpPktKeyV3) (h);
+ se = (uint8_t *)(v + 1);
+ switch (v->pubkey_algo) {
+ case PGPPUBKEYALGO_RSA:
+ se += pgpMpiLen(se);
+ memmove(keyid, (se-8), 8);
+ rc = 0;
+ break;
+ default: /* TODO: md5 of mpi bodies (i.e. no length) */
+ break;
+ }
+ } break;
+ case 4:
+ { pgpPktKeyV4 v = (pgpPktKeyV4) (h);
+ uint8_t * d = NULL;
+ uint8_t in[3];
+ size_t dlen;
+ int i;
+
+ se = (uint8_t *)(v + 1);
+ switch (v->pubkey_algo) {
+ case PGPPUBKEYALGO_RSA:
+ for (i = 0; i < 2; i++)
+ se += pgpMpiLen(se);
+ break;
+ case PGPPUBKEYALGO_DSA:
+ for (i = 0; i < 4; i++)
+ se += pgpMpiLen(se);
+ break;
+ }
+
+ ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
+ i = se - h;
+ in[0] = 0x99;
+ in[1] = i >> 8;
+ in[2] = i;
+ (void) rpmDigestUpdate(ctx, in, 3);
+ (void) rpmDigestUpdate(ctx, h, i);
+ (void) rpmDigestFinal(ctx, (void **)&d, &dlen, 0);
+
+ if (d) {
+ memmove(keyid, (d + (dlen-8)), 8);
+ free(d);
+ rc = 0;
+ }
+
+ } break;
+ }
+ return rc;
+}
+
+int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid)
+{
+ uint8_t * pkt;
+ size_t pktlen;
+
+ if (b64decode(b64pkt, (void **)&pkt, &pktlen))
+ return -1; /* on error */
+ (void) pgpPubkeyFingerprint(pkt, pktlen, keyid);
+ pkt = _free(pkt);
+ return sizeof(keyid); /* no. of bytes of pubkey signid */
+}
+
+static int pgpPrtPkt(const uint8_t *pkt, size_t pleft,
+ pgpDig _dig, pgpDigParams _digp)
+{
+ unsigned int val = *pkt;
+ size_t pktlen;
+ pgpTag tag;
+ size_t plen;
+ const uint8_t *h;
+ size_t hlen = 0;
+ int rc = 0;
+
+ /* XXX can't deal with these. */
+ if (!(val & 0x80))
+ return -1;
+
+ if (val & 0x40) {
+ tag = (val & 0x3f);
+ plen = pgpLen(pkt+1, &hlen);
+ } else {
+ tag = (val >> 2) & 0xf;
+ plen = (1 << (val & 0x3));
+ hlen = pgpGrab(pkt+1, plen);
+ }
+
+ pktlen = 1 + plen + hlen;
+ if (pktlen > pleft)
+ return -1;
+
+ h = pkt + 1 + plen;
+ switch (tag) {
+ case PGPTAG_SIGNATURE:
+ rc = pgpPrtSig(tag, h, hlen, _dig, _digp);
+ break;
+ case PGPTAG_PUBLIC_KEY:
+ /* Get the public key fingerprint. */
+ if (_digp) {
+ if (!pgpPubkeyFingerprint(pkt, pktlen, _digp->signid))
+ _digp->saved |= PGPDIG_SAVED_ID;
+ else
+ memset(_digp->signid, 0, sizeof(_digp->signid));
+ }
+ rc = pgpPrtKey(tag, h, hlen, _dig, _digp);
+ break;
+ case PGPTAG_USER_ID:
+ rc = pgpPrtUserID(tag, h, hlen, _digp);
+ break;
+ case PGPTAG_COMMENT:
+ case PGPTAG_COMMENT_OLD:
+ rc = pgpPrtComment(tag, h, hlen);
+ break;
+
+ case PGPTAG_PUBLIC_SUBKEY:
+ case PGPTAG_SECRET_KEY:
+ case PGPTAG_SECRET_SUBKEY:
+ case PGPTAG_RESERVED:
+ case PGPTAG_PUBLIC_SESSION_KEY:
+ case PGPTAG_SYMMETRIC_SESSION_KEY:
+ case PGPTAG_COMPRESSED_DATA:
+ case PGPTAG_SYMMETRIC_DATA:
+ case PGPTAG_MARKER:
+ case PGPTAG_LITERAL_DATA:
+ case PGPTAG_TRUST:
+ case PGPTAG_PHOTOID:
+ case PGPTAG_ENCRYPTED_MDC:
+ case PGPTAG_MDC:
+ case PGPTAG_PRIVATE_60:
+ case PGPTAG_PRIVATE_62:
+ case PGPTAG_CONTROL:
+ default:
+ pgpPrtVal("", pgpTagTbl, tag);
+ pgpPrtHex("", h, hlen);
+ pgpPrtNL();
+ break;
+ }
+
+ return (rc ? -1 : pktlen);
+}
+
+pgpDig pgpNewDig(void)
+{
+ pgpDig dig = xcalloc(1, sizeof(*dig));
+
+ return dig;
+}
+
+void pgpCleanDig(pgpDig dig)
+{
+ if (dig != NULL) {
+ int i;
+ dig->signature.userid = _free(dig->signature.userid);
+ dig->pubkey.userid = _free(dig->pubkey.userid);
+ dig->signature.hash = _free(dig->signature.hash);
+ dig->pubkey.hash = _free(dig->pubkey.hash);
+ /* FIX: double indirection */
+ for (i = 0; i < 4; i++) {
+ dig->signature.params[i] = _free(dig->signature.params[i]);
+ dig->pubkey.params[i] = _free(dig->pubkey.params[i]);
+ }
+
+ memset(&dig->signature, 0, sizeof(dig->signature));
+ memset(&dig->pubkey, 0, sizeof(dig->pubkey));
+
+ if (dig->keydata != NULL) {
+ SECKEY_DestroyPublicKey(dig->keydata);
+ dig->keydata = NULL;
+ }
+
+ if (dig->sigdata != NULL) {
+ SECITEM_ZfreeItem(dig->sigdata, PR_TRUE);
+ dig->sigdata = NULL;
+ }
+ }
+ return;
+}
+
+pgpDig pgpFreeDig(pgpDig dig)
+{
+ if (dig != NULL) {
+
+ /* DUmp the signature/pubkey data. */
+ pgpCleanDig(dig);
+ dig = _free(dig);
+ }
+ return dig;
+}
+
+int pgpPrtPkts(const uint8_t * pkts, size_t pktlen, pgpDig dig, int printing)
+{
+ unsigned int val = *pkts;
+ const uint8_t *p;
+ size_t pleft;
+ int len;
+ pgpDigParams _digp = NULL;
+
+ _print = printing;
+ if (dig != NULL && (val & 0x80)) {
+ pgpTag tag = (val & 0x40) ? (val & 0x3f) : ((val >> 2) & 0xf);
+ _digp = (tag == PGPTAG_SIGNATURE) ? &dig->signature : &dig->pubkey;
+ _digp->tag = tag;
+ } else
+ _digp = NULL;
+
+ for (p = pkts, pleft = pktlen; p < (pkts + pktlen); p += len, pleft -= len) {
+ len = pgpPrtPkt(p, pleft, dig, _digp);
+ if (len <= 0)
+ return len;
+ if (len > pleft) /* XXX shouldn't happen */
+ break;
+ }
+ return 0;
+}
+
+static SECOidTag getSigAlg(pgpDigParams sigp)
+{
+ SECOidTag sigalg = SEC_OID_UNKNOWN;
+ if (sigp->pubkey_algo == PGPPUBKEYALGO_DSA) {
+ /* assume SHA1 for now, NSS doesn't have SECOID's for other types */
+ sigalg = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
+ } else if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA) {
+ switch (sigp->hash_algo) {
+ case PGPHASHALGO_MD5:
+ sigalg = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_MD2:
+ sigalg = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA1:
+ sigalg = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA256:
+ sigalg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA384:
+ sigalg = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
+ break;
+ case PGPHASHALGO_SHA512:
+ sigalg = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
+ break;
+ default:
+ break;
+ }
+ }
+ return sigalg;
+}
+
+char *pgpIdentItem(pgpDigParams digp)
+{
+ char *id = NULL;
+ if (digp) {
+
+ char *signid = pgpHexStr(digp->signid+4, sizeof(digp->signid)-4);
+ rasprintf(&id, _("V%d %s/%s %s, key ID %s"),
+ digp->version,
+ pgpValStr(pgpPubkeyTbl, digp->pubkey_algo),
+ pgpValStr(pgpHashTbl, digp->hash_algo),
+ pgpValStr(pgpTagTbl, digp->tag),
+ signid);
+ free(signid);
+ } else {
+ id = xstrdup(_("(none)"));
+ }
+ return id;
+}
+
+rpmRC pgpVerifySig(pgpDig dig, DIGEST_CTX hashctx)
+{
+ DIGEST_CTX ctx = rpmDigestDup(hashctx);
+ uint8_t *hash = NULL;
+ size_t hashlen = 0;
+ rpmRC res = RPMRC_FAIL; /* assume failure */
+ pgpDigParams sigp = dig ? &dig->signature : NULL;
+
+ if (sigp == NULL || ctx == NULL)
+ goto exit;
+
+ if (sigp->hash != NULL)
+ rpmDigestUpdate(ctx, sigp->hash, sigp->hashlen);
+
+ if (sigp->version == 4) {
+ /* V4 trailer is six octets long (rfc4880) */
+ uint8_t trailer[6];
+ uint32_t nb = sigp->hashlen;
+ nb = htonl(nb);
+ trailer[0] = sigp->version;
+ trailer[1] = 0xff;
+ memcpy(trailer+2, &nb, 4);
+ rpmDigestUpdate(ctx, trailer, sizeof(trailer));
+ }
+
+ rpmDigestFinal(ctx, (void **)&hash, &hashlen, 0);
+
+ /* Compare leading 16 bits of digest for quick check. */
+ if (hash && memcmp(hash, sigp->signhash16, 2) != 0)
+ goto exit;
+
+ /*
+ * If we have a key, verify the signature for real. Otherwise we've
+ * done all we can, return NOKEY to indicate "looks okay but dunno."
+ */
+ if (dig->keydata == NULL) {
+ res = RPMRC_NOKEY;
+ } else {
+ SECItem digest = { .type = siBuffer, .data = hash, .len = hashlen };
+ SECItem *sig = dig->sigdata;
+
+ /* Zero-pad RSA signature to expected size if necessary */
+ if (sigp->pubkey_algo == PGPPUBKEYALGO_RSA) {
+ size_t siglen = SECKEY_SignatureLen(dig->keydata);
+ if (siglen > sig->len) {
+ size_t pad = siglen - sig->len;
+ if ((sig = SECITEM_AllocItem(NULL, NULL, siglen)) == NULL) {
+ goto exit;
+ }
+ memset(sig->data, 0, pad);
+ memcpy(sig->data+pad, dig->sigdata->data, dig->sigdata->len);
+ }
+ }
+
+ /* XXX VFY_VerifyDigest() is deprecated in NSS 3.12 */
+ if (VFY_VerifyDigest(&digest, dig->keydata, sig,
+ getSigAlg(sigp), NULL) == SECSuccess) {
+ res = RPMRC_OK;
+ }
+
+ if (sig != dig->sigdata) {
+ SECITEM_ZfreeItem(sig, 1);
+ }
+ }
+
+exit:
+ free(hash);
+ return res;
+
+}
+
+static pgpArmor decodePkts(uint8_t *b, uint8_t **pkt, size_t *pktlen)
+{
+ const char * enc = NULL;
+ const char * crcenc = NULL;
+ uint8_t * dec;
+ uint8_t * crcdec;
+ size_t declen;
+ size_t crclen;
+ uint32_t crcpkt, crc;
+ const char * armortype = NULL;
+ char * t, * te;
+ int pstate = 0;
+ pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */
+ if (pgpIsPkt(b)) {
+#ifdef NOTYET /* XXX ASCII Pubkeys only, please. */
+ ec = 0; /* XXX fish out pkt type. */
+#endif
+ goto exit;
+ }
+
+#define TOKEQ(_s, _tok) (rstreqn((_s), (_tok), sizeof(_tok)-1))
+
+ for (t = (char *)b; t && *t; t = te) {
+ int rc;
+ if ((te = strchr(t, '\n')) == NULL)
+ te = t + strlen(t);
+ else
+ te++;
+
+ switch (pstate) {
+ case 0:
+ armortype = NULL;
+ if (!TOKEQ(t, "-----BEGIN PGP "))
+ continue;
+ t += sizeof("-----BEGIN PGP ")-1;
+
+ rc = pgpValTok(pgpArmorTbl, t, te);
+ if (rc < 0) {
+ ec = PGPARMOR_ERR_UNKNOWN_ARMOR_TYPE;
+ goto exit;
+ }
+ if (rc != PGPARMOR_PUBKEY) /* XXX ASCII Pubkeys only, please. */
+ continue;
+
+ armortype = pgpValStr(pgpArmorTbl, rc);
+ t += strlen(armortype);
+ if (!TOKEQ(t, "-----"))
+ continue;
+ t += sizeof("-----")-1;
+ if (*t != '\n' && *t != '\r')
+ continue;
+ *t = '\0';
+ pstate++;
+ break;
+ case 1:
+ enc = NULL;
+ rc = pgpValTok(pgpArmorKeyTbl, t, te);
+ if (rc >= 0)
+ continue;
+ if (*t != '\n' && *t != '\r') {
+ pstate = 0;
+ continue;
+ }
+ enc = te; /* Start of encoded packets */
+ pstate++;
+ break;
+ case 2:
+ crcenc = NULL;
+ if (*t != '=')
+ continue;
+ *t++ = '\0'; /* Terminate encoded packets */
+ crcenc = t; /* Start of encoded crc */
+ pstate++;
+ break;
+ case 3:
+ pstate = 0;
+ if (!TOKEQ(t, "-----END PGP ")) {
+ ec = PGPARMOR_ERR_NO_END_PGP;
+ goto exit;
+ }
+ *t = '\0'; /* Terminate encoded crc */
+ t += sizeof("-----END PGP ")-1;
+ if (t >= te) continue;
+
+ if (armortype == NULL) /* XXX can't happen */
+ continue;
+ if (!rstreqn(t, armortype, strlen(armortype)))
+ continue;
+
+ t += strlen(armortype);
+ if (t >= te) continue;
+
+ if (!TOKEQ(t, "-----")) {
+ ec = PGPARMOR_ERR_NO_END_PGP;
+ goto exit;
+ }
+ t += (sizeof("-----")-1);
+ if (t >= te) continue;
+ /* XXX permitting \r here is not RFC-2440 compliant <shrug> */
+ if (!(*t == '\n' || *t == '\r')) continue;
+
+ crcdec = NULL;
+ crclen = 0;
+ if (b64decode(crcenc, (void **)&crcdec, &crclen) != 0) {
+ ec = PGPARMOR_ERR_CRC_DECODE;
+ goto exit;
+ }
+ crcpkt = pgpGrab(crcdec, crclen);
+ crcdec = _free(crcdec);
+ dec = NULL;
+ declen = 0;
+ if (b64decode(enc, (void **)&dec, &declen) != 0) {
+ ec = PGPARMOR_ERR_BODY_DECODE;
+ goto exit;
+ }
+ crc = pgpCRC(dec, declen);
+ if (crcpkt != crc) {
+ ec = PGPARMOR_ERR_CRC_CHECK;
+ goto exit;
+ }
+ if (pkt) *pkt = dec;
+ if (pktlen) *pktlen = declen;
+ ec = PGPARMOR_PUBKEY; /* XXX ASCII Pubkeys only, please. */
+ goto exit;
+ break;
+ }
+ }
+ ec = PGPARMOR_NONE;
+
+exit:
+ return ec;
+}
+
+pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen)
+{
+ uint8_t * b = NULL;
+ ssize_t blen;
+ pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */
+ int rc = rpmioSlurp(fn, &b, &blen);
+ if (rc == 0 && b != NULL && blen > 0) {
+ ec = decodePkts(b, pkt, pktlen);
+ }
+ free(b);
+ return ec;
+}
+
+pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen)
+{
+ pgpArmor ec = PGPARMOR_ERR_NO_BEGIN_PGP; /* XXX assume failure */
+ if (armor && strlen(armor) > 0) {
+ uint8_t *b = (uint8_t*) xstrdup(armor);
+ ec = decodePkts(b, pkt, pktlen);
+ free(b);
+ }
+ return ec;
+}
+
+char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns)
+{
+ char *buf = NULL, *val = NULL;
+ char *enc = b64encode(s, ns, -1);
+ char *crc = b64crc(s, ns);
+ const char *valstr = pgpValStr(pgpArmorTbl, atype);
+
+ if (crc != NULL && enc != NULL) {
+ rasprintf(&buf, "%s=%s", enc, crc);
+ }
+ free(crc);
+ free(enc);
+
+ rasprintf(&val, "-----BEGIN PGP %s-----\nVersion: rpm-" VERSION " (NSS-3)\n\n"
+ "%s\n-----END PGP %s-----\n",
+ valstr, buf != NULL ? buf : "", valstr);
+
+ free(buf);
+ return val;
+}
+
+/*
+ * Only flag for re-initialization here, in the common case the child
+ * exec()'s something else shutting down NSS here would be waste of time.
+ */
+static void at_forkchild(void)
+{
+ _new_process = 1;
+}
+
+int rpmInitCrypto(void) {
+ int rc = 0;
+
+ /* Lazy NSS shutdown for re-initialization after fork() */
+ if (_new_process && _crypto_initialized) {
+ rpmFreeCrypto();
+ }
+
+ /* Initialize NSS if not already done */
+ if (!_crypto_initialized) {
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ rc = -1;
+ } else {
+ _crypto_initialized = 1;
+ }
+ }
+
+ /* Register one post-fork handler per process */
+ if (_new_process) {
+ if (pthread_atfork(NULL, NULL, at_forkchild) != 0) {
+ rpmlog(RPMLOG_WARNING, _("Failed to register fork handler: %m\n"));
+ }
+ _new_process = 0;
+ }
+ return rc;
+}
+
+int rpmFreeCrypto(void)
+{
+ int rc = 0;
+ if (_crypto_initialized) {
+ rc = (NSS_Shutdown() != SECSuccess);
+ _crypto_initialized = 0;
+ }
+ return rc;
+}
+
+