/*- * Copyright (c) 2012,2013,2014,2015,2016 Alistair Crooks * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netpgpv-bzlib.h" #include "zlib.h" #include "array.h" #include "b64.h" #include "bn.h" #include "bufgap.h" #include "digest.h" #include "misc.h" #include "pgpsum.h" #include "rsa.h" #include "verify.h" #ifndef PRIi64 #define PRIi64 "lld" #endif /* 64bit key ids */ #define PGPV_KEYID_LEN 8 #define PGPV_STR_KEYID_LEN (PGPV_KEYID_LEN + PGPV_KEYID_LEN + 1) /* bignum structure */ typedef struct pgpv_bignum_t { void *bn; /* hide the implementation details */ uint16_t bits; /* cached number of bits */ } pgpv_bignum_t; /* right now, our max binary digest length is 20 bytes */ #define PGPV_MAX_HASH_LEN 64 /* fingerprint */ typedef struct pgpv_fingerprint_t { uint8_t hashalg; /* algorithm for digest */ uint8_t v[PGPV_MAX_HASH_LEN]; /* the digest */ uint32_t len; /* its length */ } pgpv_fingerprint_t; /* specify size for array of bignums */ #define PGPV_MAX_PUBKEY_BN 4 /* public key */ typedef struct pgpv_pubkey_t { pgpv_fingerprint_t fingerprint; /* key fingerprint i.e. digest */ uint8_t keyid[PGPV_KEYID_LEN]; /* last 8 bytes of v4 keys */ int64_t birth; /* creation time */ int64_t expiry; /* expiry time */ pgpv_bignum_t bn[PGPV_MAX_PUBKEY_BN]; /* bignums */ uint8_t keyalg; /* key algorithm */ uint8_t hashalg; /* hash algorithm */ uint8_t version; /* key version */ } pgpv_pubkey_t; #define PGPV_MAX_SESSKEY_BN 2 /* a (size, byte array) string */ typedef struct pgpv_string_t { size_t size; uint8_t *data; uint8_t allocated; } pgpv_string_t; typedef struct pgpv_ref_t { void *vp; size_t offset; unsigned mem; } pgpv_ref_t; #define PGPV_MAX_SECKEY_BN 4 typedef struct pgpv_compress_t { pgpv_string_t s; uint8_t compalg; } pgpv_compress_t; /* a packet dealing with trust */ typedef struct pgpv_trust_t { uint8_t level; uint8_t amount; } pgpv_trust_t; /* a signature sub packet */ typedef struct pgpv_sigsubpkt_t { pgpv_string_t s; uint8_t tag; uint8_t critical; } pgpv_sigsubpkt_t; #define PGPV_MAX_SIG_BN 2 typedef struct pgpv_signature_t { uint8_t signer[PGPV_KEYID_LEN]; /* key id of signer */ pgpv_ref_t hashstart; uint8_t *hash2; uint8_t *mpi; int64_t birth; int64_t keyexpiry; int64_t expiry; uint32_t hashlen; uint8_t version; uint8_t type; uint8_t keyalg; uint8_t hashalg; uint8_t trustlevel; uint8_t trustamount; pgpv_bignum_t bn[PGPV_MAX_SIG_BN]; char *regexp; char *pref_key_server; char *policy; char *features; char *why_revoked; uint8_t *revoke_fingerprint; uint8_t *issuer_fingerprint; uint8_t ifver; uint8_t revoke_alg; uint8_t revoke_sensitive; uint8_t trustsig; uint8_t revocable; uint8_t pref_symm_alg; uint8_t pref_hash_alg; uint8_t pref_compress_alg; uint8_t key_server_modify; uint8_t notation; uint8_t type_key; uint8_t primary_userid; uint8_t revoked; /* subtract 1 to get real reason, 0 == not revoked */ } pgpv_signature_t; /* a signature packet */ typedef struct pgpv_sigpkt_t { pgpv_signature_t sig; uint16_t subslen; uint16_t unhashlen; ARRAY(uint64_t, subpackets); } pgpv_sigpkt_t; /* a one-pass signature packet */ typedef struct pgpv_onepass_t { uint8_t keyid[PGPV_KEYID_LEN]; uint8_t version; uint8_t type; uint8_t hashalg; uint8_t keyalg; uint8_t nested; } pgpv_onepass_t; /* a literal data packet */ typedef struct pgpv_litdata_t { pgpv_string_t filename; pgpv_string_t s; uint32_t secs; uint8_t namelen; char format; unsigned mem; size_t offset; size_t len; } pgpv_litdata_t; /* user attributes - images */ typedef struct pgpv_userattr_t { size_t len; ARRAY(pgpv_string_t, subattrs); } pgpv_userattr_t; /* a general PGP packet */ typedef struct pgpv_pkt_t { uint8_t tag; uint8_t newfmt; uint8_t allocated; uint8_t mement; size_t offset; pgpv_string_t s; union { pgpv_sigpkt_t sigpkt; pgpv_onepass_t onepass; pgpv_litdata_t litdata; pgpv_compress_t compressed; pgpv_trust_t trust; pgpv_pubkey_t pubkey; pgpv_string_t userid; pgpv_userattr_t userattr; } u; } pgpv_pkt_t; /* a memory structure */ typedef struct pgpv_mem_t { size_t size; size_t cc; uint8_t *mem; FILE *fp; uint8_t dealloc; const char *allowed; /* the types of packet that are allowed */ } pgpv_mem_t; /* packet parser */ typedef struct pgpv_signed_userid_t { pgpv_string_t userid; ARRAY(uint64_t, signatures); uint8_t primary_userid; uint8_t revoked; } pgpv_signed_userid_t; typedef struct pgpv_signed_userattr_t { pgpv_userattr_t userattr; ARRAY(uint64_t, signatures); uint8_t revoked; } pgpv_signed_userattr_t; typedef struct pgpv_signed_subkey_t { pgpv_pubkey_t subkey; pgpv_signature_t revoc_self_sig; ARRAY(uint64_t, signatures); } pgpv_signed_subkey_t; typedef struct pgpv_primarykey_t { pgpv_pubkey_t primary; pgpv_signature_t revoc_self_sig; ARRAY(uint64_t, signatures); ARRAY(uint64_t, signed_userids); ARRAY(uint64_t, signed_userattrs); ARRAY(uint64_t, signed_subkeys); size_t fmtsize; uint8_t primary_userid; } pgpv_primarykey_t; /* everything stems from this structure */ struct pgpv_t { ARRAY(pgpv_pkt_t, pkts); /* packet array */ ARRAY(pgpv_primarykey_t, primaries); /* array of primary keys */ ARRAY(pgpv_mem_t, areas); /* areas we read packets from */ ARRAY(size_t, datastarts); /* starts of data packets */ ARRAY(pgpv_signature_t, signatures); /* all signatures */ ARRAY(pgpv_signed_userid_t, signed_userids); /* all signed userids */ ARRAY(pgpv_signed_userattr_t, signed_userattrs); /* all signed user attrs */ ARRAY(pgpv_signed_subkey_t, signed_subkeys); /* all signed subkeys */ ARRAY(pgpv_sigsubpkt_t, subpkts); /* all sub packets */ size_t pkt; /* when parsing, current pkt number */ const char *op; /* the operation we're doing */ unsigned ssh; /* using ssh keys */ }; #define PGPV_REASON_LEN 128 /* when searching, we define a cursor, and fill in an array of subscripts */ struct pgpv_cursor_t { pgpv_t *pgp; /* pointer to pgp tree */ char *field; /* field we're searching on */ char *op; /* operation we're doing */ char *value; /* value we're searching for */ void *ptr; /* for regexps etc */ ARRAY(uint32_t, found); /* array of matched pimary key subscripts */ ARRAY(size_t, datacookies); /* cookies to retrieve matched data */ int64_t sigtime; /* time of signature */ char why[PGPV_REASON_LEN]; /* reason for bad signature */ }; #ifndef USE_ARG #define USE_ARG(x) /*LINTED*/(void)&(x) #endif #ifndef __dead #define __dead __attribute__((__noreturn__)) #endif #ifndef __printflike #define __printflike(n, m) __attribute__((format(printf,n,m))) #endif #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define BITS_TO_BYTES(b) (((b) + (CHAR_BIT - 1)) / CHAR_BIT) /* packet types */ #define SIGNATURE_PKT 2 #define ONEPASS_SIGNATURE_PKT 4 #define PUBKEY_PKT 6 #define COMPRESSED_DATA_PKT 8 #define MARKER_PKT 10 #define LITDATA_PKT 11 #define TRUST_PKT 12 #define USERID_PKT 13 #define PUB_SUBKEY_PKT 14 #define USER_ATTRIBUTE_PKT 17 /* only allow certain packets at certain times */ #define PUBRING_ALLOWED "\002\006\014\015\016\021" #define SIGNATURE_ALLOWED "\002\004\010\013" /* actions to do on close */ #define FREE_MEM 0x01 #define UNMAP_MEM 0x02 /* types of pubkey we encounter */ #define PUBKEY_RSA_ENCRYPT_OR_SIGN 1 #define PUBKEY_RSA_ENCRYPT 2 #define PUBKEY_RSA_SIGN 3 #define PUBKEY_ELGAMAL_ENCRYPT 16 #define PUBKEY_DSA 17 #define PUBKEY_ECDH 18 #define PUBKEY_ECDSA 19 #define PUBKEY_ELGAMAL_ENCRYPT_OR_SIGN 20 /* hash algorithm definitions */ #define PGPV_HASH_MD5 1 #define PGPV_HASH_SHA1 2 #define PGPV_HASH_RIPEMD 3 #define PGPV_HASH_SHA256 8 #define PGPV_HASH_SHA384 9 #define PGPV_HASH_SHA512 10 /* pubkey defs for bignums */ #define RSA_N 0 #define RSA_E 1 #define DSA_P 0 #define DSA_Q 1 #define DSA_G 2 #define DSA_Y 3 #define ELGAMAL_P 0 #define ELGAMAL_G 1 #define ELGAMAL_Y 2 /* sesskey indices */ #define RSA_SESSKEY_ENCRYPTED_M 0 #define RSA_SESSKEY_M 1 #define ELGAMAL_SESSKEY_G_TO_K 0 #define ELGAMAL_SESSKEY_ENCRYPTED_M 1 /* seckey indices */ #define RSA_SECKEY_D 0 #define RSA_SECKEY_P 1 #define RSA_SECKEY_Q 2 #define RSA_SECKEY_U 3 #define DSA_SECKEY_X 0 #define ELGAMAL_SECKEY_X 0 /* signature mpi indices in bignumber array */ #define RSA_SIG 0 #define DSA_R 0 #define DSA_S 1 #define ELGAMAL_SIG_R 0 #define ELGAMAL_SIG_S 1 /* signature types */ #define SIGTYPE_BINARY_DOC 0x00 /* Signature of a binary document */ #define SIGTYPE_TEXT 0x01 /* Signature of a canonical text document */ #define SIGTYPE_STANDALONE 0x02 /* Standalone signature */ #define SIGTYPE_GENERIC_USERID 0x10 /* Generic certification of a User ID and Public Key packet */ #define SIGTYPE_PERSONA_USERID 0x11 /* Persona certification of a User ID and Public Key packet */ #define SIGTYPE_CASUAL_USERID 0x12 /* Casual certification of a User ID and Public Key packet */ #define SIGTYPE_POSITIVE_USERID 0x13 /* Positive certification of a User ID and Public Key packet */ #define SIGTYPE_SUBKEY_BINDING 0x18 /* Subkey Binding Signature */ #define SIGTYPE_PRIMARY_KEY_BINDING 0x19 /* Primary Key Binding Signature */ #define SIGTYPE_DIRECT_KEY 0x1f /* Signature directly on a key */ #define SIGTYPE_KEY_REVOCATION 0x20 /* Key revocation signature */ #define SIGTYPE_SUBKEY_REVOCATION 0x28 /* Subkey revocation signature */ #define SIGTYPE_CERT_REVOCATION 0x30 /* Certification revocation signature */ #define SIGTYPE_TIMESTAMP_SIG 0x40 /* Timestamp signature */ #define SIGTYPE_3RDPARTY 0x50 /* Third-Party Confirmation signature */ /* Forward declarations */ static int read_all_packets(pgpv_t */*pgp*/, pgpv_mem_t */*mem*/, const char */*op*/); static int read_binary_file(pgpv_t */*pgp*/, const char */*op*/, const char */*fmt*/, ...) __printflike(3, 4); static int read_binary_memory(pgpv_t */*pgp*/, const char */*op*/, const void */*memory*/, size_t /*size*/); /* output buffer structure */ typedef struct obuf_t { size_t alloc; /* amount of memory allocated */ size_t c; /* # of chars used so far */ uint8_t *v; /* array of bytes */ uint32_t endian; /* byte order of output stream */ } obuf_t; /* grow the buffer, if needed */ static int growbuf(obuf_t *obuf, size_t cc) { size_t newalloc; uint8_t *newv; if (obuf->c + cc > obuf->alloc) { newalloc = howmany(obuf->alloc + cc, 128) * 128; newv = realloc(obuf->v, newalloc); if (newv == NULL) { return 0; } obuf->v = newv; obuf->alloc = newalloc; } return 1; } /* add a fixed-length area of memory */ static int obuf_add_mem(obuf_t *obuf, const void *s, size_t len) { if (obuf && s && len > 0) { if (!growbuf(obuf, len)) { return 0; } memcpy(&obuf->v[obuf->c], s, len); obuf->c += len; return 1; } return 0; } /* read a file into the pgpv_mem_t struct */ static int read_file(pgpv_t *pgp, const char *f) { struct stat st; pgpv_mem_t *mem; ARRAY_EXPAND(pgp->areas); ARRAY_COUNT(pgp->areas) += 1; mem = &ARRAY_LAST(pgp->areas); memset(mem, 0x0, sizeof(*mem)); if ((mem->fp = fopen(f, "r")) == NULL) { fprintf(stderr, "can't read '%s'", f); return 0; } fstat(fileno(mem->fp), &st); mem->size = (size_t)st.st_size; mem->mem = mmap(NULL, mem->size, PROT_READ, MAP_SHARED, fileno(mem->fp), 0); mem->dealloc = UNMAP_MEM; return 1; } /* DTRT and free resources */ static int closemem(pgpv_mem_t *mem) { switch(mem->dealloc) { case FREE_MEM: free(mem->mem); mem->size = 0; break; case UNMAP_MEM: munmap(mem->mem, mem->size); fclose(mem->fp); break; } return 1; } /* make a reference to a memory area, and its offset */ static void make_ref(pgpv_t *pgp, uint8_t mement, pgpv_ref_t *ref) { ref->mem = mement; ref->offset = ARRAY_ELEMENT(pgp->areas, ref->mem).cc; ref->vp = pgp; } /* return the pointer we wanted originally */ static uint8_t * get_ref(pgpv_ref_t *ref) { pgpv_mem_t *mem; pgpv_t *pgp = (pgpv_t *)ref->vp; mem = &ARRAY_ELEMENT(pgp->areas, ref->mem); return &mem->mem[ref->offset]; } #define IS_PARTIAL(x) ((x) >= 224 && (x) < 255) #define DECODE_PARTIAL(x) (1 << ((x) & 0x1f)) #define PKT_LENGTH(m, off) \ ((m[off] < 192) ? (m[off]) : \ (m[off] < 224) ? ((m[off] - 192) << 8) + (m[off + 1]) + 192 : \ (m[off + 1] << 24) | ((m[off + 2]) << 16) | ((m[off + 3]) << 8) | (m[off + 4])) #define PKT_LENGTH_LENGTH(m, off) \ ((m[off] < 192) ? 1 : (m[off] < 224) ? 2 : 5) /* fix up partial body lengths, return new size */ static size_t fixup_partials(pgpv_t *pgp, uint8_t *p, size_t totlen, size_t filesize, size_t *cc) { pgpv_mem_t *mem; size_t partial; size_t newcc; if (totlen > filesize) { printf("fixup_partial: filesize %zu is less than encoded size %zu\n", filesize, totlen); return 0; } ARRAY_EXPAND(pgp->areas); ARRAY_COUNT(pgp->areas) += 1; mem = &ARRAY_LAST(pgp->areas); mem->size = totlen; if ((mem->mem = calloc(1, mem->size + 5)) == NULL) { printf("fixup_partial: can't allocate %zu length\n", totlen); return 0; } newcc = 0; mem->dealloc = FREE_MEM; for (*cc = 0 ; *cc < totlen ; newcc += partial, *cc += partial + 1) { if (IS_PARTIAL(p[*cc])) { partial = DECODE_PARTIAL(p[*cc]); memcpy(&mem->mem[newcc], &p[*cc + 1], partial); } else { partial = PKT_LENGTH(p, *cc); *cc += PKT_LENGTH_LENGTH(p, *cc); memcpy(&mem->mem[newcc], &p[*cc], partial); newcc += partial; *cc += partial; break; } } return newcc; } /* get the weirdo packet length */ static size_t get_pkt_len(uint8_t newfmt, uint8_t *p, size_t filesize, int isprimary) { size_t lenbytes; size_t len; if (newfmt) { if (IS_PARTIAL(*p)) { if (!isprimary) { /* for sub-packets, only 1, 2 or 4 byte sizes allowed */ return ((*p - 192) << 8) + *(p + 1) + 192; } lenbytes = 1; for (len = DECODE_PARTIAL(*p) ; IS_PARTIAL(p[len + lenbytes]) ; lenbytes++) { len += DECODE_PARTIAL(p[len + lenbytes]); } len += get_pkt_len(newfmt, &p[len + lenbytes], filesize, 1); return len; } return PKT_LENGTH(p, 0); } else { switch(*--p & 0x3) { case 0: return *(p + 1); case 1: return (*(p + 1) << 8) | *(p + 2); case 2: return (*(p + 1) << 24) | (*(p + 2) << 16) | (*(p + 3) << 8) | *(p + 4); default: return filesize; } } } static const uint8_t base64s[] = /* 000 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 016 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 032 */ "\0\0\0\0\0\0\0\0\0\0\0?\0\0\0@" /* 048 */ "56789:;<=>\0\0\0\0\0\0" /* 064 */ "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17" /* 080 */ "\20\21\22\23\24\25\26\27\30\31\32\0\0\0\0\0" /* 096 */ "\0\33\34\35\36\37 !\"#$%&'()" /* 112 */ "*+,-./01234\0\0\0\0\0" /* 128 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 144 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 160 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 176 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 192 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 208 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 224 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" /* 240 */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; /* short function to decode from base64 */ /* inspired by an ancient copy of b64.c, then rewritten, the bugs are all mine */ static int frombase64(char *dst, const char *src, size_t size, int flag) { uint8_t out[3]; uint8_t in[4]; uint8_t b; size_t srcc; int dstc; int gotc; int i; USE_ARG(flag); for (dstc = 0, srcc = 0 ; srcc < size; ) { for (gotc = 0, i = 0; i < 4 && srcc < size; i++) { for (b = 0x0; srcc < size && b == 0x0 ; ) { b = base64s[(unsigned)src[srcc++]]; } if (srcc < size) { gotc += 1; if (b) { in[i] = (uint8_t)(b - 1); } } else { in[i] = 0x0; } } if (gotc) { out[0] = (uint8_t)((unsigned)in[0] << 2 | (unsigned)in[1] >> 4); out[1] = (uint8_t)((unsigned)in[1] << 4 | (unsigned)in[2] >> 2); out[2] = (uint8_t)(((in[2] << 6) & 0xc0) | in[3]); for (i = 0; i < gotc - 1; i++) { *dst++ = out[i]; } dstc += gotc - 1; } } return dstc; } /* get the length of the packet length field */ static unsigned get_pkt_len_len(uint8_t newfmt, uint8_t *p, int isprimary) { if (newfmt) { if (IS_PARTIAL(*p)) { return (isprimary) ? 1 : 2; } return PKT_LENGTH_LENGTH(p, 0); } else { switch(*--p & 0x3) { case 0: return 1; case 1: return 2; case 2: return 4; default: return 0; } } } /* copy the 32bit integer in memory in network order */ static unsigned fmt_32(uint8_t *p, uint32_t a) { a = pgp_hton32(a); memcpy(p, &a, sizeof(a)); return sizeof(a); } /* copy the 16bit integer in memory in network order */ static unsigned fmt_16(uint8_t *p, uint16_t a) { a = pgp_hton16(a); memcpy(p, &a, sizeof(a)); return sizeof(a); } /* format a binary string in memory */ static size_t fmt_binary(obuf_t *obuf, const uint8_t *bin, unsigned len) { unsigned i; char newbuf[3]; for (i = 0 ; i < len ; i++) { snprintf(newbuf, sizeof(newbuf), "%02hhx", bin[i]); if (!obuf_add_mem(obuf, newbuf, 2)) { return 0; } } return 1; } /* format an mpi into memory */ static unsigned fmt_binary_mpi(pgpv_bignum_t *mpi, uint8_t *p, size_t size) { unsigned bytes; PGPV_BIGNUM *bn; bytes = BITS_TO_BYTES(mpi->bits); if ((size_t)bytes + 2 + 1 > size) { fprintf(stderr, "truncated mpi"); return 0; } bn = (PGPV_BIGNUM *)mpi->bn; if (bn == NULL || PGPV_BN_is_zero(bn)) { fmt_32(p, 0); return 2 + 1; } fmt_16(p, mpi->bits); PGPV_BN_bn2bin(bn, &p[2]); return bytes + 2; } /* dump an mpi value onto stdout */ static size_t fmt_mpi(char *s, size_t size, pgpv_bignum_t *bn, const char *name, int pbits) { size_t cc; char *buf; cc = snprintf(s, size, "%s=", name); if (pbits) { cc += snprintf(&s[cc], size - cc, "[%u bits] ", bn->bits); } buf = PGPV_BN_bn2hex(bn->bn); cc += snprintf(&s[cc], size - cc, "%s\n", buf); free(buf); return cc; } #define ALG_IS_RSA(alg) (((alg) == PUBKEY_RSA_ENCRYPT_OR_SIGN) || \ ((alg) == PUBKEY_RSA_ENCRYPT) || \ ((alg) == PUBKEY_RSA_SIGN)) #define ALG_IS_DSA(alg) ((alg) == PUBKEY_DSA) /* format key mpis into memory */ static unsigned fmt_key_mpis(pgpv_pubkey_t *pubkey, uint8_t *buf, size_t size) { size_t cc; cc = 0; buf[cc++] = pubkey->version; cc += fmt_32(&buf[cc], (uint32_t)pubkey->birth); /* XXX - do this portably! */ buf[cc++] = pubkey->keyalg; /* XXX - sign, or encrypt and sign? */ switch(pubkey->keyalg) { case PUBKEY_RSA_ENCRYPT_OR_SIGN: case PUBKEY_RSA_ENCRYPT: case PUBKEY_RSA_SIGN: cc += fmt_binary_mpi(&pubkey->bn[RSA_N], &buf[cc], size - cc); cc += fmt_binary_mpi(&pubkey->bn[RSA_E], &buf[cc], size - cc); break; case PUBKEY_DSA: cc += fmt_binary_mpi(&pubkey->bn[DSA_P], &buf[cc], size - cc); cc += fmt_binary_mpi(&pubkey->bn[DSA_Q], &buf[cc], size - cc); cc += fmt_binary_mpi(&pubkey->bn[DSA_G], &buf[cc], size - cc); cc += fmt_binary_mpi(&pubkey->bn[DSA_Y], &buf[cc], size - cc); break; default: cc += fmt_binary_mpi(&pubkey->bn[ELGAMAL_P], &buf[cc], size - cc); cc += fmt_binary_mpi(&pubkey->bn[ELGAMAL_G], &buf[cc], size - cc); cc += fmt_binary_mpi(&pubkey->bn[ELGAMAL_Y], &buf[cc], size - cc); break; } return (unsigned)cc; } /* calculate the fingerprint, RFC 4880, section 12.2 */ static int pgpv_calc_fingerprint(pgpv_fingerprint_t *fingerprint, pgpv_pubkey_t *pubkey, const char *hashtype) { digest_t fphash; uint16_t cc; uint8_t ch = 0x99; uint8_t buf[8192 + 2 + 1]; uint8_t len[2]; memset(&fphash, 0x0, sizeof(fphash)); if (pubkey->version == 4) { /* v4 keys */ fingerprint->hashalg = digest_get_alg(hashtype); digest_init(&fphash, (unsigned)fingerprint->hashalg); cc = fmt_key_mpis(pubkey, buf, sizeof(buf)); digest_update(&fphash, &ch, 1); fmt_16(len, cc); digest_update(&fphash, len, 2); digest_update(&fphash, buf, (unsigned)cc); fingerprint->len = digest_final(fingerprint->v, &fphash); return 1; } if (ALG_IS_RSA(pubkey->keyalg)) { /* v3 keys are RSA */ fingerprint->hashalg = digest_get_alg("md5"); digest_init(&fphash, (unsigned)fingerprint->hashalg); if (pubkey->bn[RSA_N].bn && pubkey->bn[RSA_E].bn) { cc = fmt_binary_mpi(&pubkey->bn[RSA_N], buf, sizeof(buf)); digest_update(&fphash, &buf[2], (unsigned)(cc - 2)); cc = fmt_binary_mpi(&pubkey->bn[RSA_E], buf, sizeof(buf)); digest_update(&fphash, &buf[2], (unsigned)(cc - 2)); fingerprint->len = digest_final(fingerprint->v, &fphash); return 1; } } if (pubkey->bn[RSA_N].bn) { if ((cc = fmt_binary_mpi(&pubkey->bn[RSA_N], buf, sizeof(buf))) >= PGPV_KEYID_LEN) { memcpy(fingerprint->v, &buf[cc - PGPV_KEYID_LEN], PGPV_KEYID_LEN); fingerprint->len = PGPV_KEYID_LEN; return 1; } } /* exhausted all avenues, really */ memset(fingerprint->v, 0xff, fingerprint->len = PGPV_KEYID_LEN); return 1; } /* format a fingerprint into memory */ static int fmt_fingerprint(obuf_t *obuf, pgpv_fingerprint_t *fingerprint, const char *name) { unsigned i; char newbuf[3]; int cc; if (!obuf_add_mem(obuf, name, strlen(name)) || !obuf_add_mem(obuf, " ", 1)) { return 0; } for (i = 0 ; i < fingerprint->len ; i++) { cc = snprintf(newbuf, sizeof(newbuf), "%02hhx", fingerprint->v[i]); if (!obuf_add_mem(obuf, newbuf, cc)) { return 0; } if (i % 2 == 1 && !obuf_add_mem(obuf, " ", 1)) { return 0; } } return obuf_add_mem(obuf, "\n", 1); } /* calculate keyid from a pubkey */ static int calc_keyid(pgpv_pubkey_t *key, const char *hashtype) { pgpv_calc_fingerprint(&key->fingerprint, key, hashtype); memcpy(key->keyid, &key->fingerprint.v[key->fingerprint.len - PGPV_KEYID_LEN], PGPV_KEYID_LEN); return 1; } /* convert a hex string to a 64bit key id (in big endian byte order */ static void str_to_keyid(const char *s, uint8_t *keyid) { uint64_t u; u = (uint64_t)strtoull(s, NULL, 16); u = ((u & 0x00000000000000FFULL) << 56) | ((u & 0x000000000000FF00ULL) << 40) | ((u & 0x0000000000FF0000ULL) << 24) | ((u & 0x00000000FF000000ULL) << 8) | ((u & 0x000000FF00000000ULL) >> 8) | ((u & 0x0000FF0000000000ULL) >> 24) | ((u & 0x00FF000000000000ULL) >> 40) | ((u & 0xFF00000000000000ULL) >> 56); memcpy(keyid, &u, PGPV_KEYID_LEN); } #define PKT_ALWAYS_ON 0x80 #define PKT_NEWFMT_MASK 0x40 #define PKT_NEWFMT_TAG_MASK 0x3f #define PKT_OLDFMT_TAG_MASK 0x3c #define SUBPKT_CRITICAL_MASK 0x80 #define SUBPKT_TAG_MASK 0x7f #define SUBPKT_SIG_BIRTH 2 #define SUBPKT_SIG_EXPIRY 3 #define SUBPKT_EXPORT_CERT 4 #define SUBPKT_TRUST_SIG 5 #define SUBPKT_REGEXP 6 #define SUBPKT_REVOCABLE 7 #define SUBPKT_KEY_EXPIRY 9 #define SUBPKT_BWD_COMPAT 10 #define SUBPKT_PREF_SYMMETRIC_ALG 11 #define SUBPKT_REVOCATION_KEY 12 #define SUBPKT_ISSUER 16 #define SUBPKT_NOTATION 20 #define SUBPKT_PREF_HASH_ALG 21 #define SUBPKT_PREF_COMPRESS_ALG 22 #define SUBPKT_KEY_SERVER_PREFS 23 #define SUBPKT_PREF_KEY_SERVER 24 #define SUBPKT_PRIMARY_USER_ID 25 #define SUBPKT_POLICY_URI 26 #define SUBPKT_KEY_FLAGS 27 #define SUBPKT_SIGNER_ID 28 #define SUBPKT_REVOCATION_REASON 29 #define SUBPKT_FEATURES 30 #define SUBPKT_SIGNATURE_TARGET 31 #define SUBPKT_EMBEDDED_SIGNATURE 32 #define SUBPKT_ISSUER_FINGERPRINT 33 #define UNCOMPRESSED 0 #define ZIP_COMPRESSION 1 #define ZLIB_COMPRESSION 2 #define BZIP2_COMPRESSION 3 /* get a 16 bit integer, in host order */ static uint16_t get_16(uint8_t *p) { uint16_t u16; memcpy(&u16, p, sizeof(u16)); return pgp_ntoh16(u16); } /* get a 32 bit integer, in host order */ static uint32_t get_32(uint8_t *p) { uint32_t u32; memcpy(&u32, p, sizeof(u32)); return pgp_ntoh32(u32); } #define HOURSECS (int64_t)(60 * 60) #define DAYSECS (int64_t)(24 * 60 * 60) #define MONSECS (int64_t)(30 * DAYSECS) #define YEARSECS (int64_t)(365 * DAYSECS) /* format (human readable) time into memory */ static size_t fmt_time(obuf_t *obuf, const char *header, int64_t n, const char *trailer, int relative) { struct tm tm; time_t elapsed; time_t now; time_t t; char newbuf[128]; int cc; t = (time_t)n; now = time(NULL); elapsed = now - t; gmtime_r(&t, &tm); cc = snprintf(newbuf, sizeof(newbuf), "%04d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); if (!obuf_add_mem(obuf, header, strlen(header)) || !obuf_add_mem(obuf, newbuf, cc)) { return 0; } if (relative) { cc = snprintf(newbuf, sizeof(newbuf), " (%lldy %lldm %lldd %lldh %s)", llabs((long long)elapsed / YEARSECS), llabs(((long long)elapsed % YEARSECS) / MONSECS), llabs(((long long)elapsed % MONSECS) / DAYSECS), llabs(((long long)elapsed % DAYSECS) / HOURSECS), (now > t) ? "ago" : "ahead"); if (!obuf_add_mem(obuf, newbuf, cc)) { return 0; } } return (*trailer) ? obuf_add_mem(obuf, trailer, strlen(trailer)) : 1; } /* dump key mpis to stdout */ static void print_key_mpis(pgpv_bignum_t *v, uint8_t keyalg) { char s[8192]; switch(keyalg) { case PUBKEY_RSA_ENCRYPT_OR_SIGN: case PUBKEY_RSA_ENCRYPT: case PUBKEY_RSA_SIGN: fmt_mpi(s, sizeof(s), &v[RSA_N], "rsa.n", 1); printf("%s", s); fmt_mpi(s, sizeof(s), &v[RSA_E], "rsa.e", 1); printf("%s", s); break; case PUBKEY_ELGAMAL_ENCRYPT: fmt_mpi(s, sizeof(s), &v[ELGAMAL_P], "elgamal.p", 1); printf("%s", s); fmt_mpi(s, sizeof(s), &v[ELGAMAL_Y], "elgamal.y", 1); printf("%s", s); break; case PUBKEY_DSA: fmt_mpi(s, sizeof(s), &v[DSA_P], "dsa.p", 1); printf("%s", s); fmt_mpi(s, sizeof(s), &v[DSA_Q], "dsa.q", 1); printf("%s", s); fmt_mpi(s, sizeof(s), &v[DSA_G], "dsa.g", 1); printf("%s", s); fmt_mpi(s, sizeof(s), &v[DSA_Y], "dsa.y", 1); printf("%s", s); break; default: printf("hi, unusual keyalg %u\n", keyalg); break; } } /* get an mpi, including 2 byte length */ static int get_mpi(pgpv_bignum_t *mpi, uint8_t *p, size_t pktlen, size_t *off) { size_t bytes; mpi->bits = get_16(p); if ((bytes = (size_t)BITS_TO_BYTES(mpi->bits)) > pktlen) { return 0; } *off += sizeof(mpi->bits); mpi->bn = PGPV_BN_bin2bn(&p[sizeof(mpi->bits)], (int)bytes, NULL); *off += bytes; return 1; } /* read mpis in signature */ static int read_signature_mpis(pgpv_sigpkt_t *sigpkt, uint8_t *p, size_t pktlen) { size_t off; off = 0; switch(sigpkt->sig.keyalg) { case PUBKEY_RSA_ENCRYPT_OR_SIGN: case PUBKEY_RSA_SIGN: case PUBKEY_RSA_ENCRYPT: if (!get_mpi(&sigpkt->sig.bn[RSA_SIG], p, pktlen, &off)) { printf("sigpkt->version %d, rsa sig weird\n", sigpkt->sig.version); return 0; } break; case PUBKEY_DSA: case PUBKEY_ECDSA: case PUBKEY_ELGAMAL_ENCRYPT_OR_SIGN: /* deprecated */ if (!get_mpi(&sigpkt->sig.bn[DSA_R], p, pktlen, &off) || !get_mpi(&sigpkt->sig.bn[DSA_S], &p[off], pktlen, &off)) { printf("sigpkt->version %d, dsa/elgamal sig weird\n", sigpkt->sig.version); return 0; } break; default: printf("weird type of sig! %d\n", sigpkt->sig.keyalg); return 0; } return 1; } /* add the signature sub packet to the signature packet */ static int add_subpacket(pgpv_t *pgp, pgpv_sigpkt_t *sigpkt, uint8_t tag, uint8_t *p, uint16_t len) { pgpv_sigsubpkt_t subpkt; memset(&subpkt, 0x0, sizeof(subpkt)); subpkt.s.size = len; subpkt.critical = 0; subpkt.tag = tag; subpkt.s.data = p; ARRAY_APPEND(sigpkt->subpackets, ARRAY_COUNT(pgp->subpkts)); ARRAY_APPEND(pgp->subpkts, subpkt); return 1; } /* read the subpackets in the signature */ static int read_sig_subpackets(pgpv_t *pgp, pgpv_sigpkt_t *sigpkt, uint8_t *p, size_t pktlen) { pgpv_sigsubpkt_t subpkt; const int is_subpkt = 0; unsigned lenlen; unsigned i; uint8_t *start; start = p; for (i = 0 ; (unsigned)(p - start) < sigpkt->subslen ; i++) { memset(&subpkt, 0x0, sizeof(subpkt)); subpkt.s.size = get_pkt_len(1, p, 0, is_subpkt); lenlen = get_pkt_len_len(1, p, is_subpkt); if (lenlen > pktlen) { printf("weird lenlen %u\n", lenlen); return 0; } p += lenlen; subpkt.critical = (*p & SUBPKT_CRITICAL_MASK); subpkt.tag = (*p & SUBPKT_TAG_MASK); p += 1; switch(subpkt.tag) { case SUBPKT_SIG_BIRTH: sigpkt->sig.birth = (int64_t)get_32(p); break; case SUBPKT_SIG_EXPIRY: sigpkt->sig.expiry = (int64_t)get_32(p); break; case SUBPKT_KEY_EXPIRY: sigpkt->sig.keyexpiry = (int64_t)get_32(p); break; case SUBPKT_ISSUER: memcpy(sigpkt->sig.signer, p, sizeof(sigpkt->sig.signer)); break; case SUBPKT_SIGNER_ID: memcpy(sigpkt->sig.signer, p, sizeof(sigpkt->sig.signer)); break; case SUBPKT_TRUST_SIG: sigpkt->sig.trustsig = *p; break; case SUBPKT_REGEXP: sigpkt->sig.regexp = (char *)(void *)p; break; case SUBPKT_REVOCABLE: sigpkt->sig.revocable = *p; break; case SUBPKT_PREF_SYMMETRIC_ALG: sigpkt->sig.pref_symm_alg = *p; break; case SUBPKT_REVOCATION_KEY: sigpkt->sig.revoke_sensitive = (*p & 0x40); sigpkt->sig.revoke_alg = p[1]; sigpkt->sig.revoke_fingerprint = &p[2]; break; case SUBPKT_NOTATION: sigpkt->sig.notation = *p; break; case SUBPKT_PREF_HASH_ALG: sigpkt->sig.pref_hash_alg = *p; break; case SUBPKT_PREF_COMPRESS_ALG: sigpkt->sig.pref_compress_alg = *p; break; case SUBPKT_PREF_KEY_SERVER: sigpkt->sig.pref_key_server = (char *)(void *)p; break; case SUBPKT_KEY_SERVER_PREFS: sigpkt->sig.key_server_modify = *p; break; case SUBPKT_KEY_FLAGS: sigpkt->sig.type_key = *p; break; case SUBPKT_PRIMARY_USER_ID: sigpkt->sig.primary_userid = *p; break; case SUBPKT_POLICY_URI: sigpkt->sig.policy = (char *)(void *)p; break; case SUBPKT_FEATURES: sigpkt->sig.features = (char *)(void *)p; break; case SUBPKT_REVOCATION_REASON: sigpkt->sig.revoked = *p++ + 1; sigpkt->sig.why_revoked = (char *)(void *)p; break; case SUBPKT_ISSUER_FINGERPRINT: sigpkt->sig.ifver = *p; sigpkt->sig.issuer_fingerprint = &p[1]; break; default: printf("Ignoring unusual/reserved signature subpacket %d\n", subpkt.tag); break; } subpkt.s.data = p; p += subpkt.s.size - 1; ARRAY_APPEND(sigpkt->subpackets, ARRAY_COUNT(pgp->subpkts)); ARRAY_APPEND(pgp->subpkts, subpkt); } return 1; } /* parse signature packet */ static int read_sigpkt(pgpv_t *pgp, uint8_t mement, pgpv_sigpkt_t *sigpkt, uint8_t *p, size_t pktlen) { unsigned lenlen; uint8_t *base; make_ref(pgp, mement, &sigpkt->sig.hashstart); base = p; switch(sigpkt->sig.version = *p++) { case 2: case 3: if ((lenlen = *p++) != 5) { printf("read_sigpkt: hashed length not 5\n"); return 0; } sigpkt->sig.hashlen = lenlen; /* put birthtime into a subpacket */ sigpkt->sig.type = *p++; add_subpacket(pgp, sigpkt, SUBPKT_SIG_BIRTH, p, sizeof(uint32_t)); sigpkt->sig.birth = (int64_t)get_32(p); p += sizeof(uint32_t); memcpy(sigpkt->sig.signer, p, sizeof(sigpkt->sig.signer)); add_subpacket(pgp, sigpkt, SUBPKT_SIGNER_ID, p, PGPV_KEYID_LEN); p += PGPV_KEYID_LEN; sigpkt->sig.keyalg = *p++; sigpkt->sig.hashalg = *p++; sigpkt->sig.hash2 = p; if (!read_signature_mpis(sigpkt, sigpkt->sig.mpi = p + 2, pktlen)) { printf("read_sigpkt: can't read sigs v3\n"); return 0; } break; case 4: sigpkt->sig.type = *p++; sigpkt->sig.keyalg = *p++; sigpkt->sig.hashalg = *p++; sigpkt->subslen = get_16(p); p += sizeof(sigpkt->subslen); if (!read_sig_subpackets(pgp, sigpkt, p, pktlen)) { printf("read_sigpkt: can't read sig subpackets, v4\n"); return 0; } if (sigpkt->sig.signer[0] == 0x0) { memcpy(sigpkt->sig.signer, get_ref(&sigpkt->sig.hashstart) + 16, sizeof(sigpkt->sig.signer)); } p += sigpkt->subslen; sigpkt->sig.hashlen = (unsigned)(p - base); sigpkt->unhashlen = get_16(p); p += sizeof(sigpkt->unhashlen) + sigpkt->unhashlen; sigpkt->sig.hash2 = p; if (!read_signature_mpis(sigpkt, sigpkt->sig.mpi = p + 2, pktlen)) { printf("read_sigpkt: can't read sigs, v4\n"); return 0; } break; default: printf("read_sigpkt: unusual signature version (%u)\n", sigpkt->sig.version); break; } return 1; } /* this parses compressed data, decompresses it, and calls the parser again */ static int read_compressed(pgpv_t *pgp, pgpv_compress_t *compressed, uint8_t *p, size_t len) { pgpv_mem_t *unzmem; bz_stream bz; z_stream z; int ok = 0; compressed->compalg = *p; compressed->s.size = len; if ((compressed->s.data = calloc(1, len)) == NULL) { printf("read_compressed: can't allocate %zu length\n", len); return 0; } switch(compressed->compalg) { case UNCOMPRESSED: printf("not implemented %d compression yet\n", compressed->compalg); return 0; default: break; } ARRAY_EXPAND(pgp->areas); ARRAY_COUNT(pgp->areas) += 1; unzmem = &ARRAY_LAST(pgp->areas); unzmem->size = len * 10; unzmem->dealloc = FREE_MEM; if ((unzmem->mem = calloc(1, unzmem->size)) == NULL) { printf("read_compressed: calloc failed!\n"); return 0; } switch(compressed->compalg) { case ZIP_COMPRESSION: case ZLIB_COMPRESSION: memset(&z, 0x0, sizeof(z)); z.next_in = p + 1; z.avail_in = (unsigned)(len - 1); z.total_in = (unsigned)(len - 1); z.next_out = unzmem->mem; z.avail_out = (unsigned)unzmem->size; z.total_out = (unsigned)unzmem->size; break; case BZIP2_COMPRESSION: memset(&bz, 0x0, sizeof(bz)); bz.avail_in = (unsigned)(len - 1); bz.next_in = (char *)(void *)p + 1; bz.next_out = (char *)(void *)unzmem->mem; bz.avail_out = (unsigned)unzmem->size; break; } switch(compressed->compalg) { case ZIP_COMPRESSION: ok = (inflateInit2(&z, -15) == Z_OK); break; case ZLIB_COMPRESSION: ok = (inflateInit(&z) == Z_OK); break; case BZIP2_COMPRESSION: ok = (netpgpv_BZ2_bzDecompressInit(&bz, 1, 0) == BZ_OK); break; } if (!ok) { printf("read_compressed: initialisation failed!\n"); return 0; } switch(compressed->compalg) { case ZIP_COMPRESSION: case ZLIB_COMPRESSION: ok = (inflate(&z, Z_FINISH) == Z_STREAM_END); unzmem->size = z.total_out; break; case BZIP2_COMPRESSION: ok = (netpgpv_BZ2_bzDecompress(&bz) == BZ_STREAM_END); unzmem->size = ((uint64_t)bz.total_out_hi32 << 32) | bz.total_out_lo32; break; } if (!ok) { printf("read_compressed: inflate failed!\n"); return 0; } return 1; } /* parse one pass signature packet */ static int read_onepass_sig(pgpv_onepass_t *onepasspkt, uint8_t *mem) { onepasspkt->version = mem[0]; onepasspkt->type = mem[1]; onepasspkt->hashalg = mem[2]; onepasspkt->keyalg = mem[3]; memcpy(onepasspkt->keyid, &mem[4], sizeof(onepasspkt->keyid)); onepasspkt->nested = mem[12]; return 1; } /* parse public key packet */ static int read_pubkey(pgpv_pubkey_t *pubkey, uint8_t *mem, size_t pktlen, int pbn) { size_t off; off = 0; pubkey->version = mem[off++]; pubkey->birth = get_32(&mem[off]); off += 4; if (pubkey->version == 2 || pubkey->version == 3) { pubkey->expiry = get_16(&mem[off]) * DAYSECS; off += 2; } if ((pubkey->keyalg = mem[off++]) == 0) { pubkey->keyalg = PUBKEY_RSA_ENCRYPT_OR_SIGN; printf("got unusual pubkey keyalg %u\n", mem[off - 1]); } switch(pubkey->keyalg) { case PUBKEY_RSA_ENCRYPT_OR_SIGN: case PUBKEY_RSA_ENCRYPT: case PUBKEY_RSA_SIGN: if (!get_mpi(&pubkey->bn[RSA_N], &mem[off], pktlen, &off) || !get_mpi(&pubkey->bn[RSA_E], &mem[off], pktlen, &off)) { return 0; } break; case PUBKEY_ELGAMAL_ENCRYPT: case PUBKEY_ELGAMAL_ENCRYPT_OR_SIGN: if (!get_mpi(&pubkey->bn[ELGAMAL_P], &mem[off], pktlen, &off) || !get_mpi(&pubkey->bn[ELGAMAL_Y], &mem[off], pktlen, &off)) { return 0; } break; case PUBKEY_DSA: if (!get_mpi(&pubkey->bn[DSA_P], &mem[off], pktlen, &off) || !get_mpi(&pubkey->bn[DSA_Q], &mem[off], pktlen, &off) || !get_mpi(&pubkey->bn[DSA_G], &mem[off], pktlen, &off) || !get_mpi(&pubkey->bn[DSA_Y], &mem[off], pktlen, &off)) { return 0; } break; default: printf("hi, different type of pubkey here %u\n", pubkey->keyalg); break; } if (pbn) { print_key_mpis(pubkey->bn, pubkey->keyalg); } return 1; } /* parse a user attribute */ static int read_userattr(pgpv_userattr_t *userattr, uint8_t *p, size_t pktlen) { pgpv_string_t subattr; const int is_subpkt = 0; const int indian = 1; unsigned lenlen; uint16_t imagelen; size_t cc; userattr->len = pktlen; for (cc = 0 ; cc < pktlen ; cc += subattr.size + lenlen + 1) { subattr.size = get_pkt_len(1, p, 0, is_subpkt); lenlen = get_pkt_len_len(1, p, is_subpkt); if (lenlen > pktlen) { printf("weird lenlen %u\n", lenlen); return 0; } p += lenlen; if (*p++ != 1) { printf("image type (%u) != 1. weird packet\n", *(p - 1)); } memcpy(&imagelen, p, sizeof(imagelen)); if (!*(const char *)(const void *)&indian) { /* big endian - byteswap length */ imagelen = (((unsigned)imagelen & 0xff) << 8) | (((unsigned)imagelen >> 8) & 0xff); } subattr.data = p + 3; p += subattr.size; ARRAY_APPEND(userattr->subattrs, subattr); } return 1; } #define LITDATA_BINARY 'b' #define LITDATA_TEXT 't' #define LITDATA_UTF8 'u' /* parse literal packet */ static int read_litdata(pgpv_t *pgp, pgpv_litdata_t *litdata, uint8_t *p, size_t size) { size_t cc; cc = 0; switch(litdata->format = p[cc++]) { case LITDATA_BINARY: case LITDATA_TEXT: case LITDATA_UTF8: litdata->namelen = 0; break; default: printf("weird litdata format %u\n", litdata->format); break; } litdata->filename.size = litdata->namelen = p[cc++]; litdata->filename.data = &p[cc]; litdata->filename.allocated = 0; cc += litdata->namelen; litdata->secs = get_32(&p[cc]); cc += 4; litdata->s.data = &p[cc]; litdata->len = litdata->s.size = size - cc; litdata->mem = ARRAY_COUNT(pgp->areas) - 1; litdata->offset = cc; return 1; } /* parse a single packet */ static int read_pkt(pgpv_t *pgp, pgpv_mem_t *mem) { const int isprimary = 1; pgpv_pkt_t pkt; pgpv_mem_t *newmem; unsigned lenlen; uint8_t ispartial; size_t size; memset(&pkt, 0x0, sizeof(pkt)); pkt.tag = mem->mem[mem->cc++]; if (!(pkt.tag & PKT_ALWAYS_ON)) { printf("BAD PACKET - bit 7 not 1, offset %zu!\n", mem->cc - 1); } pkt.newfmt = (pkt.tag & PKT_NEWFMT_MASK); pkt.tag = (pkt.newfmt) ? (pkt.tag & PKT_NEWFMT_TAG_MASK) : (((unsigned)pkt.tag & PKT_OLDFMT_TAG_MASK) >> 2); ispartial = (pkt.newfmt && IS_PARTIAL(mem->mem[mem->cc])); pkt.s.size = get_pkt_len(pkt.newfmt, &mem->mem[mem->cc], mem->size - mem->cc, isprimary); lenlen = get_pkt_len_len(pkt.newfmt, &mem->mem[mem->cc], isprimary); pkt.offset = mem->cc; mem->cc += lenlen; pkt.mement = (uint8_t)(mem - ARRAY_ARRAY(pgp->areas)); pkt.s.data = &mem->mem[mem->cc]; if (strchr(mem->allowed, pkt.tag) == NULL) { printf("packet %d not allowed for operation %s\n", pkt.tag, pgp->op); return 0; } size = pkt.s.size; if (ispartial) { pkt.s.size = fixup_partials(pgp, &mem->mem[mem->cc - lenlen], pkt.s.size, mem->size, &size); newmem = &ARRAY_LAST(pgp->areas); pkt.mement = (uint8_t)(newmem - ARRAY_ARRAY(pgp->areas)); pkt.s.data = newmem->mem; size -= 1; } switch(pkt.tag) { case SIGNATURE_PKT: if (!read_sigpkt(pgp, pkt.mement, &pkt.u.sigpkt, pkt.s.data, pkt.s.size)) { return 0; } break; case ONEPASS_SIGNATURE_PKT: read_onepass_sig(&pkt.u.onepass, pkt.s.data); break; case PUBKEY_PKT: case PUB_SUBKEY_PKT: break; case LITDATA_PKT: read_litdata(pgp, &pkt.u.litdata, pkt.s.data, pkt.s.size); break; case TRUST_PKT: pkt.u.trust.level = pkt.s.data[0]; pkt.u.trust.amount = pkt.s.data[1]; break; case USERID_PKT: pkt.u.userid.size = pkt.s.size; pkt.u.userid.data = pkt.s.data; pkt.u.userid.allocated = 0; break; case COMPRESSED_DATA_PKT: read_compressed(pgp, &pkt.u.compressed, pkt.s.data, pkt.s.size); ARRAY_APPEND(pgp->pkts, pkt); read_all_packets(pgp, &ARRAY_LAST(pgp->areas), pgp->op); break; case USER_ATTRIBUTE_PKT: read_userattr(&pkt.u.userattr, pkt.s.data, pkt.s.size); break; default: printf("hi, need to implement %d, offset %zu\n", pkt.tag, mem->cc); break; } mem->cc += size; if (pkt.tag != COMPRESSED_DATA_PKT) { /* compressed was added earlier to preserve pkt ordering */ ARRAY_APPEND(pgp->pkts, pkt); } return 1; } /* checks the tag type of a packet */ static int pkt_is(pgpv_t *pgp, int wanted) { return (ARRAY_ELEMENT(pgp->pkts, pgp->pkt).tag == wanted); } /* checks the packet is a signature packet, and the signature type is the expected one */ static int pkt_sigtype_is(pgpv_t *pgp, int wanted) { if (!pkt_is(pgp, SIGNATURE_PKT)) { return 0; } return (ARRAY_ELEMENT(pgp->pkts, pgp->pkt).u.sigpkt.sig.type == wanted); } /* check for expected type of packet, and move to the next */ static int pkt_accept(pgpv_t *pgp, int expected) { int got; if ((got = ARRAY_ELEMENT(pgp->pkts, pgp->pkt).tag) == expected) { pgp->pkt += 1; return 1; } printf("problem at token %zu, expcted %d, got %d\n", pgp->pkt, expected, got); return 0; } /* recognise signature (and trust) packet */ static int recog_signature(pgpv_t *pgp, pgpv_signature_t *signature) { if (!pkt_is(pgp, SIGNATURE_PKT)) { printf("recog_signature: not a signature packet\n"); return 0; } memcpy(signature, &ARRAY_ELEMENT(pgp->pkts, pgp->pkt).u.sigpkt.sig, sizeof(*signature)); pgp->pkt += 1; if (pkt_is(pgp, TRUST_PKT)) { pkt_accept(pgp, TRUST_PKT); } return 1; } /* recognise user id packet */ static int recog_userid(pgpv_t *pgp, pgpv_signed_userid_t *userid) { pgpv_signature_t signature; pgpv_pkt_t *pkt; memset(userid, 0x0, sizeof(*userid)); if (!pkt_is(pgp, USERID_PKT)) { printf("recog_userid: not %d\n", USERID_PKT); return 0; } pkt = &ARRAY_ELEMENT(pgp->pkts, pgp->pkt); userid->userid.size = pkt->s.size; userid->userid.data = pkt->s.data; userid->userid.allocated = 0; pgp->pkt += 1; while (pkt_is(pgp, SIGNATURE_PKT)) { if (!recog_signature(pgp, &signature)) { printf("recog_userid: can't recognise signature/trust\n"); return 0; } ARRAY_APPEND(userid->signatures, ARRAY_COUNT(pgp->signatures)); ARRAY_APPEND(pgp->signatures, signature); if (signature.primary_userid) { userid->primary_userid = signature.primary_userid; } if (signature.revoked) { userid->revoked = signature.revoked; } } return 1; } /* recognise user attributes packet */ static int recog_userattr(pgpv_t *pgp, pgpv_signed_userattr_t *userattr) { pgpv_signature_t signature; memset(userattr, 0x0, sizeof(*userattr)); if (!pkt_is(pgp, USER_ATTRIBUTE_PKT)) { printf("recog_userattr: not %d\n", USER_ATTRIBUTE_PKT); return 0; } userattr->userattr = ARRAY_ELEMENT(pgp->pkts, pgp->pkt).u.userattr; pgp->pkt += 1; while (pkt_is(pgp, SIGNATURE_PKT)) { if (!recog_signature(pgp, &signature)) { printf("recog_userattr: can't recognise signature/trust\n"); return 0; } ARRAY_APPEND(userattr->signatures, ARRAY_COUNT(pgp->signatures)); ARRAY_APPEND(pgp->signatures, signature); if (signature.revoked) { userattr->revoked = signature.revoked; } } return 1; } /* recognise a sub key */ static int recog_subkey(pgpv_t *pgp, pgpv_signed_subkey_t *subkey) { pgpv_signature_t signature; pgpv_pkt_t *pkt; pkt = &ARRAY_ELEMENT(pgp->pkts, pgp->pkt); memset(subkey, 0x0, sizeof(*subkey)); read_pubkey(&subkey->subkey, pkt->s.data, pkt->s.size, 0); pgp->pkt += 1; if (pkt_sigtype_is(pgp, SIGTYPE_KEY_REVOCATION) || pkt_sigtype_is(pgp, SIGTYPE_SUBKEY_REVOCATION) || pkt_sigtype_is(pgp, SIGTYPE_CERT_REVOCATION)) { recog_signature(pgp, &signature); subkey->revoc_self_sig = signature; } do { if (!pkt_is(pgp, SIGNATURE_PKT)) { printf("recog_subkey: not signature packet at %zu\n", pgp->pkt); return 0; } if (!recog_signature(pgp, &signature)) { printf("recog_subkey: bad signature/trust at %zu\n", pgp->pkt); return 0; } ARRAY_APPEND(subkey->signatures, ARRAY_COUNT(pgp->signatures)); ARRAY_APPEND(pgp->signatures, signature); if (signature.keyexpiry) { /* XXX - check it's a good key expiry */ subkey->subkey.expiry = signature.keyexpiry; } } while (pkt_is(pgp, SIGNATURE_PKT)); return 1; } /* use a sparse map for the text strings here to save space */ static const char *keyalgs[] = { "[Unknown]", "RSA (Encrypt or Sign)", "RSA (Encrypt Only)", "RSA (Sign Only)", "Elgamal (Encrypt Only)", "DSA", "Elliptic Curve", "ECDSA", "Elgamal (Encrypt or Sign)" }; #define MAX_KEYALG 21 static const char *keyalgmap = "\0\01\02\03\0\0\0\0\0\0\0\0\0\0\0\0\04\05\06\07\010\011"; /* return human readable name for key algorithm */ static const char * fmtkeyalg(uint8_t keyalg) { return keyalgs[(uint8_t)keyalgmap[(keyalg >= MAX_KEYALG) ? 0 : keyalg]]; } /* return the number of bits in the public key */ static unsigned numkeybits(const pgpv_pubkey_t *pubkey) { switch(pubkey->keyalg) { case PUBKEY_RSA_ENCRYPT_OR_SIGN: case PUBKEY_RSA_ENCRYPT: case PUBKEY_RSA_SIGN: return pubkey->bn[RSA_N].bits; case PUBKEY_DSA: case PUBKEY_ECDSA: return pubkey->bn[DSA_P].bits; //return BITS_TO_BYTES(pubkey->bn[DSA_Q].bits) * 64; case PUBKEY_ELGAMAL_ENCRYPT: case PUBKEY_ELGAMAL_ENCRYPT_OR_SIGN: return pubkey->bn[ELGAMAL_P].bits; default: return 0; } } /* print a public key */ static int fmt_pubkey(obuf_t *obuf, pgpv_pubkey_t *pubkey, const char *leader) { char newbuf[128]; int cc; cc = snprintf(newbuf, sizeof(newbuf), " %u/%s ", numkeybits(pubkey), fmtkeyalg(pubkey->keyalg)); if (!obuf_add_mem(obuf, leader, strlen(leader)) || !obuf_add_mem(obuf, newbuf, cc)) { return 0; } if (!fmt_binary(obuf, pubkey->keyid, PGPV_KEYID_LEN)) { return 0; } if (!fmt_time(obuf, " ", pubkey->birth, "", 0)) { return 0; } if (pubkey->expiry) { if (!fmt_time(obuf, " [Expiry ", pubkey->birth + pubkey->expiry, "]", 0)) { return 0; } } if (!obuf_add_mem(obuf, "\n", 1)) { return 0; } return fmt_fingerprint(obuf, &pubkey->fingerprint, "fingerprint "); } /* we add 1 to revocation value to denote compromised */ #define COMPROMISED (0x02 + 1) /* format a userid - used to order the userids when formatting */ static int fmt_userid(obuf_t *obuf, pgpv_t *pgp, pgpv_primarykey_t *primary, uint8_t u) { pgpv_signed_userid_t *userid; const char *s; uint64_t id; id = ARRAY_ELEMENT(primary->signed_userids, u); userid = &ARRAY_ELEMENT(pgp->signed_userids, id); s = (userid->revoked == COMPROMISED) ? " [COMPROMISED AND REVOKED]\n" : (userid->revoked) ? " [REVOKED]\n" : "\n"; return obuf_add_mem(obuf, "uid ", 14) && obuf_add_mem(obuf, userid->userid.data, userid->userid.size) && obuf_add_mem(obuf, s, strlen(s)); } /* format a trust sig - used to order the userids when formatting */ static int fmt_trust(obuf_t *obuf, pgpv_signature_t *sig) { if (!obuf_add_mem(obuf, "trust ", 15) || !fmt_binary(obuf, sig->signer, PGPV_KEYID_LEN)) { return 0; } return obuf_add_mem(obuf, "\n", 1); } /* print a primary key, per RFC 4880 */ static int fmt_primary(obuf_t *obuf, pgpv_t *pgp, pgpv_primarykey_t *primary, unsigned subkey, const char *modifiers) { pgpv_signed_userid_t *userid; pgpv_signed_subkey_t *signed_subkey; pgpv_pubkey_t *pubkey; unsigned i; unsigned j; uint64_t id; if (subkey == 0) { pubkey = &primary->primary; } else { id = ARRAY_ELEMENT(primary->signed_subkeys, subkey); pubkey = &ARRAY_ELEMENT(pgp->signed_subkeys, id).subkey; } if (!fmt_pubkey(obuf, pubkey, "signature ")) { return 0; } if (!fmt_userid(obuf, pgp, primary, primary->primary_userid)) { return 0; } for (i = 0 ; i < ARRAY_COUNT(primary->signed_userids) ; i++) { if (i != primary->primary_userid) { if (!fmt_userid(obuf, pgp, primary, i)) { return 0; } if (strcasecmp(modifiers, "trust") == 0) { id = ARRAY_ELEMENT(primary->signed_userids, i); userid = &ARRAY_ELEMENT(pgp->signed_userids, id); for (j = 0 ; j < ARRAY_COUNT(userid->signatures) ; j++) { if (!fmt_trust(obuf, &ARRAY_ELEMENT(pgp->signatures, ARRAY_ELEMENT(userid->signatures, j)))) { return 0; } } } } } if (strcasecmp(modifiers, "subkeys") == 0) { for (i = 0 ; i < ARRAY_COUNT(primary->signed_subkeys) ; i++) { id = ARRAY_ELEMENT(primary->signed_subkeys, i); signed_subkey = &ARRAY_ELEMENT(pgp->signed_subkeys, id); if (!fmt_pubkey(obuf, &signed_subkey->subkey, "encryption")) { return 0; } } } return obuf_add_mem(obuf, "\n", 1); } /* check the padding on the signature */ static int rsa_padding_check_none(uint8_t *to, int tlen, const uint8_t *from, int flen, int num) { USE_ARG(num); if (flen > tlen) { printf("from length larger than to length\n"); return -1; } (void) memset(to, 0x0, (size_t)(tlen - flen)); (void) memcpy(to + tlen - flen, from, (size_t)flen); return tlen; } #define RSA_MAX_MODULUS_BITS 16384 #define RSA_SMALL_MODULUS_BITS 3072 #define RSA_MAX_PUBEXP_BITS 64 /* exponent limit enforced for "large" modulus only */ /* check against the exponent/moudulo operation */ static int lowlevel_rsa_public_check(const uint8_t *encbuf, int enclen, uint8_t *dec, const netpgpv_rsa_pubkey_t *rsa) { uint8_t *decbuf; PGPV_BIGNUM *decbn; PGPV_BIGNUM *encbn; int decbytes; int nbytes; int r; nbytes = 0; r = -1; decbuf = NULL; decbn = encbn = NULL; if (PGPV_BN_num_bits(rsa->n) > RSA_MAX_MODULUS_BITS) { printf("rsa r modulus too large\n"); goto err; } if (PGPV_BN_cmp(rsa->n, rsa->e) <= 0) { printf("rsa r bad n value\n"); goto err; } if (PGPV_BN_num_bits(rsa->n) > RSA_SMALL_MODULUS_BITS && PGPV_BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS) { printf("rsa r bad exponent limit\n"); goto err; } nbytes = PGPV_BN_num_bytes(rsa->n); if ((encbn = PGPV_BN_new()) == NULL || (decbn = PGPV_BN_new()) == NULL || (decbuf = calloc(1, (size_t)nbytes)) == NULL) { printf("allocation failure\n"); goto err; } if (enclen > nbytes) { printf("rsa r > mod len\n"); goto err; } if (PGPV_BN_bin2bn(encbuf, enclen, encbn) == NULL) { printf("null encrypted BN\n"); goto err; } if (PGPV_BN_cmp(encbn, rsa->n) >= 0) { printf("rsa r data too large for modulus\n"); goto err; } if (PGPV_BN_mod_exp(decbn, encbn, rsa->e, rsa->n, NULL) < 0) { printf("PGPV_BN_mod_exp < 0\n"); goto err; } decbytes = PGPV_BN_num_bytes(decbn); (void) PGPV_BN_bn2bin(decbn, decbuf); if ((r = rsa_padding_check_none(dec, nbytes, decbuf, decbytes, 0)) < 0) { printf("rsa r padding check failed\n"); } err: PGPV_BN_clear_free(encbn); PGPV_BN_clear_free(decbn); if (decbuf != NULL) { (void) memset(decbuf, 0x0, nbytes); free(decbuf); } return r; } /* verify */ static int rsa_public_decrypt(int enclen, const unsigned char *enc, unsigned char *dec, NETPGPV_RSA *rsa, int padding) { netpgpv_rsa_pubkey_t pub; int ret; if (enc == NULL || dec == NULL || rsa == NULL) { return 0; } USE_ARG(padding); (void) memset(&pub, 0x0, sizeof(pub)); pub.n = PGPV_BN_dup(rsa->n); pub.e = PGPV_BN_dup(rsa->e); ret = lowlevel_rsa_public_check(enc, enclen, dec, &pub); PGPV_BN_clear_free(pub.n); PGPV_BN_clear_free(pub.e); return ret; } #define SUBKEY_LEN(x) (80 + 80) #define SIG_LEN 80 #define UID_LEN 80 /* return worst case number of bytes needed to format a primary key */ static size_t estimate_primarykey_size(pgpv_primarykey_t *primary) { size_t cc; cc = SUBKEY_LEN("signature") + (ARRAY_COUNT(primary->signed_userids) * UID_LEN) + (ARRAY_COUNT(primary->signed_subkeys) * SUBKEY_LEN("encrypt uids")); return cc; } /* use public decrypt to verify a signature */ static int pgpv_rsa_public_decrypt(uint8_t *out, const uint8_t *in, size_t length, const pgpv_pubkey_t *pubkey) { NETPGPV_RSA *orsa; int n; if ((orsa = calloc(1, sizeof(*orsa))) == NULL) { return 0; } orsa->n = pubkey->bn[RSA_N].bn; orsa->e = pubkey->bn[RSA_E].bn; n = rsa_public_decrypt((int)length, in, out, orsa, NETPGPV_RSA_NO_PADDING); orsa->n = orsa->e = NULL; free(orsa); return n; } /* verify rsa signature */ static int rsa_verify(uint8_t *calculated, unsigned calclen, uint8_t hashalg, pgpv_bignum_t *bn, pgpv_pubkey_t *pubkey) { unsigned prefixlen; unsigned decryptc; unsigned i; uint8_t decrypted[8192]; uint8_t sigbn[8192]; uint8_t prefix[64]; size_t keysize; keysize = BITS_TO_BYTES(pubkey->bn[RSA_N].bits); PGPV_BN_bn2bin(bn[RSA_SIG].bn, sigbn); decryptc = pgpv_rsa_public_decrypt(decrypted, sigbn, BITS_TO_BYTES(bn[RSA_SIG].bits), pubkey); if (decryptc != keysize || (decrypted[0] != 0 || decrypted[1] != 1)) { return 0; } if ((prefixlen = digest_get_prefix((unsigned)hashalg, prefix, sizeof(prefix))) == 0) { printf("rsa_verify: unknown hash algorithm: %d\n", hashalg); return 0; } for (i = 2 ; i < keysize - prefixlen - calclen - 1 ; i++) { if (decrypted[i] != 0xff) { return 0; } } if (decrypted[i++] != 0x0) { return 0; } if (memcmp(&decrypted[i], prefix, prefixlen) != 0) { printf("rsa_verify: wrong hash algorithm\n"); return 0; } return memcmp(&decrypted[i + prefixlen], calculated, calclen) == 0; } /* return 1 if bn <= 0 */ static int bignum_is_bad(PGPV_BIGNUM *bn) { return PGPV_BN_is_zero(bn) || PGPV_BN_is_negative(bn); } #define BAD_BIGNUM(s, k) \ (bignum_is_bad((s)->bn) || PGPV_BN_cmp((s)->bn, (k)->bn) >= 0) #ifndef DSA_MAX_MODULUS_BITS #define DSA_MAX_MODULUS_BITS 10000 #endif /* verify DSA signature */ static int verify_dsa_sig(uint8_t *calculated, unsigned calclen, pgpv_bignum_t *sig, pgpv_pubkey_t *pubkey) { PGPV_BIGNUM *M; PGPV_BIGNUM *W; PGPV_BIGNUM *t1; unsigned qbits; uint8_t calcnum[128]; uint8_t signum[128]; int ret; if (pubkey->bn[DSA_P].bn == NULL || pubkey->bn[DSA_Q].bn == NULL || pubkey->bn[DSA_G].bn == NULL) { return 0; } M = W = t1 = NULL; qbits = pubkey->bn[DSA_Q].bits; switch(qbits) { case 160: case 224: case 256: break; default: printf("dsa: bad # of Q bits\n"); return 0; } if (pubkey->bn[DSA_P].bits > DSA_MAX_MODULUS_BITS) { printf("dsa: p too large\n"); return 0; } if (calclen > SHA256_DIGEST_LENGTH) { printf("dsa: digest too long\n"); return 0; } ret = 0; if ((M = PGPV_BN_new()) == NULL || (W = PGPV_BN_new()) == NULL || (t1 = PGPV_BN_new()) == NULL || BAD_BIGNUM(&sig[DSA_R], &pubkey->bn[DSA_Q]) || BAD_BIGNUM(&sig[DSA_S], &pubkey->bn[DSA_Q]) || PGPV_BN_mod_inverse(W, sig[DSA_S].bn, pubkey->bn[DSA_Q].bn, NULL) == NULL) { goto done; } if (calclen > qbits / 8) { calclen = qbits / 8; } if (PGPV_BN_bin2bn(calculated, (int)calclen, M) == NULL || !PGPV_BN_mod_mul(M, M, W, pubkey->bn[DSA_Q].bn, NULL) || !PGPV_BN_mod_mul(W, sig[DSA_R].bn, W, pubkey->bn[DSA_Q].bn, NULL) || !PGPV_BN_mod_exp(t1, pubkey->bn[DSA_G].bn, M, pubkey->bn[DSA_P].bn, NULL) || !PGPV_BN_mod_exp(W, pubkey->bn[DSA_Y].bn, W, pubkey->bn[DSA_P].bn, NULL) || !PGPV_BN_mod_mul(t1, t1, W, pubkey->bn[DSA_P].bn, NULL) || !PGPV_BN_div(NULL, t1, t1, pubkey->bn[DSA_Q].bn, NULL)) { goto done; } /* only compare the first q bits */ PGPV_BN_bn2bin(t1, calcnum); PGPV_BN_bn2bin(sig[DSA_R].bn, signum); ret = memcmp(calcnum, signum, BITS_TO_BYTES(qbits)) == 0; done: if (M) { PGPV_BN_clear_free(M); } if (W) { PGPV_BN_clear_free(W); } if (t1) { PGPV_BN_clear_free(t1); } return ret; } #define TIME_SNPRINTF(_cc, _buf, _size, _fmt, _val) do { \ time_t _t; \ char *_s; \ \ _t = _val; \ _s = ctime(&_t); \ _cc += snprintf(_buf, _size, _fmt, _s); \ } while(/*CONSTCOND*/0) /* check dates on signature and key are valid */ static size_t valid_dates(pgpv_signature_t *signature, pgpv_pubkey_t *pubkey, char *buf, size_t size) { time_t now; time_t t; size_t cc; cc = 0; if (signature->birth < pubkey->birth) { TIME_SNPRINTF(cc, buf, size, "Signature time (%.24s) was before pubkey creation ", signature->birth); TIME_SNPRINTF(cc, &buf[cc], size - cc, "(%s)", pubkey->birth); return cc; } now = time(NULL); if (signature->expiry != 0) { if ((t = signature->birth + signature->expiry) < now) { TIME_SNPRINTF(cc, buf, size, "Signature expired on %.24s", t); return cc; } } if (now < signature->birth) { TIME_SNPRINTF(cc, buf, size, "Signature not valid before %.24s", signature->birth); return cc; } return 0; } /* check if the signing key has expired */ static int key_expired(pgpv_pubkey_t *pubkey, char *buf, size_t size) { time_t now; time_t t; size_t cc; now = time(NULL); cc = 0; if (pubkey->expiry != 0) { if ((t = pubkey->birth + pubkey->expiry) < now) { TIME_SNPRINTF(cc, buf, size, "Pubkey expired on %.24s", t); return (int)cc; } } if (now < pubkey->birth) { TIME_SNPRINTF(cc, buf, size, "Pubkey not valid before %.24s", pubkey->birth); return (int)cc; } return 0; } /* find the leading onepass packet */ static size_t find_onepass(pgpv_cursor_t *cursor, size_t datastart) { size_t pkt; for (pkt = datastart ; pkt < ARRAY_COUNT(cursor->pgp->pkts) ; pkt++) { if (ARRAY_ELEMENT(cursor->pgp->pkts, pkt).tag == ONEPASS_SIGNATURE_PKT) { return pkt + 1; } } snprintf(cursor->why, sizeof(cursor->why), "No signature to verify"); return 0; } static const char *armor_begins[] = { "-----BEGIN PGP SIGNED MESSAGE-----\n", "-----BEGIN PGP MESSAGE-----\n", NULL }; /* return non-zero if the buf introduces an armored message */ static int is_armored(const char *buf, size_t size) { const char **arm; const char *nl; size_t n; if ((nl = memchr(buf, '\n', size)) == NULL) { return 0; } n = (size_t)(nl - buf); for (arm = armor_begins ; *arm ; arm++) { if (strncmp(buf, *arm, n) == 0) { return 1; } } return 0; } /* find first occurrence of pat binary string in block */ static void * find_bin_string(const void *blockarg, size_t blen, const void *pat, size_t plen) { const uint8_t *block; const uint8_t *bp; if (plen == 0) { return __UNCONST(blockarg); } if (blen < plen) { return NULL; } for (bp = block = blockarg ; (size_t)(bp - block) < blen - plen + 1 ; bp++) { if (memcmp(bp, pat, plen) == 0) { return __UNCONST(bp); } } return NULL; } /* store string in allocated memory */ static uint8_t * pgpv_strdup(const char *s) { uint8_t *cp; size_t len; len = strlen(s); if ((cp = calloc(len + 1, 1)) != NULL) { memcpy(cp, s, len); } return cp; } #define SIGSTART "-----BEGIN PGP SIGNATURE-----\n" #define SIGEND "-----END PGP SIGNATURE-----\n" /* for ascii armor, we don't get a onepass packet - make one */ static const char *cons_onepass = "\304\015\003\0\0\0\0\377\377\377\377\377\377\377\377\1"; /* read ascii armor */ static int read_ascii_armor(pgpv_cursor_t *cursor, pgpv_mem_t *mem, const char *filename) { pgpv_onepass_t *onepass; pgpv_sigpkt_t *sigpkt; pgpv_pkt_t litdata; uint8_t binsig[8192]; uint8_t *datastart; uint8_t *sigend; uint8_t *p; size_t binsigsize; /* cons up litdata pkt */ memset(&litdata, 0x0, sizeof(litdata)); litdata.u.litdata.mem = ARRAY_COUNT(cursor->pgp->areas) - 1; p = mem->mem; /* jump over signed message line */ if ((p = find_bin_string(mem->mem, mem->size, "\n\n", 2)) == NULL) { snprintf(cursor->why, sizeof(cursor->why), "malformed armor at offset 0"); return 0; } p += 2; litdata.tag = LITDATA_PKT; litdata.s.data = p; litdata.u.litdata.offset = (size_t)(p - mem->mem); litdata.u.litdata.filename.data = pgpv_strdup(filename); litdata.u.litdata.filename.allocated = 1; if ((p = find_bin_string(datastart = p, mem->size - litdata.offset, SIGSTART, sizeof(SIGSTART) - 1)) == NULL) { snprintf(cursor->why, sizeof(cursor->why), "malformed armor - no sig - at %zu", (size_t)(p - mem->mem)); return 0; } litdata.u.litdata.len = litdata.s.size = (size_t)(p - datastart); /* this puts p at the newline character, so it will find \n\n if no version */ p += strlen(SIGSTART) - 1; if ((p = find_bin_string(p, mem->size, "\n\n", 2)) == NULL) { snprintf(cursor->why, sizeof(cursor->why), "malformed armed signature at %zu", (size_t)(p - mem->mem)); return 0; } p += 2; sigend = find_bin_string(p, mem->size, SIGEND, sizeof(SIGEND) - 1); if (sigend == NULL) { snprintf(cursor->why, sizeof(cursor->why), "malformed armor - no end sig - at %zu", (size_t)(p - mem->mem)); return 0; } binsigsize = netpgpv_b64decode((char *)p, (size_t)(sigend - p), binsig, sizeof(binsig)); read_binary_memory(cursor->pgp, "signature", cons_onepass, 15); ARRAY_APPEND(cursor->pgp->pkts, litdata); read_binary_memory(cursor->pgp, "signature", binsig, binsigsize - 3); /* XXX - hardwired - 3 is format and length */ /* fix up packets in the packet array now we have them there */ onepass = &ARRAY_ELEMENT(cursor->pgp->pkts, ARRAY_COUNT(cursor->pgp->pkts) - 1 - 2).u.onepass; sigpkt = &ARRAY_LAST(cursor->pgp->pkts).u.sigpkt; memcpy(onepass->keyid, sigpkt->sig.signer, sizeof(onepass->keyid)); onepass->hashalg = sigpkt->sig.hashalg; onepass->keyalg = sigpkt->sig.keyalg; return 1; } /* read ascii armor from a file */ static int read_ascii_armor_file(pgpv_cursor_t *cursor, const char *filename) { /* cons up litdata pkt */ read_file(cursor->pgp, filename); return read_ascii_armor(cursor, &ARRAY_LAST(cursor->pgp->areas), filename); } /* read ascii armor from memory */ static int read_ascii_armor_memory(pgpv_cursor_t *cursor, const void *p, size_t size) { pgpv_mem_t *mem; /* cons up litdata pkt */ ARRAY_EXPAND(cursor->pgp->areas); ARRAY_COUNT(cursor->pgp->areas) += 1; mem = &ARRAY_LAST(cursor->pgp->areas); memset(mem, 0x0, sizeof(*mem)); mem->size = size; mem->mem = __UNCONST(p); mem->dealloc = 0; return read_ascii_armor(cursor, mem, "[stdin]"); } /* set up the data to verify */ static int setup_data(pgpv_cursor_t *cursor, pgpv_t *pgp, const void *p, ssize_t size) { FILE *fp; char buf[BUFSIZ]; if (cursor == NULL || pgp == NULL || p == NULL) { return 0; } memset(cursor, 0x0, sizeof(*cursor)); ARRAY_APPEND(pgp->datastarts, pgp->pkt); cursor->pgp = pgp; if (size < 0) { /* we have a file name in p */ if ((fp = fopen(p, "r")) == NULL) { snprintf(cursor->why, sizeof(cursor->why), "No such file '%s'", (const char *)p); return 0; } if (fgets(buf, (int)sizeof(buf), fp) == NULL) { fclose(fp); snprintf(cursor->why, sizeof(cursor->why), "can't read file '%s'", (const char *)p); return 0; } if (is_armored(buf, sizeof(buf))) { read_ascii_armor_file(cursor, p); } else { read_binary_file(pgp, "signature", "%s", (const char *)p); } fclose(fp); } else { if (is_armored(p, (size_t)size)) { read_ascii_armor_memory(cursor, p, (size_t)size); } else { read_binary_memory(pgp, "signature", p, (size_t)size); } } return 1; } /* get the data and size from litdata packet */ static uint8_t * get_literal_data(pgpv_cursor_t *cursor, pgpv_litdata_t *litdata, size_t *size) { pgpv_mem_t *mem; if (litdata->s.data == NULL && litdata->s.size == 0) { mem = &ARRAY_ELEMENT(cursor->pgp->areas, litdata->mem); *size = litdata->len; return &mem->mem[litdata->offset]; } *size = litdata->s.size; return litdata->s.data; } /* RFC 4880 describes the structure of v4 keys as: Primary-Key [Revocation Self Signature] [Direct Key Signature...] User ID [Signature ...] [User ID [Signature ...] ...] [User Attribute [Signature ...] ...] [[Subkey [Binding-Signature-Revocation] Primary-Key-Binding-Signature] ...] and that's implemented below as a recursive descent parser. It has had to be modified, though: see the comment some keys out there have user ids where they shouldn't to look like: Primary-Key [Revocation Self Signature] [Direct Key Signature...] [User ID [Signature ...] [User ID [Signature ...] ...] [User Attribute [Signature ...] ...] [Subkey [Binding-Signature-Revocation] Primary-Key-Binding-Signature] ...] to accommodate keyrings set up by gpg */ /* recognise a primary key */ static int recog_primary_key(pgpv_t *pgp, pgpv_primarykey_t *primary) { pgpv_signed_userattr_t userattr; pgpv_signed_userid_t userid; pgpv_signed_subkey_t subkey; pgpv_signature_t signature; pgpv_pkt_t *pkt; pkt = &ARRAY_ELEMENT(pgp->pkts, pgp->pkt); memset(primary, 0x0, sizeof(*primary)); read_pubkey(&primary->primary, pkt->s.data, pkt->s.size, 0); pgp->pkt += 1; if (pkt_sigtype_is(pgp, SIGTYPE_KEY_REVOCATION)) { if (!recog_signature(pgp, &primary->revoc_self_sig)) { printf("recog_primary_key: no signature/trust at PGPV_SIGTYPE_KEY_REVOCATION\n"); return 0; } } while (pkt_sigtype_is(pgp, SIGTYPE_DIRECT_KEY)) { if (!recog_signature(pgp, &signature)) { printf("recog_primary_key: no signature/trust at PGPV_SIGTYPE_DIRECT_KEY\n"); return 0; } if (signature.keyexpiry) { /* XXX - check it's a good key expiry */ primary->primary.expiry = signature.keyexpiry; } ARRAY_APPEND(primary->signatures, ARRAY_COUNT(pgp->signatures)); ARRAY_APPEND(pgp->signatures, signature); } /* some keys out there have user ids where they shouldn't */ do { if (!recog_userid(pgp, &userid)) { printf("recog_primary_key: not userid\n"); return 0; } ARRAY_APPEND(primary->signed_userids, ARRAY_COUNT(pgp->signed_userids)); ARRAY_APPEND(pgp->signed_userids, userid); if (userid.primary_userid) { primary->primary_userid = ARRAY_COUNT(primary->signed_userids) - 1; } while (pkt_is(pgp, USERID_PKT)) { if (!recog_userid(pgp, &userid)) { printf("recog_primary_key: not signed secondary userid\n"); return 0; } ARRAY_APPEND(primary->signed_userids, ARRAY_COUNT(pgp->signed_userids)); ARRAY_APPEND(pgp->signed_userids, userid); if (userid.primary_userid) { primary->primary_userid = ARRAY_COUNT(primary->signed_userids) - 1; } } while (pkt_is(pgp, USER_ATTRIBUTE_PKT)) { if (!recog_userattr(pgp, &userattr)) { printf("recog_primary_key: not signed user attribute\n"); return 0; } ARRAY_APPEND(primary->signed_userattrs, ARRAY_COUNT(pgp->signed_userattrs)); ARRAY_APPEND(pgp->signed_userattrs, userattr); } while (pkt_is(pgp, PUB_SUBKEY_PKT)) { if (!recog_subkey(pgp, &subkey)) { printf("recog_primary_key: not signed public subkey\n"); return 0; } calc_keyid(&subkey.subkey, "sha1"); ARRAY_APPEND(primary->signed_subkeys, ARRAY_COUNT(pgp->signed_subkeys)); ARRAY_APPEND(pgp->signed_subkeys, subkey); } } while (pgp->pkt < ARRAY_COUNT(pgp->pkts) && pkt_is(pgp, USERID_PKT)); primary->fmtsize = estimate_primarykey_size(primary); return 1; } /* parse all of the packets for a given operation */ static int read_all_packets(pgpv_t *pgp, pgpv_mem_t *mem, const char *op) { pgpv_primarykey_t primary; if (op == NULL) { return 0; } if (strcmp(pgp->op = op, "pubring") == 0) { mem->allowed = PUBRING_ALLOWED; /* pubrings have thousands of small packets */ ARRAY_EXPAND_SIZED(pgp->pkts, 0, 5000); } else if (strcmp(op, "signature") == 0) { mem->allowed = SIGNATURE_ALLOWED; } else { mem->allowed = ""; } for (mem->cc = 0; mem->cc < mem->size ; ) { if (!read_pkt(pgp, mem)) { return 0; } } if (strcmp(op, "pubring") == 0) { for (pgp->pkt = 0; pgp->pkt < ARRAY_COUNT(pgp->pkts) && recog_primary_key(pgp, &primary) ; ) { calc_keyid(&primary.primary, "sha1"); ARRAY_APPEND(pgp->primaries, primary); } if (pgp->pkt < ARRAY_COUNT(pgp->pkts)) { printf("short pubring recognition???\n"); } } pgp->pkt = ARRAY_COUNT(pgp->pkts); return 1; } /* create a filename, read it, and then parse according to "op" */ static int read_binary_file(pgpv_t *pgp, const char *op, const char *fmt, ...) { va_list args; char buf[1024]; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); if (!read_file(pgp, buf)) { return 0; } return read_all_packets(pgp, &ARRAY_LAST(pgp->areas), op); } /* get a bignum from the buffer gap */ static int getbignum(pgpv_bignum_t *bignum, bufgap_t *bg, char *buf, const char *header) { uint32_t len; USE_ARG(header); (void) bufgap_getbin(bg, &len, sizeof(len)); len = pgp_ntoh32(len); (void) bufgap_seek(bg, sizeof(len), BGFromHere, BGByte); (void) bufgap_getbin(bg, buf, len); bignum->bn = PGPV_BN_bin2bn((const uint8_t *)buf, (int)len, NULL); bignum->bits = PGPV_BN_num_bits(bignum->bn); (void) bufgap_seek(bg, len, BGFromHere, BGByte); return 1; } /* structure for searching for constant strings */ typedef struct str_t { const char *s; /* string */ size_t len; /* its length */ int type; /* return type */ } str_t; static str_t pkatypes[] = { { "ssh-rsa", 7, PUBKEY_RSA_SIGN }, { "ssh-dss", 7, PUBKEY_DSA }, { "ssh-dsa", 7, PUBKEY_DSA }, { NULL, 0, 0 } }; /* look for a string in the given array */ static int findstr(str_t *array, const char *name) { str_t *sp; for (sp = array ; sp->s ; sp++) { if (strncmp(name, sp->s, sp->len) == 0) { return sp->type; } } return -1; } /* read public key from the ssh pubkey file */ static __printflike(3, 4) int read_ssh_file(pgpv_t *pgp, pgpv_primarykey_t *primary, const char *fmt, ...) { pgpv_signed_userid_t userid; pgpv_pubkey_t *pubkey; struct stat st; bufgap_t bg; uint32_t len; int64_t off; va_list args; char hostname[256]; char owner[256]; char *space; char *buf; char *bin; char newbuf[2048]; char f[1024]; int ok; int cc; USE_ARG(pgp); memset(primary, 0x0, sizeof(*primary)); (void) memset(&bg, 0x0, sizeof(bg)); va_start(args, fmt); vsnprintf(f, sizeof(f), fmt, args); va_end(args); if (!bufgap_open(&bg, f)) { (void) fprintf(stderr, "pgp_ssh2pubkey: can't open '%s'\n", f); return 0; } (void)stat(f, &st); if ((buf = calloc(1, (size_t)st.st_size)) == NULL) { (void) fprintf(stderr, "can't calloc %zu bytes for '%s'\n", (size_t)st.st_size, f); bufgap_close(&bg); return 0; } if ((bin = calloc(1, (size_t)st.st_size)) == NULL) { (void) fprintf(stderr, "can't calloc %zu bytes for '%s'\n", (size_t)st.st_size, f); (void) free(buf); bufgap_close(&bg); return 0; } /* move past ascii type of key */ while (bufgap_peek(&bg, 0) != ' ') { if (!bufgap_seek(&bg, 1, BGFromHere, BGByte)) { (void) fprintf(stderr, "bad key file '%s'\n", f); (void) free(buf); bufgap_close(&bg); return 0; } } if (!bufgap_seek(&bg, 1, BGFromHere, BGByte)) { (void) fprintf(stderr, "bad key file '%s'\n", f); (void) free(buf); bufgap_close(&bg); return 0; } off = bufgap_tell(&bg, BGFromBOF, BGByte); if (bufgap_size(&bg, BGByte) - off < 10) { (void) fprintf(stderr, "bad key file '%s'\n", f); (void) free(buf); bufgap_close(&bg); return 0; } /* convert from base64 to binary */ cc = bufgap_getbin(&bg, buf, (size_t)bg.bcc); if ((space = strchr(buf, ' ')) != NULL) { cc = (int)(space - buf); } cc = frombase64(bin, buf, (size_t)cc, 0); bufgap_delete(&bg, (uint64_t)bufgap_tell(&bg, BGFromEOF, BGByte)); bufgap_insert(&bg, bin, cc); bufgap_seek(&bg, off, BGFromBOF, BGByte); /* get the type of key */ (void) bufgap_getbin(&bg, &len, sizeof(len)); len = pgp_ntoh32(len); if (len >= st.st_size) { (void) fprintf(stderr, "bad public key file '%s'\n", f); return 0; } (void) bufgap_seek(&bg, sizeof(len), BGFromHere, BGByte); (void) bufgap_getbin(&bg, buf, len); (void) bufgap_seek(&bg, len, BGFromHere, BGByte); pubkey = &primary->primary; pubkey->hashalg = digest_get_alg("sha256"); /* gets fixed up later */ pubkey->version = 4; pubkey->birth = 0; /* gets fixed up later */ /* get key type */ ok = 1; switch (pubkey->keyalg = findstr(pkatypes, buf)) { case PUBKEY_RSA_ENCRYPT_OR_SIGN: case PUBKEY_RSA_SIGN: getbignum(&pubkey->bn[RSA_E], &bg, buf, "RSA E"); getbignum(&pubkey->bn[RSA_N], &bg, buf, "RSA N"); break; case PUBKEY_DSA: getbignum(&pubkey->bn[DSA_P], &bg, buf, "DSA P"); getbignum(&pubkey->bn[DSA_Q], &bg, buf, "DSA Q"); getbignum(&pubkey->bn[DSA_G], &bg, buf, "DSA G"); getbignum(&pubkey->bn[DSA_Y], &bg, buf, "DSA Y"); break; default: (void) fprintf(stderr, "Unrecognised pubkey type %d for '%s'\n", pubkey->keyalg, f); ok = 0; break; } /* check for stragglers */ if (ok && bufgap_tell(&bg, BGFromEOF, BGByte) > 0) { printf("%"PRIi64" bytes left\n", bufgap_tell(&bg, BGFromEOF, BGByte)); printf("[%s]\n", bufgap_getstr(&bg)); ok = 0; } if (ok) { memset(&userid, 0x0, sizeof(userid)); (void) gethostname(hostname, sizeof(hostname)); if (strlen(space + 1) - 1 == 0) { (void) snprintf(owner, sizeof(owner), "", 240, hostname); } else { (void) snprintf(owner, sizeof(owner), "<%.*s>", (int)strlen(space + 1) - 1, space + 1); } calc_keyid(pubkey, "sha1"); cc = snprintf(newbuf, sizeof(newbuf), "%s (%s) %s", hostname, f, owner); userid.userid.size = cc; userid.userid.allocated = 1; if ((userid.userid.data = calloc(1, cc + 1)) == NULL) { ok = 0; } else { memcpy(userid.userid.data, newbuf, cc); ARRAY_APPEND(primary->signed_userids, ARRAY_COUNT(pgp->signed_userids)); ARRAY_APPEND(pgp->signed_userids, userid); primary->fmtsize = estimate_primarykey_size(primary) + 1024; } } (void) free(bin); (void) free(buf); bufgap_close(&bg); return ok; } /* parse memory according to "op" */ static int read_binary_memory(pgpv_t *pgp, const char *op, const void *memory, size_t size) { pgpv_mem_t *mem; ARRAY_EXPAND(pgp->areas); ARRAY_COUNT(pgp->areas) += 1; mem = &ARRAY_LAST(pgp->areas); memset(mem, 0x0, sizeof(*mem)); mem->size = size; mem->mem = __UNCONST(memory); mem->dealloc = 0; return read_all_packets(pgp, mem, op); } /* fixup the detached signature packets */ static int fixup_detached(pgpv_cursor_t *cursor, const char *f) { pgpv_onepass_t *onepass; const char *dot; pgpv_pkt_t sigpkt; pgpv_pkt_t litdata; pgpv_mem_t *mem; size_t el; char original[MAXPATHLEN]; /* cons up litdata pkt */ if ((dot = strrchr(f, '.')) == NULL || strcasecmp(dot, ".sig") != 0) { printf("weird filename '%s'\n", f); return 0; } /* hold sigpkt in a temp var while we insert onepass and litdata */ el = ARRAY_COUNT(cursor->pgp->pkts) - 1; sigpkt = ARRAY_ELEMENT(cursor->pgp->pkts, el); ARRAY_DELETE(cursor->pgp->pkts, el); ARRAY_EXPAND(cursor->pgp->pkts); /* get onepass packet, append to packets */ read_binary_memory(cursor->pgp, "signature", cons_onepass, 15); onepass = &ARRAY_ELEMENT(cursor->pgp->pkts, el).u.onepass; /* read the original file into litdata */ snprintf(original, sizeof(original), "%.*s", (int)(dot - f), f); if (!read_file(cursor->pgp, original)) { printf("can't read file '%s'\n", original); return 0; } memset(&litdata, 0x0, sizeof(litdata)); mem = &ARRAY_LAST(cursor->pgp->areas); litdata.tag = LITDATA_PKT; litdata.s.data = mem->mem; litdata.u.litdata.format = LITDATA_BINARY; litdata.u.litdata.offset = 0; litdata.u.litdata.filename.data = pgpv_strdup(original); litdata.u.litdata.filename.allocated = 1; litdata.u.litdata.mem = ARRAY_COUNT(cursor->pgp->areas) - 1; litdata.u.litdata.len = litdata.s.size = mem->size; ARRAY_APPEND(cursor->pgp->pkts, litdata); ARRAY_APPEND(cursor->pgp->pkts, sigpkt); memcpy(onepass->keyid, sigpkt.u.sigpkt.sig.signer, sizeof(onepass->keyid)); onepass->hashalg = sigpkt.u.sigpkt.sig.hashalg; onepass->keyalg = sigpkt.u.sigpkt.sig.keyalg; return 1; } /* match the calculated signature against the one in the signature packet */ static int match_sig(pgpv_cursor_t *cursor, pgpv_signature_t *signature, pgpv_pubkey_t *pubkey, uint8_t *data, size_t size) { unsigned calclen; uint8_t calculated[64]; int match; calclen = pgpv_digest_memory(calculated, sizeof(calculated), data, size, get_ref(&signature->hashstart), signature->hashlen, (signature->type == SIGTYPE_TEXT) ? 't' : 'b'); if (ALG_IS_RSA(signature->keyalg)) { match = rsa_verify(calculated, calclen, signature->hashalg, signature->bn, pubkey); } else if (ALG_IS_DSA(signature->keyalg)) { match = verify_dsa_sig(calculated, calclen, signature->bn, pubkey); } else { snprintf(cursor->why, sizeof(cursor->why), "Signature type %u not recognised", signature->keyalg); return 0; } if (!match && signature->type == SIGTYPE_TEXT) { /* second try for cleartext data, ignoring trailing whitespace */ calclen = pgpv_digest_memory(calculated, sizeof(calculated), data, size, get_ref(&signature->hashstart), signature->hashlen, 'w'); if (ALG_IS_RSA(signature->keyalg)) { match = rsa_verify(calculated, calclen, signature->hashalg, signature->bn, pubkey); } else if (ALG_IS_DSA(signature->keyalg)) { match = verify_dsa_sig(calculated, calclen, signature->bn, pubkey); } } if (!match) { snprintf(cursor->why, sizeof(cursor->why), "Signature on data did not match"); return 0; } if (valid_dates(signature, pubkey, cursor->why, sizeof(cursor->why)) > 0) { return 0; } if (key_expired(pubkey, cursor->why, sizeof(cursor->why))) { return 0; } if (signature->revoked) { snprintf(cursor->why, sizeof(cursor->why), "Signature was revoked"); return 0; } return 1; } /* fixup key id, with birth, keyalg and hashalg value from signature */ static int fixup_ssh_keyid(pgpv_t *pgp, pgpv_signature_t *signature, const char *hashtype) { pgpv_pubkey_t *pubkey; unsigned i; for (i = 0 ; i < ARRAY_COUNT(pgp->primaries) ; i++) { pubkey = &ARRAY_ELEMENT(pgp->primaries, i).primary; pubkey->keyalg = signature->keyalg; calc_keyid(pubkey, hashtype); } return 1; } /* find key id */ static int find_keyid(pgpv_t *pgp, const char *strkeyid, uint8_t *keyid, unsigned *sub) { pgpv_signed_subkey_t *subkey; pgpv_primarykey_t *prim; unsigned i; unsigned j; uint64_t n; uint8_t binkeyid[PGPV_KEYID_LEN]; size_t off; size_t cmp; if (strkeyid == NULL && keyid == NULL) { return 0; } if (strkeyid) { str_to_keyid(strkeyid, binkeyid); cmp = strlen(strkeyid) / 2; } else { memcpy(binkeyid, keyid, sizeof(binkeyid)); cmp = PGPV_KEYID_LEN; } *sub = 0; off = PGPV_KEYID_LEN - cmp; for (i = 0 ; i < ARRAY_COUNT(pgp->primaries) ; i++) { prim = &ARRAY_ELEMENT(pgp->primaries, i); if (memcmp(&prim->primary.keyid[off], &binkeyid[off], cmp) == 0) { return i; } for (j = 0 ; j < ARRAY_COUNT(prim->signed_subkeys) ; j++) { n = ARRAY_ELEMENT(prim->signed_subkeys, j); subkey = &ARRAY_ELEMENT(pgp->signed_subkeys, n); if (memcmp(&subkey->subkey.keyid[off], &binkeyid[off], cmp) == 0) { *sub = j + 1; return i; } } } return -1; } /* match the signature with the id indexed by 'primary' */ static int match_sig_id(pgpv_cursor_t *cursor, pgpv_t *pgp, pgpv_signature_t *signature, pgpv_litdata_t *litdata, unsigned primary, unsigned sub) { pgpv_primarykey_t *prim; pgpv_pubkey_t *pubkey; uint64_t n; uint8_t *data; size_t insize; cursor->sigtime = signature->birth; /* calc hash on data packet */ data = get_literal_data(cursor, litdata, &insize); if (sub == 0) { pubkey = &ARRAY_ELEMENT(cursor->pgp->primaries, primary).primary; return match_sig(cursor, signature, pubkey, data, insize); } prim = &ARRAY_ELEMENT(cursor->pgp->primaries, primary); n = ARRAY_ELEMENT(prim->signed_subkeys, sub - 1); pubkey = &ARRAY_ELEMENT(pgp->signed_subkeys, n).subkey; return match_sig(cursor, signature, pubkey, data, insize); } /* return the packet type */ static const char * get_packet_type(uint8_t tag) { switch(tag) { case SIGNATURE_PKT: return "signature packet"; case ONEPASS_SIGNATURE_PKT: return "onepass signature packet"; case PUBKEY_PKT: return "pubkey packet"; case COMPRESSED_DATA_PKT: return "compressed data packet"; case MARKER_PKT: return "marker packet"; case LITDATA_PKT: return "litdata packet"; case TRUST_PKT: return "trust packet"; case USERID_PKT: return "userid packet"; case PUB_SUBKEY_PKT: return "public subkey packet"; case USER_ATTRIBUTE_PKT: return "user attribute packet"; default: return "[UNKNOWN]"; } } /* check return value from getenv */ static const char * nonnull_getenv(const char *key) { char *value; return ((value = getenv(key)) == NULL) ? "" : value; } /* free an array of bignums */ static void free_bn_array(pgpv_bignum_t *v, unsigned n) { unsigned i; for (i = 0 ; i < n ; i++) { PGPV_BN_clear_free(v[i].bn); v[i].bn = NULL; } } /************************************************************************/ /* start of exported functions */ /************************************************************************/ /* close all stuff */ int pgpv_close(pgpv_t *pgp) { pgpv_primarykey_t *primary; pgpv_pkt_t *pkt; uint64_t n; unsigned i; unsigned j; if (pgp == NULL) { return 0; } for (i = 0 ; i < ARRAY_COUNT(pgp->areas) ; i++) { if (ARRAY_ELEMENT(pgp->areas, i).size > 0) { closemem(&ARRAY_ELEMENT(pgp->areas, i)); } } ARRAY_FREE(pgp->areas); for (i = 0 ; i < ARRAY_COUNT(pgp->pkts) ; i++) { pkt = &ARRAY_ELEMENT(pgp->pkts, i); switch(pkt->tag) { case SIGNATURE_PKT: ARRAY_FREE(pkt->u.sigpkt.subpackets); break; case LITDATA_PKT: if (pkt->u.litdata.filename.allocated) { free(pkt->u.litdata.filename.data); } break; case PUBKEY_PKT: free_bn_array(pkt->u.pubkey.bn, PGPV_MAX_PUBKEY_BN); break; case USERID_PKT: if (pkt->u.userid.allocated) { free(pkt->u.userid.data); } break; case USER_ATTRIBUTE_PKT: ARRAY_FREE(pkt->u.userattr.subattrs); break; } } ARRAY_FREE(pgp->pkts); for (i = 0 ; i < ARRAY_COUNT(pgp->primaries) ; i++) { primary = &ARRAY_ELEMENT(pgp->primaries, i); free_bn_array(primary->primary.bn, PGPV_MAX_PUBKEY_BN); ARRAY_FREE(primary->signatures); for (j = 0 ; j < ARRAY_COUNT(primary->signed_userids) ; j++) { n = ARRAY_ELEMENT(primary->signed_userids, j); ARRAY_FREE(ARRAY_ELEMENT(pgp->signed_userids, n).signatures); } ARRAY_FREE(primary->signed_userids); ARRAY_FREE(primary->signed_userattrs); ARRAY_FREE(primary->signed_subkeys); } for (i = 0 ; i < ARRAY_COUNT(pgp->signatures) ; i++) { free_bn_array(ARRAY_ELEMENT(pgp->signatures, i).bn, PGPV_MAX_SIG_BN); } for (i = 0 ; i < ARRAY_COUNT(pgp->signed_subkeys) ; i++) { free_bn_array(ARRAY_ELEMENT(pgp->signed_subkeys, i).subkey.bn, PGPV_MAX_SIG_BN); } ARRAY_FREE(pgp->primaries); ARRAY_FREE(pgp->datastarts); ARRAY_FREE(pgp->signatures); ARRAY_FREE(pgp->signed_userids); ARRAY_FREE(pgp->signed_userattrs); ARRAY_FREE(pgp->signed_subkeys); ARRAY_FREE(pgp->subpkts); return 1; } /* free resources attached to cursor */ int pgpv_cursor_close(pgpv_cursor_t *cursor) { if (cursor) { ARRAY_FREE(cursor->datacookies); ARRAY_FREE(cursor->found); } return 0; } /* return the formatted entry for the primary key desired */ size_t pgpv_get_entry(pgpv_t *pgp, unsigned ent, char **s, const char *modifiers) { unsigned subkey; unsigned prim; obuf_t obuf; prim = ((ent >> 8) & 0xffffff); subkey = (ent & 0xff); if (s == NULL || pgp == NULL || prim >= ARRAY_COUNT(pgp->primaries)) { return 0; } *s = NULL; if (modifiers == NULL || (strcasecmp(modifiers, "trust") != 0 && strcasecmp(modifiers, "subkeys") != 0)) { modifiers = "no-subkeys"; } memset(&obuf, 0x0, sizeof(obuf)); if (!fmt_primary(&obuf, pgp, &ARRAY_ELEMENT(pgp->primaries, prim), subkey, modifiers)) { return 0; } *s = (char *)obuf.v; return obuf.c; } /* make a new pgpv struct */ pgpv_t * pgpv_new(void) { return calloc(1, sizeof(pgpv_t)); } /* make a new pgpv_cursor struct */ pgpv_cursor_t * pgpv_new_cursor(void) { return calloc(1, sizeof(pgpv_cursor_t)); } /* get an element from the found array */ int pgpv_get_cursor_element(pgpv_cursor_t *cursor, size_t element) { if (cursor && element < ARRAY_COUNT(cursor->found)) { return (int)ARRAY_ELEMENT(cursor->found, element); } return -1; } /* verify the signed packets we have */ size_t pgpv_verify(pgpv_cursor_t *cursor, pgpv_t *pgp, const void *p, ssize_t size) { pgpv_signature_t *signature; pgpv_onepass_t *onepass; pgpv_litdata_t *litdata; unsigned sub; size_t pkt; obuf_t obuf; int j; if (cursor == NULL || pgp == NULL || p == NULL) { return 0; } if (!setup_data(cursor, pgp, p, size)) { snprintf(cursor->why, sizeof(cursor->why), "No input data"); return 0; } if (ARRAY_COUNT(cursor->pgp->pkts) == ARRAY_LAST(cursor->pgp->datastarts) + 1) { /* got detached signature here */ if (!fixup_detached(cursor, p)) { snprintf(cursor->why, sizeof(cursor->why), "Can't read signed file '%s'", (const char *)p); return 0; } } if ((pkt = find_onepass(cursor, ARRAY_LAST(cursor->pgp->datastarts))) == 0) { snprintf(cursor->why, sizeof(cursor->why), "No signature found"); return 0; } pkt -= 1; onepass = &ARRAY_ELEMENT(cursor->pgp->pkts, pkt).u.onepass; litdata = &ARRAY_ELEMENT(cursor->pgp->pkts, pkt + 1).u.litdata; signature = &ARRAY_ELEMENT(cursor->pgp->pkts, pkt + 2).u.sigpkt.sig; /* sanity check values in signature and onepass agree */ if (signature->birth == 0) { if (!fmt_time(&obuf, "Signature creation time [", signature->birth, "] out of range", 0)) { } snprintf(cursor->why, sizeof(cursor->why), "%.*s", (int)obuf.c, (char *)obuf.v); return 0; } memset(&obuf, 0x0, sizeof(obuf)); if (memcmp(onepass->keyid, signature->signer, PGPV_KEYID_LEN) != 0) { if (!fmt_binary(&obuf, onepass->keyid, (unsigned)sizeof(onepass->keyid))) { snprintf(cursor->why, sizeof(cursor->why), "Memory allocation failure"); return 0; } snprintf(cursor->why, sizeof(cursor->why), "Signature key id %.*s does not match onepass keyid", (int)obuf.c, (char *)obuf.v); return 0; } if (onepass->hashalg != signature->hashalg) { snprintf(cursor->why, sizeof(cursor->why), "Signature hashalg %u does not match onepass hashalg %u", signature->hashalg, onepass->hashalg); return 0; } if (onepass->keyalg != signature->keyalg) { snprintf(cursor->why, sizeof(cursor->why), "Signature keyalg %u does not match onepass keyalg %u", signature->keyalg, onepass->keyalg); return 0; } if (cursor->pgp->ssh) { fixup_ssh_keyid(cursor->pgp, signature, "sha1"); } sub = 0; if ((j = find_keyid(cursor->pgp, NULL, onepass->keyid, &sub)) < 0) { if (!fmt_binary(&obuf, onepass->keyid, (unsigned)sizeof(onepass->keyid))) { snprintf(cursor->why, sizeof(cursor->why), "Memory allocation failure"); return 0; } snprintf(cursor->why, sizeof(cursor->why), "Signature key id %.*s not found ", (int)obuf.c, (char *)obuf.v); return 0; } if (!match_sig_id(cursor, pgp, signature, litdata, (unsigned)j, sub)) { return 0; } ARRAY_APPEND(cursor->datacookies, pkt); j = ((j & 0xffffff) << 8) | (sub & 0xff); ARRAY_APPEND(cursor->found, j); return pkt + 1; } /* set up the pubkey keyring */ int pgpv_read_pubring(pgpv_t *pgp, const void *keyring, ssize_t size) { if (pgp == NULL) { return 0; } if (keyring) { return (size > 0) ? read_binary_memory(pgp, "pubring", keyring, (size_t)size) : read_binary_file(pgp, "pubring", "%s", (const char *)keyring); } return read_binary_file(pgp, "pubring", "%s/%s", nonnull_getenv("HOME"), ".gnupg/pubring.gpg"); } /* set up the pubkey keyring from ssh pub key */ int pgpv_read_ssh_pubkeys(pgpv_t *pgp, const void *keyring, ssize_t size) { pgpv_primarykey_t primary; USE_ARG(size); if (pgp == NULL) { return 0; } if (keyring) { if (!read_ssh_file(pgp, &primary, "%s", (const char *)keyring)) { return 0; } } else if (!read_ssh_file(pgp, &primary, "%s/%s", nonnull_getenv("HOME"), ".ssh/id_rsa.pub")) { return 0; } ARRAY_APPEND(pgp->primaries, primary); pgp->ssh = 1; return 1; } /* get verified data as a string, return its size */ size_t pgpv_get_verified(pgpv_cursor_t *cursor, size_t cookie, char **ret) { pgpv_litdata_t *litdata; uint8_t *data; size_t size; size_t pkt; if (ret == NULL || cursor == NULL || cookie == 0) { return 0; } *ret = NULL; if ((pkt = find_onepass(cursor, cookie - 1)) == 0) { return 0; } litdata = &ARRAY_ELEMENT(cursor->pgp->pkts, pkt).u.litdata; data = get_literal_data(cursor, litdata, &size); if ((*ret = calloc(1, size)) == NULL) { return 0; } memcpy(*ret, data, size); return size; } #define KB(x) ((x) * 1024) /* dump all packets */ size_t pgpv_dump(pgpv_t *pgp, char **data) { ssize_t dumpc; size_t alloc; size_t pkt; size_t cc; size_t n; char buf[800]; char *newdata; cc = alloc = 0; *data = NULL; for (pkt = 0 ; pkt < ARRAY_COUNT(pgp->pkts) ; pkt++) { if (cc + KB(64) >= alloc) { if ((newdata = realloc(*data, alloc + KB(64))) == NULL) { return cc; } alloc += KB(64); *data = newdata; } memset(buf, 0x0, sizeof(buf)); dumpc = netpgp_hexdump(ARRAY_ELEMENT(pgp->pkts, pkt).s.data, MIN((sizeof(buf) / 80) * 16, ARRAY_ELEMENT(pgp->pkts, pkt).s.size), buf, sizeof(buf)); n = snprintf(&(*data)[cc], alloc - cc, "[%zu] off %zu, len %zu, tag %u, %s\n%.*s", pkt, ARRAY_ELEMENT(pgp->pkts, pkt).offset, ARRAY_ELEMENT(pgp->pkts, pkt).s.size, ARRAY_ELEMENT(pgp->pkts, pkt).tag, get_packet_type(ARRAY_ELEMENT(pgp->pkts, pkt).tag), (int)dumpc, buf); cc += n; } return cc; } /* return cursor field as a number */ int64_t pgpv_get_cursor_num(pgpv_cursor_t *cursor, const char *field) { if (cursor && field) { if (strcmp(field, "sigtime") == 0) { return cursor->sigtime; } } return 0; } /* return cursor field as a string */ char * pgpv_get_cursor_str(pgpv_cursor_t *cursor, const char *field) { if (cursor && field) { if (strcmp(field, "why") == 0) { return cursor->why; } } return 0; }