diff options
author | Neal H. Walfield <neal@pep.foundation> | 2022-04-11 15:04:45 +0200 |
---|---|---|
committer | Panu Matilainen <pmatilai@redhat.com> | 2022-04-12 09:48:53 +0300 |
commit | d8bb57eeabe249c2c85bf46b1162d7e57a310e37 (patch) | |
tree | d9a2ed7b35cf391efebd31445f7c0ec212968319 /rpmio | |
parent | eaf359f35c6433cfe80fc8814795081b92171be8 (diff) | |
download | rpm-d8bb57eeabe249c2c85bf46b1162d7e57a310e37.tar.gz |
Move the internal OpenPGP implementation to its own file.
Split the internal OpenPGP implementation into the bits that are
needed by a new OpenPGP backend like Sequoia, and the bits that are
not needed by another OpenPGP backend.
Move most of the functionality in rpmio/rpmpgp.c into
rpmio/rpmpgp_internal.c.
Leave pgpValStr, and pgpIdentItem, which are used for printing and
needn't be reimplemented by other backends, and pgpReadPkts, which is
just a thin wrapper around pgpParsePkts, and which uses an internal
rpm function that a new backend shouldn't have to worry about
emulating or even calling.
Move the symbol tables, which are used by pgpValStr, pgpIdentItem, and
the internal OpenPGP implementation to rpmio/rpmpgp.h. These are
common to all implementations.
Fixes #2000.
Diffstat (limited to 'rpmio')
-rw-r--r-- | rpmio/Makefile.am | 4 | ||||
-rw-r--r-- | rpmio/rpmpgp.c | 1504 | ||||
-rw-r--r-- | rpmio/rpmpgp.h | 173 | ||||
-rw-r--r-- | rpmio/rpmpgp_internal.c | 1344 |
4 files changed, 1522 insertions, 1503 deletions
diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am index 6ff64fa1d..35718cbf8 100644 --- a/rpmio/Makefile.am +++ b/rpmio/Makefile.am @@ -20,11 +20,11 @@ usrlib_LTLIBRARIES = librpmio.la librpmio_la_SOURCES = \ argv.c base64.c digest.h digest.c expression.c macro.c \ rpmhook.c rpmio.c rpmlog.c rpmmalloc.c rgetopt.c \ - rpmpgp.c rpmsq.c rpmsw.c url.c \ + rpmpgp.c rpmpgp.h rpmsq.c rpmsw.c url.c \ rpmio_internal.h rpmhook.h rpmvercmp.c rpmver.c \ rpmstring.c rpmfileutil.c rpmglob.c \ rpmkeyring.c rpmstrpool.c rpmmacro_internal.h \ - rpmlua.c rpmlua.h lposix.c lposix.h + rpmlua.c rpmlua.h lposix.c lposix.h rpmpgp_internal.c if WITH_OPENSSL librpmio_la_SOURCES += digest_openssl.c diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c index cf827d6d2..efff7ff06 100644 --- a/rpmio/rpmpgp.c +++ b/rpmio/rpmpgp.c @@ -9,222 +9,14 @@ #include <netinet/in.h> #include <rpm/rpmstring.h> #include <rpm/rpmlog.h> -#include <rpm/rpmbase64.h> #include "rpmio/digest.h" +#include "rpmio/rpmpgp.h" #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */ #include "debug.h" -static int _print = 0; - -/** \ingroup rpmio - * Values parsed from OpenPGP signature/pubkey packet(s). - */ -struct pgpDigParams_s { - char * userid; - uint8_t * hash; - uint8_t tag; - - uint8_t key_flags; /*!< key usage flags */ - uint8_t version; /*!< version number. */ - uint32_t time; /*!< key/signature creation time. */ - uint8_t pubkey_algo; /*!< public key algorithm. */ - - uint8_t hash_algo; - uint8_t sigtype; - uint32_t hashlen; - uint8_t signhash16[2]; - pgpKeyID_t signid; - uint8_t saved; /*!< Various flags. `PGPDIG_SAVED_*` are never reset. - * `PGPDIG_SIG_HAS_*` are reset for each signature. */ -#define PGPDIG_SAVED_TIME (1 << 0) -#define PGPDIG_SAVED_ID (1 << 1) -#define PGPDIG_SIG_HAS_CREATION_TIME (1 << 2) -#define PGPDIG_SIG_HAS_KEY_FLAGS (1 << 3) - - pgpDigAlg alg; -}; - -/** \ingroup rpmio - * Container for values parsed from an OpenPGP signature and public key. - */ -struct pgpDig_s { - struct pgpDigParams_s * signature; - struct pgpDigParams_s * pubkey; -}; - -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)" }, - { PGPPUBKEYALGO_EDDSA, "EdDSA" }, - { -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 const char * pgpValStr(pgpValTbl vs, uint8_t val) +const char * pgpValStr(pgpValTbl vs, uint8_t val) { do { if (vs->val == val) @@ -258,1053 +50,6 @@ const char * pgpValString(pgpValType type, uint8_t val) 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 = rpmhex(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); -} - -static void pgpPrtTime(const char * pre, const uint8_t *p, size_t plen) -{ - if (!_print) return; - if (pre && *pre) - fprintf(stderr, "%s", pre); - if (plen == 4) { - char buf[1024]; - time_t t = pgpGrab(p, plen); - struct tm _tm, *tms = localtime_r(&t, &_tm); - if (strftime(buf, sizeof(buf), "%c", tms) > 0) - fprintf(stderr, " %-24.24s(0x%08x)", buf, (unsigned)t); - } else { - pgpPrtHex("", p+1, plen-1); - } -} - -/** \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; -} - -/** \ingroup rpmpgp - * Decode length from 1, 2, or 5 octet body length encoding, used in - * new format packet headers and V4 signature subpackets. - * Partial body lengths are (intentionally) not supported. - * @param s pointer to length encoding buffer - * @param slen buffer size - * @param[out] *lenp decoded length - * @return no. of bytes used to encode the length, 0 on error - */ -static inline -size_t pgpLen(const uint8_t *s, size_t slen, size_t * lenp) -{ - size_t dlen = 0; - size_t lenlen = 0; - - /* - * Callers can only ensure we'll always have the first byte, beyond - * that the required size is not known until we decode it so we need - * to check if we have enough bytes to read the size as we go. - */ - if (*s < 192) { - lenlen = 1; - dlen = *s; - } else if (*s < 224 && slen > 2) { - lenlen = 2; - dlen = (((s[0]) - 192) << 8) + s[1] + 192; - } else if (*s == 255 && slen > 5) { - lenlen = 5; - dlen = pgpGrab(s+1, 4); - } - - if (slen - lenlen < dlen) - lenlen = 0; - - if (lenlen) - *lenp = dlen; - - return lenlen; -} - -struct pgpPkt { - uint8_t tag; /* decoded PGP tag */ - const uint8_t *head; /* pointer to start of packet (header) */ - const uint8_t *body; /* pointer to packet body */ - size_t blen; /* length of body in bytes */ -}; - -/** \ingroup rpmpgp - * Read a length field `nbytes` long. Checks that the buffer is big enough to - * hold `nbytes + *valp` bytes. - * @param s pointer to read from - * @param nbytes length of length field - * @param send pointer past end of buffer - * @param[out] *valp decoded length - * @return 0 if buffer can hold `nbytes + *valp` of data, - * otherwise -1. - */ -static int pgpGet(const uint8_t *s, size_t nbytes, const uint8_t *send, - size_t *valp) -{ - int rc = -1; - - *valp = 0; - if (nbytes <= 4 && send - s >= nbytes) { - unsigned int val = pgpGrab(s, nbytes); - if (send - s - nbytes >= val) { - rc = 0; - *valp = val; - } - } - - return rc; -} - -static int decodePkt(const uint8_t *p, size_t plen, struct pgpPkt *pkt) -{ - int rc = -1; /* assume failure */ - - /* Valid PGP packet header must always have two or more bytes in it */ - if (p && plen >= 2 && p[0] & 0x80) { - size_t lenlen = 0; - size_t hlen = 0; - - if (p[0] & 0x40) { - /* New format packet, body length encoding in second byte */ - lenlen = pgpLen(p+1, plen-1, &pkt->blen); - pkt->tag = (p[0] & 0x3f); - } else { - /* Old format packet, body length encoding in tag byte */ - lenlen = (1 << (p[0] & 0x3)); - /* Reject indefinite length packets and check bounds */ - if (pgpGet(p + 1, lenlen, p + plen, &pkt->blen)) - return rc; - pkt->tag = (p[0] >> 2) & 0xf; - } - hlen = lenlen + 1; - - /* Does the packet header and its body fit in our boundaries? */ - if (lenlen && (hlen + pkt->blen <= plen)) { - pkt->head = p; - pkt->body = pkt->head + hlen; - rc = 0; - } - } - - 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 pgpVersion(const uint8_t *h, size_t hlen, uint8_t *version) -{ - if (hlen < 1) - return -1; - - *version = h[0]; - return 0; -} - -int pgpSignatureType(pgpDigParams _digp) -{ - int rc = -1; - - if (_digp && _digp->tag == PGPTAG_SIGNATURE) - rc = _digp->sigtype; - - return rc; -} - -static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype, - pgpDigParams _digp, int hashed) -{ - const uint8_t *p = h; - size_t plen = 0, i; - int rc = 0; - - while (hlen > 0 && rc == 0) { - int impl = 0; - i = pgpLen(p, hlen, &plen); - if (i == 0 || plen < 1 || i + plen > hlen) - break; - - p += i; - hlen -= i; - - pgpPrtVal(" ", pgpSubTypeTbl, (p[0]&(~PGPSUBTYPE_CRITICAL))); - if (p[0] & PGPSUBTYPE_CRITICAL) - if (_print) - fprintf(stderr, " *CRITICAL*"); - switch (*p & ~PGPSUBTYPE_CRITICAL) { - 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: /* signature creation time */ - if (!hashed) - break; /* RFC 4880 §5.2.3.4 creation time MUST be hashed */ - if (plen-1 != sizeof(_digp->time)) - break; /* other lengths not understood */ - if (_digp->saved & PGPDIG_SIG_HAS_CREATION_TIME) - return 1; /* duplicate timestamps not allowed */ - impl = *p; - if (!(_digp->saved & PGPDIG_SAVED_TIME)) - _digp->time = pgpGrab(p+1, sizeof(_digp->time)); - _digp->saved |= PGPDIG_SAVED_TIME | PGPDIG_SIG_HAS_CREATION_TIME; - break; - case PGPSUBTYPE_SIG_EXPIRE_TIME: - case PGPSUBTYPE_KEY_EXPIRE_TIME: - pgpPrtTime(" ", p+1, plen-1); - break; - - case PGPSUBTYPE_ISSUER_KEYID: /* issuer key ID */ - impl = *p; - if (!(_digp->saved & PGPDIG_SAVED_ID) && - (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE)) - { - if (plen-1 != sizeof(_digp->signid)) - break; - _digp->saved |= PGPDIG_SAVED_ID; - memcpy(_digp->signid, p+1, sizeof(_digp->signid)); - } - case PGPSUBTYPE_KEY_FLAGS: /* Key usage flags */ - /* Subpackets in the unhashed section cannot be trusted */ - if (!hashed) - break; - /* Reject duplicate key usage flags */ - if (_digp->saved & PGPDIG_SIG_HAS_KEY_FLAGS) - return 1; - impl = *p; - _digp->saved |= PGPDIG_SIG_HAS_KEY_FLAGS; - _digp->key_flags = plen >= 2 ? p[1] : 0; - break; - 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_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(); - - if (!impl && (p[0] & PGPSUBTYPE_CRITICAL)) - rc = 1; - - p += plen; - hlen -= plen; - } - - if (hlen != 0) - rc = 1; - - return rc; -} - -pgpDigAlg pgpDigAlgFree(pgpDigAlg alg) -{ - if (alg) { - if (alg->free) - alg->free(alg); - free(alg); - } - return NULL; -} - -static int processMpis(const int mpis, pgpDigAlg sigalg, - const uint8_t *p, const uint8_t *const pend) -{ - int i = 0, rc = 1; /* assume failure */ - for (; i < mpis && pend - p >= 2; i++) { - unsigned int mpil = pgpMpiLen(p); - if (pend - p < mpil) - return rc; - if (sigalg && sigalg->setmpi(sigalg, i, p)) - return rc; - p += mpil; - } - - /* Does the size and number of MPI's match our expectations? */ - if (p == pend && i == mpis) - rc = 0; - return rc; -} - -static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, - const uint8_t *p, const uint8_t *h, size_t hlen, - pgpDigParams sigp) -{ - const uint8_t * pend = h + hlen; - pgpDigAlg sigalg = pgpSignatureNew(pubkey_algo); - - int rc = processMpis(sigalg->mpis, sigalg, p, pend); - - /* We can't handle more than one sig at a time */ - if (rc == 0 && sigp->alg == NULL && sigp->tag == PGPTAG_SIGNATURE) - sigp->alg = sigalg; - else - pgpDigAlgFree(sigalg); - - return rc; -} - -static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, - pgpDigParams _digp) -{ - uint8_t version = 0; - const uint8_t * p; - size_t plen; - int rc = 1; - - /* Reset the saved flags */ - _digp->saved &= PGPDIG_SAVED_TIME | PGPDIG_SAVED_ID; - _digp->key_flags = 0; - - if (pgpVersion(h, hlen, &version)) - return rc; - - switch (version) { - case 3: - { pgpPktSigV3 v = (pgpPktSigV3)h; - - if (hlen <= sizeof(*v) || v->hashlen != 5) - return 1; - - pgpPrtVal("V3 ", pgpTagTbl, tag); - pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); - pgpPrtVal(" ", pgpHashTbl, v->hash_algo); - pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); - pgpPrtNL(); - pgpPrtTime(" ", v->time, sizeof(v->time)); - 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->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); - if (!(_digp->saved & PGPDIG_SAVED_TIME)) - _digp->time = pgpGrab(v->time, sizeof(v->time)); - if (!(_digp->saved & PGPDIG_SAVED_ID)) - memcpy(_digp->signid, v->signid, sizeof(_digp->signid)); - _digp->saved = PGPDIG_SAVED_TIME | PGPDIG_SIG_HAS_CREATION_TIME | PGPDIG_SAVED_ID; - _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 = tag ? pgpPrtSigParams(tag, v->pubkey_algo, p, h, hlen, _digp) : 0; - } break; - case 4: - { pgpPktSigV4 v = (pgpPktSigV4)h; - const uint8_t *const hend = h + hlen; - - if (hlen <= sizeof(*v)) - return 1; - - pgpPrtVal("V4 ", pgpTagTbl, tag); - pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); - pgpPrtVal(" ", pgpHashTbl, v->hash_algo); - pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); - pgpPrtNL(); - - p = &v->hashlen[0]; - if (pgpGet(v->hashlen, sizeof(v->hashlen), hend, &plen)) - return 1; - p += sizeof(v->hashlen); - - if ((p + plen) > hend) - return 1; - - if (_digp->pubkey_algo == 0) { - _digp->hashlen = sizeof(*v) + plen; - _digp->hash = memcpy(xmalloc(_digp->hashlen), v, _digp->hashlen); - } - if (pgpPrtSubType(p, plen, v->sigtype, _digp, 1)) - return 1; - p += plen; - - if (!(_digp->saved & PGPDIG_SIG_HAS_CREATION_TIME)) - return 1; /* RFC 4880 §5.2.3.4 creation time MUST be hashed */ - - if (pgpGet(p, 2, hend, &plen)) - return 1; - p += 2; - - if ((p + plen) > hend) - return 1; - - if (pgpPrtSubType(p, plen, v->sigtype, _digp, 0)) - return 1; - p += plen; - - if (h + hlen - p < 2) - return 1; - pgpPrtHex(" signhash16", p, 2); - pgpPrtNL(); - - if (_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 > hend) - return 1; - - rc = tag ? pgpPrtSigParams(tag, v->pubkey_algo, p, h, hlen, _digp) : 0; - } break; - default: - rpmlog(RPMLOG_WARNING, _("Unsupported version of signature: V%d\n"), version); - rc = 1; - break; - } - return rc; -} - -static uint8_t curve_oids[] = { - PGPCURVE_NIST_P_256, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, - PGPCURVE_NIST_P_384, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, - PGPCURVE_NIST_P_521, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, - PGPCURVE_BRAINPOOL_P256R1, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07, - PGPCURVE_BRAINPOOL_P512R1, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0d, - PGPCURVE_ED25519, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01, - PGPCURVE_CURVE25519, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01, - 0, -}; - -static int pgpCurveByOid(const uint8_t *p, int l) -{ - uint8_t *curve; - for (curve = curve_oids; *curve; curve += 2 + curve[1]) - if (l == (int)curve[1] && !memcmp(p, curve + 2, l)) - return (int)curve[0]; - return 0; -} - -static int pgpPrtPubkeyParams(uint8_t pubkey_algo, - const uint8_t *p, const uint8_t *h, size_t hlen, - pgpDigParams keyp) -{ - int rc = 1; - const uint8_t *pend = h + hlen; - int curve = 0; - if (pubkey_algo == PGPPUBKEYALGO_EDDSA) { - int len = (hlen > 1) ? p[0] : 0; - if (len == 0 || len == 0xff || len >= hlen) - goto exit; - curve = pgpCurveByOid(p + 1, len); - p += len + 1; - } - pgpDigAlg keyalg = pgpPubkeyNew(pubkey_algo, curve); - rc = processMpis(keyalg->mpis, keyalg, p, pend); - - /* We can't handle more than one key at a time */ - if (rc == 0 && keyp->alg == NULL && (keyp->tag == PGPTAG_PUBLIC_KEY || - keyp->tag == PGPTAG_PUBLIC_SUBKEY)) - - keyp->alg = keyalg; - else - pgpDigAlgFree(keyalg); - -exit: - return rc; -} - -static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, - pgpDigParams _digp) -{ - uint8_t version = 0; - const uint8_t * p = NULL; - int rc = 1; - - if (pgpVersion(h, hlen, &version)) - return rc; - - /* We only permit V4 keys, V3 keys are long long since deprecated */ - switch (version) { - case 4: - { pgpPktKeyV4 v = (pgpPktKeyV4)h; - - if (hlen > sizeof(*v)) { - pgpPrtVal("V4 ", pgpTagTbl, tag); - pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); - pgpPrtTime(" ", v->time, sizeof(v->time)); - pgpPrtNL(); - - /* If _digp->hash is not NULL then signature is already loaded */ - if (_digp->hash == NULL) { - _digp->version = v->version; - _digp->time = pgpGrab(v->time, sizeof(v->time)); - _digp->pubkey_algo = v->pubkey_algo; - } - - p = ((uint8_t *)v) + sizeof(*v); - rc = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _digp); - } - } break; - default: - rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), h[0]); - } - 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(); - free(_digp->userid); - _digp->userid = memcpy(xmalloc(hlen+1), h, hlen); - _digp->userid[hlen] = '\0'; - return 0; -} - -int pgpPubkeyFingerprint(const uint8_t *h, size_t hlen, - uint8_t **fp, size_t *fplen) -{ - int rc = -1; /* assume failure */ - const uint8_t *se; - const uint8_t *pend = h + hlen; - uint8_t version = 0; - - if (pgpVersion(h, hlen, &version)) - return rc; - - /* We only permit V4 keys, V3 keys are long long since deprecated */ - switch (version) { - case 4: - { pgpPktKeyV4 v = (pgpPktKeyV4) (h); - int mpis = -1; - - /* Packet must be strictly larger than v to have room for the - * required MPIs and (for EdDSA) the curve ID */ - if (hlen < sizeof(*v) + sizeof(uint8_t)) - return rc; - se = (uint8_t *)(v + 1); - switch (v->pubkey_algo) { - case PGPPUBKEYALGO_EDDSA: - /* EdDSA has a curve id before the MPIs */ - if (se[0] == 0x00 || se[0] == 0xff || pend - se < 1 + se[0]) - return rc; - se += 1 + se[0]; - mpis = 1; - break; - case PGPPUBKEYALGO_RSA: - mpis = 2; - break; - case PGPPUBKEYALGO_DSA: - mpis = 4; - break; - default: - return rc; - } - - /* Does the size and number of MPI's match our expectations? */ - if (processMpis(mpis, NULL, se, pend) == 0) { - DIGEST_CTX ctx = rpmDigestInit(RPM_HASH_SHA1, RPMDIGEST_NONE); - uint8_t *d = NULL; - size_t dlen = 0; - uint8_t in[3] = { 0x99, (hlen >> 8), hlen }; - - (void) rpmDigestUpdate(ctx, in, 3); - (void) rpmDigestUpdate(ctx, h, hlen); - (void) rpmDigestFinal(ctx, (void **)&d, &dlen, 0); - - if (dlen == 20) { - rc = 0; - *fp = d; - *fplen = dlen; - } else { - free(d); - } - } - - } break; - default: - rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), version); - } - return rc; -} - -static int getKeyID(const uint8_t *h, size_t hlen, pgpKeyID_t keyid) -{ - uint8_t *fp = NULL; - size_t fplen = 0; - int rc = pgpPubkeyFingerprint(h, hlen, &fp, &fplen); - if (fp && fplen > 8) { - memcpy(keyid, (fp + (fplen-8)), 8); - free(fp); - } - return rc; -} - -int pgpPubkeyKeyID(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid) -{ - struct pgpPkt p; - - if (decodePkt(pkt, pktlen, &p)) - return -1; - - return getKeyID(p.body, p.blen, keyid); -} - -static int pgpPrtPkt(struct pgpPkt *p, pgpDigParams _digp) -{ - int rc = 0; - - switch (p->tag) { - case PGPTAG_SIGNATURE: - rc = pgpPrtSig(p->tag, p->body, p->blen, _digp); - break; - case PGPTAG_PUBLIC_KEY: - /* Get the public key Key ID. */ - rc = getKeyID(p->body, p->blen, _digp->signid); - if (rc) - memset(_digp->signid, 0, sizeof(_digp->signid)); - else { - _digp->saved |= PGPDIG_SAVED_ID; - rc = pgpPrtKey(p->tag, p->body, p->blen, _digp); - } - break; - case PGPTAG_USER_ID: - rc = pgpPrtUserID(p->tag, p->body, p->blen, _digp); - break; - case PGPTAG_RESERVED: - rc = -1; - break; - case PGPTAG_COMMENT: - case PGPTAG_COMMENT_OLD: - case PGPTAG_PUBLIC_SUBKEY: - case PGPTAG_SECRET_KEY: - case PGPTAG_SECRET_SUBKEY: - 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, p->tag); - pgpPrtHex("", p->body, p->blen); - pgpPrtNL(); - break; - } - - return rc; -} - -pgpDigParams pgpDigParamsFree(pgpDigParams digp) -{ - if (digp) { - pgpDigAlgFree(digp->alg); - free(digp->userid); - free(digp->hash); - memset(digp, 0, sizeof(*digp)); - free(digp); - } - return NULL; -} - -int pgpDigParamsCmp(pgpDigParams p1, pgpDigParams p2) -{ - int rc = 1; /* assume different, eg if either is NULL */ - if (p1 && p2) { - /* XXX Should we compare something else too? */ - if (p1->tag != p2->tag) - goto exit; - if (p1->hash_algo != p2->hash_algo) - goto exit; - if (p1->pubkey_algo != p2->pubkey_algo) - goto exit; - if (p1->version != p2->version) - goto exit; - if (p1->sigtype != p2->sigtype) - goto exit; - if (memcmp(p1->signid, p2->signid, sizeof(p1->signid)) != 0) - goto exit; - if (p1->userid && p2->userid && strcmp(p1->userid, p2->userid) != 0) - goto exit; - - /* Parameters match ... at least for our purposes */ - rc = 0; - } -exit: - return rc; -} - -unsigned int pgpDigParamsAlgo(pgpDigParams digp, unsigned int algotype) -{ - unsigned int algo = 0; /* assume failure */ - if (digp) { - switch (algotype) { - case PGPVAL_PUBKEYALGO: - algo = digp->pubkey_algo; - break; - case PGPVAL_HASHALGO: - algo = digp->hash_algo; - break; - } - } - return algo; -} - -const uint8_t *pgpDigParamsSignID(pgpDigParams digp) -{ - return digp->signid; -} - -const char *pgpDigParamsUserID(pgpDigParams digp) -{ - return digp->userid; -} - -int pgpDigParamsVersion(pgpDigParams digp) -{ - return digp->version; -} - -uint32_t pgpDigParamsCreationTime(pgpDigParams digp) -{ - return digp->time; -} - -static pgpDigParams pgpDigParamsNew(uint8_t tag) -{ - pgpDigParams digp = xcalloc(1, sizeof(*digp)); - digp->tag = tag; - return digp; -} - -static int hashKey(DIGEST_CTX hash, const struct pgpPkt *pkt, int exptag) -{ - int rc = -1; - if (pkt->tag == exptag) { - uint8_t head[] = { - 0x99, - (pkt->blen >> 8), - (pkt->blen ), - }; - - rpmDigestUpdate(hash, head, 3); - rpmDigestUpdate(hash, pkt->body, pkt->blen); - rc = 0; - } - return rc; -} - -static int pgpVerifySelf(pgpDigParams key, pgpDigParams selfsig, - const struct pgpPkt *all, int i) -{ - int rc = -1; - DIGEST_CTX hash = NULL; - - switch (selfsig->sigtype) { - case PGPSIGTYPE_SUBKEY_BINDING: - hash = rpmDigestInit(selfsig->hash_algo, 0); - if (hash) { - rc = hashKey(hash, &all[0], PGPTAG_PUBLIC_KEY); - if (!rc) - rc = hashKey(hash, &all[i-1], PGPTAG_PUBLIC_SUBKEY); - } - break; - default: - /* ignore types we can't handle */ - rc = 0; - break; - } - - if (hash && rc == 0) - rc = pgpVerifySignature(key, selfsig, hash); - - rpmDigestFinal(hash, NULL, NULL, 0); - - return rc; -} - -static int parseSubkeySig(const struct pgpPkt *pkt, uint8_t tag, - pgpDigParams *params_p) { - pgpDigParams params = *params_p = NULL; /* assume failure */ - - if (pkt->tag != PGPTAG_SIGNATURE) - goto fail; - - params = pgpDigParamsNew(tag); - - if (pgpPrtSig(tag, pkt->body, pkt->blen, params)) - goto fail; - - if (params->sigtype != PGPSIGTYPE_SUBKEY_BINDING && - params->sigtype != PGPSIGTYPE_SUBKEY_REVOKE) - { - goto fail; - } - - *params_p = params; - return 0; -fail: - pgpDigParamsFree(params); - return -1; -} - -static const size_t RPM_MAX_OPENPGP_BYTES = 65535; /* max number of bytes in a key */ - -int pgpPrtParams(const uint8_t * pkts, size_t pktlen, unsigned int pkttype, - pgpDigParams * ret) -{ - const uint8_t *p = pkts; - const uint8_t *pend = pkts + pktlen; - pgpDigParams digp = NULL; - pgpDigParams selfsig = NULL; - int i = 0; - int alloced = 16; /* plenty for normal cases */ - int rc = -1; /* assume failure */ - int expect = 0; - int prevtag = 0; - - if (pktlen > RPM_MAX_OPENPGP_BYTES) - return rc; /* reject absurdly large data */ - - struct pgpPkt *all = xmalloc(alloced * sizeof(*all)); - while (p < pend) { - struct pgpPkt *pkt = &all[i]; - if (decodePkt(p, (pend - p), pkt)) - break; - - if (digp == NULL) { - if (pkttype && pkt->tag != pkttype) { - break; - } else { - digp = pgpDigParamsNew(pkt->tag); - } - } - - if (expect) { - if (pkt->tag != expect) - break; - selfsig = pgpDigParamsNew(pkt->tag); - } - - if (pgpPrtPkt(pkt, selfsig ? selfsig : digp)) - break; - - if (selfsig) { - /* subkeys must be followed by binding signature */ - int xx = 1; /* assume failure */ - - if (!(prevtag == PGPTAG_PUBLIC_SUBKEY && - selfsig->sigtype != PGPSIGTYPE_SUBKEY_BINDING)) - xx = pgpVerifySelf(digp, selfsig, all, i); - - selfsig = pgpDigParamsFree(selfsig); - if (xx) - break; - expect = 0; - } - - if (pkt->tag == PGPTAG_PUBLIC_SUBKEY) - expect = PGPTAG_SIGNATURE; - prevtag = pkt->tag; - - i++; - p += (pkt->body - pkt->head) + pkt->blen; - if (pkttype == PGPTAG_SIGNATURE) - break; - - if (alloced <= i) { - alloced *= 2; - all = xrealloc(all, alloced * sizeof(*all)); - } - } - - rc = (digp && (p == pend) && expect == 0) ? 0 : -1; - - free(all); - selfsig = pgpDigParamsFree(selfsig); - if (ret && rc == 0) { - *ret = digp; - } else { - pgpDigParamsFree(digp); - } - return rc; -} - -int pgpPrtParamsSubkeys(const uint8_t *pkts, size_t pktlen, - pgpDigParams mainkey, pgpDigParams **subkeys, - int *subkeysCount) -{ - const uint8_t *p = pkts; - const uint8_t *pend = pkts + pktlen; - pgpDigParams *digps = NULL; - int count = 0; - int alloced = 10; - struct pgpPkt pkt; - int rc, i; - - digps = xmalloc(alloced * sizeof(*digps)); - - while (p < pend) { - if (decodePkt(p, (pend - p), &pkt)) - break; - - p += (pkt.body - pkt.head) + pkt.blen; - - if (pkt.tag == PGPTAG_PUBLIC_SUBKEY) { - if (count == alloced) { - alloced <<= 1; - digps = xrealloc(digps, alloced * sizeof(*digps)); - } - - digps[count] = pgpDigParamsNew(PGPTAG_PUBLIC_SUBKEY); - /* Copy UID from main key to subkey */ - digps[count]->userid = xstrdup(mainkey->userid); - - if (getKeyID(pkt.body, pkt.blen, digps[count]->signid)) { - pgpDigParamsFree(digps[count]); - continue; - } - - if (pgpPrtKey(pkt.tag, pkt.body, pkt.blen, digps[count])) { - pgpDigParamsFree(digps[count]); - continue; - } - - pgpDigParams subkey_sig = NULL; - if (decodePkt(p, pend - p, &pkt) || - parseSubkeySig(&pkt, 0, &subkey_sig)) - { - pgpDigParamsFree(digps[count]); - break; - } - - /* Is the subkey revoked or incapable of signing? */ - int ignore = subkey_sig->sigtype != PGPSIGTYPE_SUBKEY_BINDING || - !((subkey_sig->saved & PGPDIG_SIG_HAS_KEY_FLAGS) && - (subkey_sig->key_flags & 0x02)); - if (ignore) { - pgpDigParamsFree(digps[count]); - } else { - digps[count]->key_flags = subkey_sig->key_flags; - digps[count]->saved |= PGPDIG_SIG_HAS_KEY_FLAGS; - count++; - } - p += (pkt.body - pkt.head) + pkt.blen; - pgpDigParamsFree(subkey_sig); - } - } - rc = (p == pend) ? 0 : -1; - - if (rc == 0) { - *subkeys = xrealloc(digps, count * sizeof(*digps)); - *subkeysCount = count; - } else { - for (i = 0; i < count; i++) - pgpDigParamsFree(digps[i]); - free(digps); - } - - return rc; -} - char *pgpIdentItem(pgpDigParams digp) { char *id = NULL; @@ -1325,193 +70,6 @@ char *pgpIdentItem(pgpDigParams digp) return id; } -rpmRC pgpVerifySignature(pgpDigParams key, pgpDigParams sig, DIGEST_CTX hashctx) -{ - DIGEST_CTX ctx = rpmDigestDup(hashctx); - uint8_t *hash = NULL; - size_t hashlen = 0; - rpmRC res = RPMRC_FAIL; /* assume failure */ - - if (sig == NULL || ctx == NULL) - goto exit; - - if (sig->hash != NULL) - rpmDigestUpdate(ctx, sig->hash, sig->hashlen); - - if (sig->version == 4) { - /* V4 trailer is six octets long (rfc4880) */ - uint8_t trailer[6]; - uint32_t nb = sig->hashlen; - nb = htonl(nb); - trailer[0] = sig->version; - trailer[1] = 0xff; - memcpy(trailer+2, &nb, 4); - rpmDigestUpdate(ctx, trailer, sizeof(trailer)); - } - - rpmDigestFinal(ctx, (void **)&hash, &hashlen, 0); - ctx = NULL; - - /* Compare leading 16 bits of digest for quick check. */ - if (hash == NULL || memcmp(hash, sig->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 (key && key->alg) { - pgpDigAlg sa = sig->alg; - pgpDigAlg ka = key->alg; - if (sa && sa->verify) { - if (sa->verify(ka, sa, hash, hashlen, sig->hash_algo) == 0) { - res = RPMRC_OK; - } - } - } else { - res = RPMRC_NOKEY; - } - -exit: - free(hash); - rpmDigestFinal(ctx, NULL, NULL, 0); - 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 */ - -#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); - /* Handle EOF without EOL here, *t == '\0' at EOF */ - if (*t && (t >= te)) continue; - /* XXX permitting \r here is not RFC-2440 compliant <shrug> */ - if (!(*t == '\n' || *t == '\r' || *t == '\0')) continue; - - crcdec = NULL; - crclen = 0; - if (rpmBase64Decode(crcenc, (void **)&crcdec, &crclen) != 0 || crclen != 3) { - crcdec = _free(crcdec); - ec = PGPARMOR_ERR_CRC_DECODE; - goto exit; - } - crcpkt = pgpGrab(crcdec, crclen); - crcdec = _free(crcdec); - dec = NULL; - declen = 0; - if (rpmBase64Decode(enc, (void **)&dec, &declen) != 0) { - ec = PGPARMOR_ERR_BODY_DECODE; - goto exit; - } - crc = pgpCRC(dec, declen); - if (crcpkt != crc) { - ec = PGPARMOR_ERR_CRC_CHECK; - _free(dec); - goto exit; - } - if (pkt) - *pkt = dec; - else - _free(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; @@ -1519,64 +77,8 @@ pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen) 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); + ec = pgpParsePkts((const char *) 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; -} - -int pgpPubKeyCertLen(const uint8_t *pkts, size_t pktslen, size_t *certlen) -{ - const uint8_t *p = pkts; - const uint8_t *pend = pkts + pktslen; - struct pgpPkt pkt; - - while (p < pend) { - if (decodePkt(p, (pend - p), &pkt)) - return -1; - - if (pkt.tag == PGPTAG_PUBLIC_KEY && pkts != p) { - *certlen = p - pkts; - return 0; - } - - p += (pkt.body - pkt.head) + pkt.blen; - } - - *certlen = pktslen; - - return 0; -} - -char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns) -{ - char *buf = NULL, *val = NULL; - char *enc = rpmBase64Encode(s, ns, -1); - char *crc = rpmBase64CRC(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"\n\n" - "%s\n-----END PGP %s-----\n", - valstr, buf != NULL ? buf : "", valstr); - - free(buf); - return val; -} - diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h new file mode 100644 index 000000000..0641f0803 --- /dev/null +++ b/rpmio/rpmpgp.h @@ -0,0 +1,173 @@ +#ifndef _RPMPGP_INTERNAL_H +#define _RPMPGP_INTERNAL_H + +#include <rpm/rpmpgp.h> + +typedef const struct pgpValTbl_s { + int val; + char const * const str; +} * pgpValTbl; + +const char * pgpValStr(pgpValTbl vs, uint8_t val); + + +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)" }, + { PGPPUBKEYALGO_EDDSA, "EdDSA" }, + { -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" } +}; + +#endif /* _RPMPGP_INTERNAL_H */ diff --git a/rpmio/rpmpgp_internal.c b/rpmio/rpmpgp_internal.c new file mode 100644 index 000000000..a4edefc8b --- /dev/null +++ b/rpmio/rpmpgp_internal.c @@ -0,0 +1,1344 @@ +/** \ingroup rpmio signature + * \file rpmio/rpmpgp_internal.c + * Routines to handle RFC-2440 detached signatures. + */ + +#include "system.h" + +#include <time.h> +#include <netinet/in.h> +#include <rpm/rpmstring.h> +#include <rpm/rpmlog.h> +#include <rpm/rpmbase64.h> + +#include "rpmio/digest.h" +#include "rpmio/rpmpgp.h" +#include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */ + +#include "debug.h" + +static int _print = 0; + +/** \ingroup rpmio + * Values parsed from OpenPGP signature/pubkey packet(s). + */ +struct pgpDigParams_s { + char * userid; + uint8_t * hash; + uint8_t tag; + + uint8_t key_flags; /*!< key usage flags */ + uint8_t version; /*!< version number. */ + uint32_t time; /*!< key/signature creation time. */ + uint8_t pubkey_algo; /*!< public key algorithm. */ + + uint8_t hash_algo; + uint8_t sigtype; + uint32_t hashlen; + uint8_t signhash16[2]; + pgpKeyID_t signid; + uint8_t saved; /*!< Various flags. `PGPDIG_SAVED_*` are never reset. + * `PGPDIG_SIG_HAS_*` are reset for each signature. */ +#define PGPDIG_SAVED_TIME (1 << 0) +#define PGPDIG_SAVED_ID (1 << 1) +#define PGPDIG_SIG_HAS_CREATION_TIME (1 << 2) +#define PGPDIG_SIG_HAS_KEY_FLAGS (1 << 3) + + pgpDigAlg alg; +}; + +static void pgpPrtNL(void) +{ + if (!_print) return; + fprintf(stderr, "\n"); +} + +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 = rpmhex(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); +} + +static void pgpPrtTime(const char * pre, const uint8_t *p, size_t plen) +{ + if (!_print) return; + if (pre && *pre) + fprintf(stderr, "%s", pre); + if (plen == 4) { + char buf[1024]; + time_t t = pgpGrab(p, plen); + struct tm _tm, *tms = localtime_r(&t, &_tm); + if (strftime(buf, sizeof(buf), "%c", tms) > 0) + fprintf(stderr, " %-24.24s(0x%08x)", buf, (unsigned)t); + } else { + pgpPrtHex("", p+1, plen-1); + } +} + +/** \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; +} + +/** \ingroup rpmpgp + * Decode length from 1, 2, or 5 octet body length encoding, used in + * new format packet headers and V4 signature subpackets. + * Partial body lengths are (intentionally) not supported. + * @param s pointer to length encoding buffer + * @param slen buffer size + * @param[out] *lenp decoded length + * @return no. of bytes used to encode the length, 0 on error + */ +static inline +size_t pgpLen(const uint8_t *s, size_t slen, size_t * lenp) +{ + size_t dlen = 0; + size_t lenlen = 0; + + /* + * Callers can only ensure we'll always have the first byte, beyond + * that the required size is not known until we decode it so we need + * to check if we have enough bytes to read the size as we go. + */ + if (*s < 192) { + lenlen = 1; + dlen = *s; + } else if (*s < 224 && slen > 2) { + lenlen = 2; + dlen = (((s[0]) - 192) << 8) + s[1] + 192; + } else if (*s == 255 && slen > 5) { + lenlen = 5; + dlen = pgpGrab(s+1, 4); + } + + if (slen - lenlen < dlen) + lenlen = 0; + + if (lenlen) + *lenp = dlen; + + return lenlen; +} + +struct pgpPkt { + uint8_t tag; /* decoded PGP tag */ + const uint8_t *head; /* pointer to start of packet (header) */ + const uint8_t *body; /* pointer to packet body */ + size_t blen; /* length of body in bytes */ +}; + +/** \ingroup rpmpgp + * Read a length field `nbytes` long. Checks that the buffer is big enough to + * hold `nbytes + *valp` bytes. + * @param s pointer to read from + * @param nbytes length of length field + * @param send pointer past end of buffer + * @param[out] *valp decoded length + * @return 0 if buffer can hold `nbytes + *valp` of data, + * otherwise -1. + */ +static int pgpGet(const uint8_t *s, size_t nbytes, const uint8_t *send, + size_t *valp) +{ + int rc = -1; + + *valp = 0; + if (nbytes <= 4 && send - s >= nbytes) { + unsigned int val = pgpGrab(s, nbytes); + if (send - s - nbytes >= val) { + rc = 0; + *valp = val; + } + } + + return rc; +} + +static int decodePkt(const uint8_t *p, size_t plen, struct pgpPkt *pkt) +{ + int rc = -1; /* assume failure */ + + /* Valid PGP packet header must always have two or more bytes in it */ + if (p && plen >= 2 && p[0] & 0x80) { + size_t lenlen = 0; + size_t hlen = 0; + + if (p[0] & 0x40) { + /* New format packet, body length encoding in second byte */ + lenlen = pgpLen(p+1, plen-1, &pkt->blen); + pkt->tag = (p[0] & 0x3f); + } else { + /* Old format packet, body length encoding in tag byte */ + lenlen = (1 << (p[0] & 0x3)); + /* Reject indefinite length packets and check bounds */ + if (pgpGet(p + 1, lenlen, p + plen, &pkt->blen)) + return rc; + pkt->tag = (p[0] >> 2) & 0xf; + } + hlen = lenlen + 1; + + /* Does the packet header and its body fit in our boundaries? */ + if (lenlen && (hlen + pkt->blen <= plen)) { + pkt->head = p; + pkt->body = pkt->head + hlen; + rc = 0; + } + } + + 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 pgpVersion(const uint8_t *h, size_t hlen, uint8_t *version) +{ + if (hlen < 1) + return -1; + + *version = h[0]; + return 0; +} + +int pgpSignatureType(pgpDigParams _digp) +{ + int rc = -1; + + if (_digp && _digp->tag == PGPTAG_SIGNATURE) + rc = _digp->sigtype; + + return rc; +} + +static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype, + pgpDigParams _digp, int hashed) +{ + const uint8_t *p = h; + size_t plen = 0, i; + int rc = 0; + + while (hlen > 0 && rc == 0) { + int impl = 0; + i = pgpLen(p, hlen, &plen); + if (i == 0 || plen < 1 || i + plen > hlen) + break; + + p += i; + hlen -= i; + + pgpPrtVal(" ", pgpSubTypeTbl, (p[0]&(~PGPSUBTYPE_CRITICAL))); + if (p[0] & PGPSUBTYPE_CRITICAL) + if (_print) + fprintf(stderr, " *CRITICAL*"); + switch (*p & ~PGPSUBTYPE_CRITICAL) { + 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: /* signature creation time */ + if (!hashed) + break; /* RFC 4880 §5.2.3.4 creation time MUST be hashed */ + if (plen-1 != sizeof(_digp->time)) + break; /* other lengths not understood */ + if (_digp->saved & PGPDIG_SIG_HAS_CREATION_TIME) + return 1; /* duplicate timestamps not allowed */ + impl = *p; + if (!(_digp->saved & PGPDIG_SAVED_TIME)) + _digp->time = pgpGrab(p+1, sizeof(_digp->time)); + _digp->saved |= PGPDIG_SAVED_TIME | PGPDIG_SIG_HAS_CREATION_TIME; + break; + case PGPSUBTYPE_SIG_EXPIRE_TIME: + case PGPSUBTYPE_KEY_EXPIRE_TIME: + pgpPrtTime(" ", p+1, plen-1); + break; + + case PGPSUBTYPE_ISSUER_KEYID: /* issuer key ID */ + impl = *p; + if (!(_digp->saved & PGPDIG_SAVED_ID) && + (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE)) + { + if (plen-1 != sizeof(_digp->signid)) + break; + _digp->saved |= PGPDIG_SAVED_ID; + memcpy(_digp->signid, p+1, sizeof(_digp->signid)); + } + case PGPSUBTYPE_KEY_FLAGS: /* Key usage flags */ + /* Subpackets in the unhashed section cannot be trusted */ + if (!hashed) + break; + /* Reject duplicate key usage flags */ + if (_digp->saved & PGPDIG_SIG_HAS_KEY_FLAGS) + return 1; + impl = *p; + _digp->saved |= PGPDIG_SIG_HAS_KEY_FLAGS; + _digp->key_flags = plen >= 2 ? p[1] : 0; + break; + 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_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(); + + if (!impl && (p[0] & PGPSUBTYPE_CRITICAL)) + rc = 1; + + p += plen; + hlen -= plen; + } + + if (hlen != 0) + rc = 1; + + return rc; +} + +pgpDigAlg pgpDigAlgFree(pgpDigAlg alg) +{ + if (alg) { + if (alg->free) + alg->free(alg); + free(alg); + } + return NULL; +} + +static int processMpis(const int mpis, pgpDigAlg sigalg, + const uint8_t *p, const uint8_t *const pend) +{ + int i = 0, rc = 1; /* assume failure */ + for (; i < mpis && pend - p >= 2; i++) { + unsigned int mpil = pgpMpiLen(p); + if (pend - p < mpil) + return rc; + if (sigalg && sigalg->setmpi(sigalg, i, p)) + return rc; + p += mpil; + } + + /* Does the size and number of MPI's match our expectations? */ + if (p == pend && i == mpis) + rc = 0; + return rc; +} + +static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, + const uint8_t *p, const uint8_t *h, size_t hlen, + pgpDigParams sigp) +{ + const uint8_t * pend = h + hlen; + pgpDigAlg sigalg = pgpSignatureNew(pubkey_algo); + + int rc = processMpis(sigalg->mpis, sigalg, p, pend); + + /* We can't handle more than one sig at a time */ + if (rc == 0 && sigp->alg == NULL && sigp->tag == PGPTAG_SIGNATURE) + sigp->alg = sigalg; + else + pgpDigAlgFree(sigalg); + + return rc; +} + +static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, + pgpDigParams _digp) +{ + uint8_t version = 0; + const uint8_t * p; + size_t plen; + int rc = 1; + + /* Reset the saved flags */ + _digp->saved &= PGPDIG_SAVED_TIME | PGPDIG_SAVED_ID; + _digp->key_flags = 0; + + if (pgpVersion(h, hlen, &version)) + return rc; + + switch (version) { + case 3: + { pgpPktSigV3 v = (pgpPktSigV3)h; + + if (hlen <= sizeof(*v) || v->hashlen != 5) + return 1; + + pgpPrtVal("V3 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + pgpPrtVal(" ", pgpHashTbl, v->hash_algo); + pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); + pgpPrtNL(); + pgpPrtTime(" ", v->time, sizeof(v->time)); + 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->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); + if (!(_digp->saved & PGPDIG_SAVED_TIME)) + _digp->time = pgpGrab(v->time, sizeof(v->time)); + if (!(_digp->saved & PGPDIG_SAVED_ID)) + memcpy(_digp->signid, v->signid, sizeof(_digp->signid)); + _digp->saved = PGPDIG_SAVED_TIME | PGPDIG_SIG_HAS_CREATION_TIME | PGPDIG_SAVED_ID; + _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 = tag ? pgpPrtSigParams(tag, v->pubkey_algo, p, h, hlen, _digp) : 0; + } break; + case 4: + { pgpPktSigV4 v = (pgpPktSigV4)h; + const uint8_t *const hend = h + hlen; + + if (hlen <= sizeof(*v)) + return 1; + + pgpPrtVal("V4 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + pgpPrtVal(" ", pgpHashTbl, v->hash_algo); + pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); + pgpPrtNL(); + + p = &v->hashlen[0]; + if (pgpGet(v->hashlen, sizeof(v->hashlen), hend, &plen)) + return 1; + p += sizeof(v->hashlen); + + if ((p + plen) > hend) + return 1; + + if (_digp->pubkey_algo == 0) { + _digp->hashlen = sizeof(*v) + plen; + _digp->hash = memcpy(xmalloc(_digp->hashlen), v, _digp->hashlen); + } + if (pgpPrtSubType(p, plen, v->sigtype, _digp, 1)) + return 1; + p += plen; + + if (!(_digp->saved & PGPDIG_SIG_HAS_CREATION_TIME)) + return 1; /* RFC 4880 §5.2.3.4 creation time MUST be hashed */ + + if (pgpGet(p, 2, hend, &plen)) + return 1; + p += 2; + + if ((p + plen) > hend) + return 1; + + if (pgpPrtSubType(p, plen, v->sigtype, _digp, 0)) + return 1; + p += plen; + + if (h + hlen - p < 2) + return 1; + pgpPrtHex(" signhash16", p, 2); + pgpPrtNL(); + + if (_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 > hend) + return 1; + + rc = tag ? pgpPrtSigParams(tag, v->pubkey_algo, p, h, hlen, _digp) : 0; + } break; + default: + rpmlog(RPMLOG_WARNING, _("Unsupported version of signature: V%d\n"), version); + rc = 1; + break; + } + return rc; +} + +static uint8_t curve_oids[] = { + PGPCURVE_NIST_P_256, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, + PGPCURVE_NIST_P_384, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, + PGPCURVE_NIST_P_521, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, + PGPCURVE_BRAINPOOL_P256R1, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07, + PGPCURVE_BRAINPOOL_P512R1, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0d, + PGPCURVE_ED25519, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01, + PGPCURVE_CURVE25519, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01, + 0, +}; + +static int pgpCurveByOid(const uint8_t *p, int l) +{ + uint8_t *curve; + for (curve = curve_oids; *curve; curve += 2 + curve[1]) + if (l == (int)curve[1] && !memcmp(p, curve + 2, l)) + return (int)curve[0]; + return 0; +} + +static int pgpPrtPubkeyParams(uint8_t pubkey_algo, + const uint8_t *p, const uint8_t *h, size_t hlen, + pgpDigParams keyp) +{ + int rc = 1; + const uint8_t *pend = h + hlen; + int curve = 0; + if (pubkey_algo == PGPPUBKEYALGO_EDDSA) { + int len = (hlen > 1) ? p[0] : 0; + if (len == 0 || len == 0xff || len >= hlen) + goto exit; + curve = pgpCurveByOid(p + 1, len); + p += len + 1; + } + pgpDigAlg keyalg = pgpPubkeyNew(pubkey_algo, curve); + rc = processMpis(keyalg->mpis, keyalg, p, pend); + + /* We can't handle more than one key at a time */ + if (rc == 0 && keyp->alg == NULL && (keyp->tag == PGPTAG_PUBLIC_KEY || + keyp->tag == PGPTAG_PUBLIC_SUBKEY)) + + keyp->alg = keyalg; + else + pgpDigAlgFree(keyalg); + +exit: + return rc; +} + +static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, + pgpDigParams _digp) +{ + uint8_t version = 0; + const uint8_t * p = NULL; + int rc = 1; + + if (pgpVersion(h, hlen, &version)) + return rc; + + /* We only permit V4 keys, V3 keys are long long since deprecated */ + switch (version) { + case 4: + { pgpPktKeyV4 v = (pgpPktKeyV4)h; + + if (hlen > sizeof(*v)) { + pgpPrtVal("V4 ", pgpTagTbl, tag); + pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); + pgpPrtTime(" ", v->time, sizeof(v->time)); + pgpPrtNL(); + + /* If _digp->hash is not NULL then signature is already loaded */ + if (_digp->hash == NULL) { + _digp->version = v->version; + _digp->time = pgpGrab(v->time, sizeof(v->time)); + _digp->pubkey_algo = v->pubkey_algo; + } + + p = ((uint8_t *)v) + sizeof(*v); + rc = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _digp); + } + } break; + default: + rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), h[0]); + } + 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(); + free(_digp->userid); + _digp->userid = memcpy(xmalloc(hlen+1), h, hlen); + _digp->userid[hlen] = '\0'; + return 0; +} + +int pgpPubkeyFingerprint(const uint8_t *h, size_t hlen, + uint8_t **fp, size_t *fplen) +{ + int rc = -1; /* assume failure */ + const uint8_t *se; + const uint8_t *pend = h + hlen; + uint8_t version = 0; + + if (pgpVersion(h, hlen, &version)) + return rc; + + /* We only permit V4 keys, V3 keys are long long since deprecated */ + switch (version) { + case 4: + { pgpPktKeyV4 v = (pgpPktKeyV4) (h); + int mpis = -1; + + /* Packet must be strictly larger than v to have room for the + * required MPIs and (for EdDSA) the curve ID */ + if (hlen < sizeof(*v) + sizeof(uint8_t)) + return rc; + se = (uint8_t *)(v + 1); + switch (v->pubkey_algo) { + case PGPPUBKEYALGO_EDDSA: + /* EdDSA has a curve id before the MPIs */ + if (se[0] == 0x00 || se[0] == 0xff || pend - se < 1 + se[0]) + return rc; + se += 1 + se[0]; + mpis = 1; + break; + case PGPPUBKEYALGO_RSA: + mpis = 2; + break; + case PGPPUBKEYALGO_DSA: + mpis = 4; + break; + default: + return rc; + } + + /* Does the size and number of MPI's match our expectations? */ + if (processMpis(mpis, NULL, se, pend) == 0) { + DIGEST_CTX ctx = rpmDigestInit(RPM_HASH_SHA1, RPMDIGEST_NONE); + uint8_t *d = NULL; + size_t dlen = 0; + uint8_t in[3] = { 0x99, (hlen >> 8), hlen }; + + (void) rpmDigestUpdate(ctx, in, 3); + (void) rpmDigestUpdate(ctx, h, hlen); + (void) rpmDigestFinal(ctx, (void **)&d, &dlen, 0); + + if (dlen == 20) { + rc = 0; + *fp = d; + *fplen = dlen; + } else { + free(d); + } + } + + } break; + default: + rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), version); + } + return rc; +} + +static int getKeyID(const uint8_t *h, size_t hlen, pgpKeyID_t keyid) +{ + uint8_t *fp = NULL; + size_t fplen = 0; + int rc = pgpPubkeyFingerprint(h, hlen, &fp, &fplen); + if (fp && fplen > 8) { + memcpy(keyid, (fp + (fplen-8)), 8); + free(fp); + } + return rc; +} + +int pgpPubkeyKeyID(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid) +{ + struct pgpPkt p; + + if (decodePkt(pkt, pktlen, &p)) + return -1; + + return getKeyID(p.body, p.blen, keyid); +} + +static int pgpPrtPkt(struct pgpPkt *p, pgpDigParams _digp) +{ + int rc = 0; + + switch (p->tag) { + case PGPTAG_SIGNATURE: + rc = pgpPrtSig(p->tag, p->body, p->blen, _digp); + break; + case PGPTAG_PUBLIC_KEY: + /* Get the public key Key ID. */ + rc = getKeyID(p->body, p->blen, _digp->signid); + if (rc) + memset(_digp->signid, 0, sizeof(_digp->signid)); + else { + _digp->saved |= PGPDIG_SAVED_ID; + rc = pgpPrtKey(p->tag, p->body, p->blen, _digp); + } + break; + case PGPTAG_USER_ID: + rc = pgpPrtUserID(p->tag, p->body, p->blen, _digp); + break; + case PGPTAG_RESERVED: + rc = -1; + break; + case PGPTAG_COMMENT: + case PGPTAG_COMMENT_OLD: + case PGPTAG_PUBLIC_SUBKEY: + case PGPTAG_SECRET_KEY: + case PGPTAG_SECRET_SUBKEY: + 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, p->tag); + pgpPrtHex("", p->body, p->blen); + pgpPrtNL(); + break; + } + + return rc; +} + +pgpDigParams pgpDigParamsFree(pgpDigParams digp) +{ + if (digp) { + pgpDigAlgFree(digp->alg); + free(digp->userid); + free(digp->hash); + memset(digp, 0, sizeof(*digp)); + free(digp); + } + return NULL; +} + +int pgpDigParamsCmp(pgpDigParams p1, pgpDigParams p2) +{ + int rc = 1; /* assume different, eg if either is NULL */ + if (p1 && p2) { + /* XXX Should we compare something else too? */ + if (p1->tag != p2->tag) + goto exit; + if (p1->hash_algo != p2->hash_algo) + goto exit; + if (p1->pubkey_algo != p2->pubkey_algo) + goto exit; + if (p1->version != p2->version) + goto exit; + if (p1->sigtype != p2->sigtype) + goto exit; + if (memcmp(p1->signid, p2->signid, sizeof(p1->signid)) != 0) + goto exit; + if (p1->userid && p2->userid && strcmp(p1->userid, p2->userid) != 0) + goto exit; + + /* Parameters match ... at least for our purposes */ + rc = 0; + } +exit: + return rc; +} + +unsigned int pgpDigParamsAlgo(pgpDigParams digp, unsigned int algotype) +{ + unsigned int algo = 0; /* assume failure */ + if (digp) { + switch (algotype) { + case PGPVAL_PUBKEYALGO: + algo = digp->pubkey_algo; + break; + case PGPVAL_HASHALGO: + algo = digp->hash_algo; + break; + } + } + return algo; +} + +const uint8_t *pgpDigParamsSignID(pgpDigParams digp) +{ + return digp->signid; +} + +const char *pgpDigParamsUserID(pgpDigParams digp) +{ + return digp->userid; +} + +int pgpDigParamsVersion(pgpDigParams digp) +{ + return digp->version; +} + +uint32_t pgpDigParamsCreationTime(pgpDigParams digp) +{ + return digp->time; +} + +static pgpDigParams pgpDigParamsNew(uint8_t tag) +{ + pgpDigParams digp = xcalloc(1, sizeof(*digp)); + digp->tag = tag; + return digp; +} + +static int hashKey(DIGEST_CTX hash, const struct pgpPkt *pkt, int exptag) +{ + int rc = -1; + if (pkt->tag == exptag) { + uint8_t head[] = { + 0x99, + (pkt->blen >> 8), + (pkt->blen ), + }; + + rpmDigestUpdate(hash, head, 3); + rpmDigestUpdate(hash, pkt->body, pkt->blen); + rc = 0; + } + return rc; +} + +static int pgpVerifySelf(pgpDigParams key, pgpDigParams selfsig, + const struct pgpPkt *all, int i) +{ + int rc = -1; + DIGEST_CTX hash = NULL; + + switch (selfsig->sigtype) { + case PGPSIGTYPE_SUBKEY_BINDING: + hash = rpmDigestInit(selfsig->hash_algo, 0); + if (hash) { + rc = hashKey(hash, &all[0], PGPTAG_PUBLIC_KEY); + if (!rc) + rc = hashKey(hash, &all[i-1], PGPTAG_PUBLIC_SUBKEY); + } + break; + default: + /* ignore types we can't handle */ + rc = 0; + break; + } + + if (hash && rc == 0) + rc = pgpVerifySignature(key, selfsig, hash); + + rpmDigestFinal(hash, NULL, NULL, 0); + + return rc; +} + +static int parseSubkeySig(const struct pgpPkt *pkt, uint8_t tag, + pgpDigParams *params_p) { + pgpDigParams params = *params_p = NULL; /* assume failure */ + + if (pkt->tag != PGPTAG_SIGNATURE) + goto fail; + + params = pgpDigParamsNew(tag); + + if (pgpPrtSig(tag, pkt->body, pkt->blen, params)) + goto fail; + + if (params->sigtype != PGPSIGTYPE_SUBKEY_BINDING && + params->sigtype != PGPSIGTYPE_SUBKEY_REVOKE) + { + goto fail; + } + + *params_p = params; + return 0; +fail: + pgpDigParamsFree(params); + return -1; +} + +static const size_t RPM_MAX_OPENPGP_BYTES = 65535; /* max number of bytes in a key */ + +int pgpPrtParams(const uint8_t * pkts, size_t pktlen, unsigned int pkttype, + pgpDigParams * ret) +{ + const uint8_t *p = pkts; + const uint8_t *pend = pkts + pktlen; + pgpDigParams digp = NULL; + pgpDigParams selfsig = NULL; + int i = 0; + int alloced = 16; /* plenty for normal cases */ + int rc = -1; /* assume failure */ + int expect = 0; + int prevtag = 0; + + if (pktlen > RPM_MAX_OPENPGP_BYTES) + return rc; /* reject absurdly large data */ + + struct pgpPkt *all = xmalloc(alloced * sizeof(*all)); + while (p < pend) { + struct pgpPkt *pkt = &all[i]; + if (decodePkt(p, (pend - p), pkt)) + break; + + if (digp == NULL) { + if (pkttype && pkt->tag != pkttype) { + break; + } else { + digp = pgpDigParamsNew(pkt->tag); + } + } + + if (expect) { + if (pkt->tag != expect) + break; + selfsig = pgpDigParamsNew(pkt->tag); + } + + if (pgpPrtPkt(pkt, selfsig ? selfsig : digp)) + break; + + if (selfsig) { + /* subkeys must be followed by binding signature */ + int xx = 1; /* assume failure */ + + if (!(prevtag == PGPTAG_PUBLIC_SUBKEY && + selfsig->sigtype != PGPSIGTYPE_SUBKEY_BINDING)) + xx = pgpVerifySelf(digp, selfsig, all, i); + + selfsig = pgpDigParamsFree(selfsig); + if (xx) + break; + expect = 0; + } + + if (pkt->tag == PGPTAG_PUBLIC_SUBKEY) + expect = PGPTAG_SIGNATURE; + prevtag = pkt->tag; + + i++; + p += (pkt->body - pkt->head) + pkt->blen; + if (pkttype == PGPTAG_SIGNATURE) + break; + + if (alloced <= i) { + alloced *= 2; + all = xrealloc(all, alloced * sizeof(*all)); + } + } + + rc = (digp && (p == pend) && expect == 0) ? 0 : -1; + + free(all); + selfsig = pgpDigParamsFree(selfsig); + if (ret && rc == 0) { + *ret = digp; + } else { + pgpDigParamsFree(digp); + } + return rc; +} + +int pgpPrtParamsSubkeys(const uint8_t *pkts, size_t pktlen, + pgpDigParams mainkey, pgpDigParams **subkeys, + int *subkeysCount) +{ + const uint8_t *p = pkts; + const uint8_t *pend = pkts + pktlen; + pgpDigParams *digps = NULL; + int count = 0; + int alloced = 10; + struct pgpPkt pkt; + int rc, i; + + digps = xmalloc(alloced * sizeof(*digps)); + + while (p < pend) { + if (decodePkt(p, (pend - p), &pkt)) + break; + + p += (pkt.body - pkt.head) + pkt.blen; + + if (pkt.tag == PGPTAG_PUBLIC_SUBKEY) { + if (count == alloced) { + alloced <<= 1; + digps = xrealloc(digps, alloced * sizeof(*digps)); + } + + digps[count] = pgpDigParamsNew(PGPTAG_PUBLIC_SUBKEY); + /* Copy UID from main key to subkey */ + digps[count]->userid = xstrdup(mainkey->userid); + + if (getKeyID(pkt.body, pkt.blen, digps[count]->signid)) { + pgpDigParamsFree(digps[count]); + continue; + } + + if (pgpPrtKey(pkt.tag, pkt.body, pkt.blen, digps[count])) { + pgpDigParamsFree(digps[count]); + continue; + } + + pgpDigParams subkey_sig = NULL; + if (decodePkt(p, pend - p, &pkt) || + parseSubkeySig(&pkt, 0, &subkey_sig)) + { + pgpDigParamsFree(digps[count]); + break; + } + + /* Is the subkey revoked or incapable of signing? */ + int ignore = subkey_sig->sigtype != PGPSIGTYPE_SUBKEY_BINDING || + !((subkey_sig->saved & PGPDIG_SIG_HAS_KEY_FLAGS) && + (subkey_sig->key_flags & 0x02)); + if (ignore) { + pgpDigParamsFree(digps[count]); + } else { + digps[count]->key_flags = subkey_sig->key_flags; + digps[count]->saved |= PGPDIG_SIG_HAS_KEY_FLAGS; + count++; + } + p += (pkt.body - pkt.head) + pkt.blen; + pgpDigParamsFree(subkey_sig); + } + } + rc = (p == pend) ? 0 : -1; + + if (rc == 0) { + *subkeys = xrealloc(digps, count * sizeof(*digps)); + *subkeysCount = count; + } else { + for (i = 0; i < count; i++) + pgpDigParamsFree(digps[i]); + free(digps); + } + + return rc; +} + +rpmRC pgpVerifySignature(pgpDigParams key, pgpDigParams sig, DIGEST_CTX hashctx) +{ + DIGEST_CTX ctx = rpmDigestDup(hashctx); + uint8_t *hash = NULL; + size_t hashlen = 0; + rpmRC res = RPMRC_FAIL; /* assume failure */ + + if (sig == NULL || ctx == NULL) + goto exit; + + if (sig->hash != NULL) + rpmDigestUpdate(ctx, sig->hash, sig->hashlen); + + if (sig->version == 4) { + /* V4 trailer is six octets long (rfc4880) */ + uint8_t trailer[6]; + uint32_t nb = sig->hashlen; + nb = htonl(nb); + trailer[0] = sig->version; + trailer[1] = 0xff; + memcpy(trailer+2, &nb, 4); + rpmDigestUpdate(ctx, trailer, sizeof(trailer)); + } + + rpmDigestFinal(ctx, (void **)&hash, &hashlen, 0); + ctx = NULL; + + /* Compare leading 16 bits of digest for quick check. */ + if (hash == NULL || memcmp(hash, sig->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 (key && key->alg) { + pgpDigAlg sa = sig->alg; + pgpDigAlg ka = key->alg; + if (sa && sa->verify) { + if (sa->verify(ka, sa, hash, hashlen, sig->hash_algo) == 0) { + res = RPMRC_OK; + } + } + } else { + res = RPMRC_NOKEY; + } + +exit: + free(hash); + rpmDigestFinal(ctx, NULL, NULL, 0); + 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 */ + +#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); + /* Handle EOF without EOL here, *t == '\0' at EOF */ + if (*t && (t >= te)) continue; + /* XXX permitting \r here is not RFC-2440 compliant <shrug> */ + if (!(*t == '\n' || *t == '\r' || *t == '\0')) continue; + + crcdec = NULL; + crclen = 0; + if (rpmBase64Decode(crcenc, (void **)&crcdec, &crclen) != 0 || crclen != 3) { + crcdec = _free(crcdec); + ec = PGPARMOR_ERR_CRC_DECODE; + goto exit; + } + crcpkt = pgpGrab(crcdec, crclen); + crcdec = _free(crcdec); + dec = NULL; + declen = 0; + if (rpmBase64Decode(enc, (void **)&dec, &declen) != 0) { + ec = PGPARMOR_ERR_BODY_DECODE; + goto exit; + } + crc = pgpCRC(dec, declen); + if (crcpkt != crc) { + ec = PGPARMOR_ERR_CRC_CHECK; + _free(dec); + goto exit; + } + if (pkt) + *pkt = dec; + else + _free(dec); + if (pktlen) *pktlen = declen; + ec = PGPARMOR_PUBKEY; /* XXX ASCII Pubkeys only, please. */ + goto exit; + break; + } + } + ec = PGPARMOR_NONE; + +exit: + 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; +} + +int pgpPubKeyCertLen(const uint8_t *pkts, size_t pktslen, size_t *certlen) +{ + const uint8_t *p = pkts; + const uint8_t *pend = pkts + pktslen; + struct pgpPkt pkt; + + while (p < pend) { + if (decodePkt(p, (pend - p), &pkt)) + return -1; + + if (pkt.tag == PGPTAG_PUBLIC_KEY && pkts != p) { + *certlen = p - pkts; + return 0; + } + + p += (pkt.body - pkt.head) + pkt.blen; + } + + *certlen = pktslen; + + return 0; +} + +char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns) +{ + char *buf = NULL, *val = NULL; + char *enc = rpmBase64Encode(s, ns, -1); + char *crc = rpmBase64CRC(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"\n\n" + "%s\n-----END PGP %s-----\n", + valstr, buf != NULL ? buf : "", valstr); + + free(buf); + return val; +} |