From dbfe3c19864dcd20da6900b23efcb35048d6c765 Mon Sep 17 00:00:00 2001 From: Dave Korn Date: Sun, 22 Jun 2008 02:37:17 +0000 Subject: [PATCH] Merged across diffs between setup_crypto_branch_branchpoint and setup_crypto_branch_mergepoint2 from setup_crypto_branch. * crypto.cc, crypto.h, gpg-packet.cc, gpg-packet.h, KeysSetting.cc, KeysSetting.h, gpg-error-config-fake, cygwin.pub, cyg-pubkey.h, gpg-key-to-s-expr.sh: New files. * ini.cc, ini.h, resource.h, res.rc, netio.cc, Makefile.am, configure.ac, libgetopt++/src/OptionSet.cc: Modified files. --- ChangeLog | 105 ++++++ KeysSetting.cc | 166 +++++++++ KeysSetting.h | 64 ++++ Makefile.am | 15 +- configure.ac | 5 +- configure.in | 5 +- crypto.cc | 698 +++++++++++++++++++++++++++++++++++ crypto.h | 319 ++++++++++++++++ cyg-pubkey.h | 14 + cygwin.pub | Bin 0 -> 964 bytes gpg-error-config-fake | 57 +++ gpg-key-to-s-expr.sh | 127 +++++++ gpg-packet.cc | 319 ++++++++++++++++ gpg-packet.h | 274 ++++++++++++++ ini.cc | 54 ++- ini.h | 1 + libgetopt++/ChangeLog | 6 + libgetopt++/src/OptionSet.cc | 5 +- netio.cc | 2 +- res.rc | 2 + resource.h | 2 + 21 files changed, 2226 insertions(+), 14 deletions(-) create mode 100755 KeysSetting.cc create mode 100755 KeysSetting.h create mode 100755 crypto.cc create mode 100755 crypto.h create mode 100755 cyg-pubkey.h create mode 100755 cygwin.pub create mode 100755 gpg-error-config-fake create mode 100755 gpg-key-to-s-expr.sh create mode 100755 gpg-packet.cc create mode 100755 gpg-packet.h diff --git a/ChangeLog b/ChangeLog index 6271dd3b..d406f498 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,108 @@ +2008-06-22 Dave Korn + + Merged across diffs between setup_crypto_branch_branchpoint and +setup_crypto_branch_mergepoint2 from setup_crypto_branch. + + * crypto.cc, crypto.h, gpg-packet.cc, gpg-packet.h, KeysSetting.cc, + KeysSetting.h, gpg-error-config-fake, cygwin.pub, cyg-pubkey.h, + gpg-key-to-s-expr.sh: New files. + * ini.cc, ini.h, resource.h, res.rc, netio.cc, Makefile.am, + configure.ac, libgetopt++/src/OptionSet.cc: Modified files. + + 2008-06-21 Dave Korn + + * ini.cc (do_remote_ini): Revert misbegotten removal of + not-always-superfluous-after-all added slash in URLs. + + 2008-06-18 Dave Korn + + * gpg-packet.h (GPG_KEY_SEXPR_BUF_SIZE): Move from here ... + * crypto.h (GPG_KEY_SEXPR_BUF_SIZE): ... to here. + (GPG_KEY_MAX_COEFF_SIZE): Add related definition. + + 2008-06-18 Dave Korn + + * gpg-packet.cc (walk_packets_1): Check packet length field is + valid sane and possible to avoid malicious manipulation. + * ChangeLog: Corrected date on previous commit. + + 2008-06-18 Dave Korn + + * crypto.cc (add_key_from_sexpr): Use real buffer size, not debug + constant size. + + 2008-06-16 Dave Korn + + * crypto.cc (verify_ini_file_sig): Actually set init-once flag! + + 2008-06-16 Dave Korn + + * crypto.cc (KeepUntrustedKeysOption): Fix helpstring punctuation. + + 2008-06-16 Dave Korn + + * crypto.cc (verify_ini_file_sig): Fix two minor logic errors. + + 2008-06-16 Dave Korn + + * ini.h (current_ini_sig_name): Declare extern. + * ini.cc (current_ini_sig_name): New variable. + (NoVerifyOption): New boolean option. + (do_remote_ini): If sig verification not disabled, attempt to + download the matching sig file for any setup.bz2 or setup.ini + and use it to verify or discard the download. + + 2008-06-16 Dave Korn + + * resource.h (IDS_SIG_INVALID, IDS_CRYPTO_ERROR): Reserve IDs for + two new message strings. + * res.rc (IDS_SIG_INVALID, IDS_CRYPTO_ERROR): Define the text. + + 2008-06-16 Dave Korn + + * Makefile.am (setup_SOURCES): Add new files crypto.cc, crypto.h, + cyg-pubkey.h, gpg-packet.cc, gpg-packet.h. KeysSetting.cc and + KeysSetting.h to the build. + * crypto.cc, crypto.h, gpg-packet.cc, gpg-packet.h, KeysSetting.cc, + KeysSetting.h: New files. Adds gpg sig verification support. + + 2008-06-16 Dave Korn + + * Makefile.am (DIST_SUBDIRS, SUBDIRS): Add libgpg-error and + libgcrypt subdirs. + (AM_CPPFLAGS): Add -I paths to generated headers in both and to + shipped headers in libgcrypt. + (setup_LDADD): Add libgpg-error.a and libgcrypt.a to final link. + + * configure.in (ac_cv_path_GPG_ERROR_CONFIG): Preload into cache + to fake out libgcrypt configure process. + (AC_CONFIG_SUBDIRS): Add libgpg-error/ and libgcrypt/ + + * gpg-error-config-fake: New shell script to redirect libgcrypt + sub-configure into looking for newly-built libgpg-error. + + 2008-06-16 Dave Korn + + * setup/libgpg-error/: Fresh import of upstream sources of + libgpg-error-1.6.tar.bz2 from http://www.gnupg.org/ + * setup/libgcrypt/: Fresh import of upstream sources of + libgcrypt-1.4.1.tar.bz2 from http://www.gnupg.org/ + + 2008-06-16 Dave Korn + + * netio.cc (NetIO::set_url): Avoid double-free bug on delete + by setting path to a strdup of url rather than using the same + pointer value twice. + + 2008-06-16 Dave Korn + + * cygwin.pub: Add reference copy of Cygwin setup signing key. + * gpg-key-to-s-expr.sh: New bash script. Outputs textual + representation of public key in s-expr format. + * cyg-pubkey.h: New header generated by the above from Cygwin + setup signing key. Currently needs manual regeneration if key + ever updated. + 2008-04-16 Brian Dessent * mount.cc: Include malloc.h. diff --git a/KeysSetting.cc b/KeysSetting.cc new file mode 100755 index 00000000..0616c69d --- /dev/null +++ b/KeysSetting.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2008, Dave Korn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + * This implements the ExtraKeysSetting class, which persists and reads + * in (and saves) extra public DSA signing keys for the verification process. + * It stores them all in a contiguous memory buffer. Each is one line of + * ASCII text terminated by LF. THERE IS NO NUL-TERMINATION HERE, TAKE CARE! + * The buffer is sized to the exact size of the content including the terminating + * LF of the last entry. There is no zero after it. After reading the file, + * any partial last line is truncated. + * + * Written by Dave Korn + * + */ + +#if 0 +static const char *cvsid = + "\n%%% $Id$\n"; +#endif + +#include +#include "UserSettings.h" +#include "io_stream.h" +#include "KeysSetting.h" + +void +ExtraKeysSetting::load() +{ + static int inited = 0; + if (inited) + return; + io_stream *f = UserSettings::Instance().settingFileForLoad("last-extrakeys"); + if (f) + { + bufsize = f->get_size (); + if (bufsize) + { + keybuffer = new char[bufsize]; + f->read (keybuffer, bufsize); + // Calling count_keys gets the count but also sizes the buffer + // correctly, discarding any trailing non-LF-terminated data. + bufsize = count_keys (); + } + delete f; + } + inited = 1; +} + +void +ExtraKeysSetting::save() +{ + io_stream *f = UserSettings::Instance().settingFileForSave("last-extrakeys"); + if (f) + { + if (bufsize) + f->write(keybuffer, bufsize); + delete f; + } +} + +void +ExtraKeysSetting::flush (void) +{ + if (bufsize) + delete [] keybuffer; + keybuffer = 0; + bufsize = 0; + numkeys = 0; +} + +void +ExtraKeysSetting::realloc (size_t newbufsize) +{ + char *newbuf = new char[newbufsize]; + if (bufsize) + { + memcpy (newbuf, keybuffer, newbufsize < bufsize ? newbufsize : bufsize); + delete [] keybuffer; + } + keybuffer = newbuf; + bufsize = newbufsize; +} + +size_t +ExtraKeysSetting::count_keys (void) +{ + size_t offs = 0, size = 0; + numkeys = 0; + while (offs < bufsize) + if (keybuffer[offs++] == 0x0a) + { + size = offs; + ++numkeys; + } + return size; +} + +size_t +ExtraKeysSetting::num_keys (void) +{ + return numkeys; +} + +const char * +ExtraKeysSetting::get_key (size_t num, size_t *size) +{ + if (!numkeys || (num >= numkeys)) + return NULL; + + const char *ptr = keybuffer; + while (num--) + while (*ptr++ != 0x0a); + + // Count its size if requested. + if (size) + { + const char *ptr2 = ptr; + while (num--) + if (*ptr2 != 0x0a) + ++ptr2; + else + break; + *size = ptr2 - ptr; + } + return ptr; +} + +void +ExtraKeysSetting::add_unique_key (const char *key) +{ + size_t osize = bufsize; + realloc (bufsize + strlen (key) + 1); + strcpy (keybuffer + osize, key); + keybuffer[bufsize - 1] = 0x0a; + ++numkeys; +} + +void +ExtraKeysSetting::add_key (const char *key) +{ + /* Only add key if we don't already have it. */ + const char *ptr = keybuffer; + size_t remain = bufsize; + size_t keylen = strlen (key); + + while (remain >= keylen) + { + if (memcmp (ptr, key, keylen) == 0) + return; + + while (remain--) + if (*ptr++ == 0x0a) + break; + } + add_unique_key (key); +} + + diff --git a/KeysSetting.h b/KeysSetting.h new file mode 100755 index 00000000..7b595715 --- /dev/null +++ b/KeysSetting.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008, Dave Korn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + * This is the header for the ExtraKeysSetting class, which persists and reads + * in (and saves) extra public DSA signing keys for the verification process. + * It stores them all in a contiguous memory buffer. Each is one line of + * ASCII text terminated by LF. THERE IS NO NUL-TERMINATION HERE, TAKE CARE! + * The buffer is sized to the exact size of the content including the terminating + * LF of the last entry. There is no zero after it. After reading the file, + * any partial last line is truncated. + * + * Written by Dave Korn + * + */ + +#ifndef SETUP_KEYSSETTING_H +#define SETUP_KEYSSETTING_H + +#include +#include "UserSetting.h" + +class ExtraKeysSetting : public UserSetting +{ + private: + char *keybuffer; + size_t bufsize; + size_t numkeys; + public: + // Loads keys from last-extrakeys + virtual void load(); + // Saves them back again. + virtual void save(); + + private: + // Extend (or shrink) allocated buffer. Leaves it in a + // potentially invalid state until count_keys is called + // (or the bufsize is made valid by filling it up and + // the numkeys count adjusted likewise). + void realloc (size_t newbufsize); + // Count keys and size buffer after loading. + size_t count_keys (void); + // Add a key without testing for existence + void add_unique_key (const char *key); + + public: + // Number of keys in buffer. + size_t num_keys (void); + // Get pointer to LF-terminated Nth. key string and len. + const char *get_key (size_t num, size_t *size); + // Add a key to the buffer (if not already present). + void add_key (const char *key); + // Empty the buffer. + void flush (void); +}; + +#endif /* SETUP_CONNECTIONSETTING_H */ diff --git a/Makefile.am b/Makefile.am index 5597a4bf..58186af2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,8 +18,8 @@ # # Makefile for Cygwin installer -DIST_SUBDIRS = libgetopt++ tests -SUBDIRS = libgetopt++ tests +DIST_SUBDIRS = libgetopt++ tests libgpg-error libgcrypt +SUBDIRS = libgetopt++ tests libgpg-error libgcrypt ## DISTCLEANFILES = include/stamp-h include/stamp-h[0-9]* @@ -35,7 +35,7 @@ AM_CFLAGS = $(AM_CXXFLAGS) -Wmissing-declarations -Winline \ AM_YFLAGS = -d AM_LFLAGS = -8 WINDRES = @WINDRES@ -AM_CPPFLAGS = -I$(srcdir)/libgetopt++/include +AM_CPPFLAGS = -I$(srcdir)/libgetopt++/include -I$(top_builddir)/libgpg-error/src -I$(top_builddir)/libgcrypt/src -I$(srcdir)/libgcrypt/src noinst_PROGRAMS = setup @INILINT@ @@ -116,6 +116,8 @@ libinilex_a_CXXFLAGS = $(BASECXXFLAGS) setup_LDADD = \ libinilex.a libgetopt++/libgetopt++.la res.o \ + libgcrypt/src/.libs/libgcrypt.a \ + libgpg-error/src/.libs/libgpg-error.a \ -lcomctl32 -lole32 -lwsock32 -lnetapi32 -luuid -lbz2 -lz setup_LDFLAGS = -mwindows -Wl,-static setup_SOURCES = \ @@ -139,6 +141,9 @@ setup_SOURCES = \ ConnectionSetting.h \ ControlAdjuster.cc \ ControlAdjuster.h \ + crypto.cc \ + crypto.h \ + cyg-pubkey.h \ cygpackage.cc \ cygpackage.h \ desktop.cc \ @@ -161,6 +166,8 @@ setup_SOURCES = \ Generic.h \ geturl.cc \ geturl.h \ + gpg-packet.cc \ + gpg-packet.h \ ini.cc \ ini.h \ IniDBBuilder.h \ @@ -181,6 +188,8 @@ setup_SOURCES = \ io_stream_memory.cc \ io_stream_memory.h \ IOStreamProvider.h \ + KeysSetting.cc \ + KeysSetting.h \ localdir.cc \ localdir.h \ LogFile.cc \ diff --git a/configure.ac b/configure.ac index 62735258..784e40fc 100644 --- a/configure.ac +++ b/configure.ac @@ -70,7 +70,10 @@ AC_CHECK_HEADERS(alloca.h \ string \ string.h ) -AC_CONFIG_SUBDIRS(libgetopt++) +dnl override configure of sub-libraries +ac_cv_path_GPG_ERROR_CONFIG="${ac_abs_confdir}/gpg-error-config-fake --dir=$ac_pwd" + +AC_CONFIG_SUBDIRS(libgetopt++ libgpg-error libgcrypt) dnl add portability sources to inilint case "$host_os" in diff --git a/configure.in b/configure.in index 62735258..784e40fc 100644 --- a/configure.in +++ b/configure.in @@ -70,7 +70,10 @@ AC_CHECK_HEADERS(alloca.h \ string \ string.h ) -AC_CONFIG_SUBDIRS(libgetopt++) +dnl override configure of sub-libraries +ac_cv_path_GPG_ERROR_CONFIG="${ac_abs_confdir}/gpg-error-config-fake --dir=$ac_pwd" + +AC_CONFIG_SUBDIRS(libgetopt++ libgpg-error libgcrypt) dnl add portability sources to inilint case "$host_os" in diff --git a/crypto.cc b/crypto.cc new file mode 100755 index 00000000..e09c8c89 --- /dev/null +++ b/crypto.cc @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2008, Dave Korn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + * Written by Dave Korn + * + */ + +#if 0 +static const char *cvsid = + "\n%%% $Id$\n"; +#endif + +#include +#include +#include +#include +#include "io_stream.h" +#include "crypto.h" +#include "compress.h" +#include "gcrypt.h" +#include "msg.h" +#include "resource.h" +#include "getopt++/StringOption.h" +#include "getopt++/BoolOption.h" +#include "KeysSetting.h" +#include "gpg-packet.h" +#include "geturl.h" + +#define CRYPTODEBUGGING (0) + +#if CRYPTODEBUGGING +#define ERRKIND __asm__ __volatile__ (".byte 0xcc"); note +#define MESSAGE msg +#else /* !CRYPTODEBUGGING */ +#define ERRKIND note +#define MESSAGE while (0) msg +#endif /* CRYPTODEBUGGING */ + +/* Command-line options for specifying and controlling extra keys. */ +static StringOption ExtraKeyOption ("", 'K', "pubkey", + "Path to extra public key file (gpg format)", true); + +static StringOption SexprExtraKeyOption ("", 'S', "sexpr-pubkey", + "Extra public key in s-expr format", true); + +static BoolOption UntrustedKeysOption (false, 'u', "untrusted-keys", + "Use untrusted keys from last-extrakeys"); +static BoolOption KeepUntrustedKeysOption (false, 'U', "keep-untrusted-keys", + "Use untrusted keys and retain all"); + +/* Persists the extra keys in /etc/setup/last-extrakeys. */ +static ExtraKeysSetting ExtraKeys; + +/* Embedded public half of Cygwin DSA signing key. */ +static const char *cygwin_pubkey_sexpr = +#include "cyg-pubkey.h" +; + +/* S-expr template for DSA pubkey. */ +static const char *dsa_pubkey_templ = "(public-key (dsa (p %m) (q %m) (g %m) (y %m)))"; + +/* S-expr template for DSA signature. */ +static const char *dsa_sig_templ = "(sig-val (dsa (r %m) (s %m)))"; + +/* S-expr template for data block to be signed. */ +static const char *data_hash_templ = "(data (flags raw) (value %m))"; + +/* User context data for sig packet walk. */ +struct sig_data +{ + /* MPI values of sig components. */ + gcry_mpi_t dsa_mpi_r, dsa_mpi_s; + + /* Hash context. */ + gcry_md_hd_t md; + + /* Main data. */ + io_stream *sign_data; + + /* Auxiliary data. */ + int sig_type; + int pk_alg; + int hash_alg; + + /* Converted algo code. */ + int algo; + + /* Final status. */ + bool complete; +}; + +/* User context data for key packet walk. */ +struct key_data +{ + std::vector keys; +}; + +/* Callback hook for walking packets in gpg key file. Extracts + the DSA coefficients from any public key packets encountered and + converts them into s-expr pubkey format, returning the public + keys thus found to the caller in a vector in the userdata context. */ +static enum +pkt_cb_resp key_file_walker (struct packet_walker *wlk, unsigned char tag, + size_t packetsize, size_t hdrpos) +{ + struct key_data *kdat = (struct key_data *)(wlk->userdata); + + MESSAGE ("key packet %d size %d at offs $%04x kdat $%08x\n", tag, + packetsize, hdrpos, kdat); + + if (tag != RFC4880_PT_PUBLIC_KEY) + return pktCONTINUE; + + // So, get the data out. Version is first. In case of any errors during + // parsing, we just discard the key and continue, hoping to find a good one. + char ver = pkt_getch (wlk->pfile); + if (ver != 4) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported key version."); + return pktCONTINUE; + } + + // Only V4 accepted. Discard creation time. + if (pkt_getdword (wlk->pfile) == -1) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing creation time."); + return pktCONTINUE; + } + + char pkalg = pkt_getch (wlk->pfile); + if (pkalg != RFC4880_PK_DSA) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, pkalg, "unsupported key alg."); + return pktCONTINUE; + } + + // Next, the four MPIs should be present. Read them out, + // convert to an s-expr and add that to the list. + gcry_mpi_t p, q, g, y; + p = q = g = y = 0; + + if ((pkt_get_mpi (&p, wlk->pfile) >= 0) + && (pkt_get_mpi (&q, wlk->pfile) >= 0) + && (pkt_get_mpi (&g, wlk->pfile) >= 0) + && (pkt_get_mpi (&y, wlk->pfile) >= 0)) + { + // Convert to s-expr. + gcry_sexp_t new_key; + size_t n; + + gcry_error_t rv = gcry_sexp_build (&new_key, &n, dsa_pubkey_templ, p, q, g, y); + if (rv != GPG_ERR_NO_ERROR) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr."); + return pktCONTINUE; + } + +#if CRYPTODEBUGGING + // Debugging + char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE]; + n = gcry_sexp_sprint (new_key, GCRYSEXP_FMT_ADVANCED, sexprbuf, + GPG_KEY_SEXPR_BUF_SIZE); + msg ("key:%d\n'%s'", n, sexprbuf); +#endif /* CRYPTODEBUGGING */ + + // Return it to caller in the vector. + kdat->keys.push_back (new_key); + } + + // Release temps and continue. + if (p) + gcry_mpi_release (p); + if (q) + gcry_mpi_release (q); + if (g) + gcry_mpi_release (g); + if (y) + gcry_mpi_release (y); + + return pktCONTINUE; +} + +/* Does what its name suggests: feeds a chosen amount of the data found + at the current seek position in an io_stream into the message digest + context passed in, using reasonably-sized chunks for efficiency. */ +static size_t +shovel_stream_data_into_md (io_stream *stream, size_t nbytes, gcry_md_hd_t md) +{ + const size_t TMPBUFSZ = 1024; + unsigned char tmpbuf[TMPBUFSZ]; + size_t this_time, total = 0; + ssize_t actual; + MESSAGE ("shovel %d bytes at pos $%08x\n", nbytes, stream->tell ()); + while (nbytes) + { + this_time = (nbytes > TMPBUFSZ) ? TMPBUFSZ : nbytes; + actual = stream->read (tmpbuf, this_time); + if (actual <= 0) + break; + gcry_md_write (md, tmpbuf, actual); + total += actual; + nbytes -= actual; + if (actual != (ssize_t)this_time) + break; + } + return total; +} + +/* Canonicalise an s-expr by converting LFs to spaces so that + it's all on one line and folding multiple spaces as we go. */ +static size_t +fold_lfs_and_spaces (char *buf, size_t n) +{ + char *ptr1 = buf, *ptr2 = buf; + + while (n--) + { + char ch = *ptr1++; + if (ch == 0x0a) + ch = ' '; + *ptr2++ = ch; + if (ch == 0x20) + while (n && ((*ptr1 == ' ') || (*ptr1 == 0x0a))) + { + --n; + ++ptr1; + } + } + return ptr2 - buf; +} + +/* Do-nothing stubs called by the sig file walker to + walk over the embedded subpackets. In the event, we don't + actually need to do this as we aren't inspecting them. */ +static enum +pkt_cb_resp hashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag, + size_t packetsize, size_t hdrpos) +{ + return pktCONTINUE; +} + +static enum +pkt_cb_resp unhashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag, + size_t packetsize, size_t hdrpos) +{ + return pktCONTINUE; +} + +/* Callback to parse the packets found in the setup.ini/setup.bz2 + signature file. We have to parse the header to get the hash type + and other details. Once we have that we can create a message + digest context and start pumping data through it; first the ini + file itself, then the portion of the packet itself that is + covered by the hash. */ + static enum +pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag, + size_t packetsize, size_t hdrpos) +{ + struct sig_data *sigdat = (struct sig_data *)(wlk->userdata); + sigdat->complete = false; + + if (tag != RFC4880_PT_SIGNATURE) + return pktCONTINUE; + + // To add the trailers later, we hang on to the current pos. + size_t v34hdrofs = wlk->pfile->tell (); + + // So, get the data out. Version is first. + char ver = pkt_getch (wlk->pfile); + if ((ver < 3) || (ver > 4)) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported sig version."); + return pktHALT; + } + + // Only V3 and V4 accepted. + if (ver == 4) + { + sigdat->sig_type = pkt_getch (wlk->pfile); + sigdat->pk_alg = pkt_getch (wlk->pfile); + sigdat->hash_alg = pkt_getch (wlk->pfile); + } + else + { + int hmsize = pkt_getch (wlk->pfile); + if (hmsize != RFC4880_SIGV3_HASHED_SIZE) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size."); + return pktHALT; + } + v34hdrofs = wlk->pfile->tell (); + if ((pkt_getch (wlk->pfile) < 0) || (pkt_getdword (wlk->pfile) == -1)) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size."); + return pktHALT; + } + if ((pkt_getdword (wlk->pfile) == -1) || (pkt_getdword (wlk->pfile) == -1)) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing signer ID."); + return pktHALT; + } + + sigdat->sig_type = 0; + sigdat->pk_alg = pkt_getch (wlk->pfile); + sigdat->hash_alg = pkt_getch (wlk->pfile); + } + + MESSAGE ("sig type %d, pk_alg %d, hash_alg %d\n", sigdat->sig_type, + sigdat->pk_alg, sigdat->hash_alg); + + // We only handle binary file signatures + if (sigdat->sig_type != RFC4880_ST_BINARY) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->sig_type, "unsupported sig type."); + return pktHALT; + } + // And we only speak DSA. + if (sigdat->pk_alg != RFC4880_PK_DSA) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->pk_alg, "unsupported pk alg."); + return pktHALT; + } + + // Start to hash all the data. Figure out what hash to use. + sigdat->algo = pkt_convert_hashcode (sigdat->hash_alg); + if (sigdat->algo == GCRY_MD_NONE) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "unconvertible hash."); + return pktHALT; + } + + // Now we know hash algo, we can create md context. + gcry_error_t rv = gcry_md_open (&sigdat->md, sigdat->algo, 0); + if (rv != GPG_ERR_NO_ERROR) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while initialising message digest."); + return pktHALT; + } + + // Add all the sig_file data into the hash. + sigdat->sign_data->seek (0, IO_SEEK_SET); + size_t nbytes = sigdat->sign_data->get_size (); + if (nbytes != shovel_stream_data_into_md (sigdat->sign_data, nbytes, sigdat->md)) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error."); + return pktHALT; + } + sigdat->sign_data->seek (0, IO_SEEK_SET); + + // V4 now has some hashed subpackets + int hashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0; + if (hashed_subpkt_size) + pkt_walk_subpackets (wlk->pfile, hashed_subpkt_walker, wlk->owner, + wlk->pfile->tell (), hashed_subpkt_size, wlk->userdata); + + // V4 now has some unhashed subpackets + int unhashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0; + if (unhashed_subpkt_size) + pkt_walk_subpackets (wlk->pfile, unhashed_subpkt_walker, wlk->owner, + wlk->pfile->tell (), unhashed_subpkt_size, wlk->userdata); + + // Both formats now have 16 bits of the hash value. + int hash_first = pkt_getword (wlk->pfile); + + MESSAGE ("sig type %d, pk_alg %d, hash_alg %d - first $%04x\n", sigdat->sig_type, + sigdat->pk_alg, sigdat->hash_alg, hash_first); + + /* Algorithm-Specific Fields for DSA signatures: + + - MPI of DSA value r. + + - MPI of DSA value s. + + DSA signatures MUST use hashes that are equal in size to the number + of bits of q, the group generated by the DSA key's generator value. */ + + if ((pkt_get_mpi (&sigdat->dsa_mpi_r, wlk->pfile) < 0) + || (pkt_get_mpi (&sigdat->dsa_mpi_s, wlk->pfile) < 0)) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi."); + return pktHALT; + } + + MESSAGE ("Read sig packets succesfully!\n"); + + // Now we got all the data out ok, rewind and hash the first trailer. + wlk->pfile->seek (v34hdrofs, IO_SEEK_SET); + nbytes = (ver == 4) ? (RFC4880_SIGV4_HASHED_OVERHEAD + hashed_subpkt_size) + : (RFC4880_SIGV3_HASHED_SIZE); + if (nbytes != shovel_stream_data_into_md (wlk->pfile, nbytes, sigdat->md)) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error 2."); + return pktHALT; + } + + if (ver == 4) + { + // And now the synthetic final trailer. + gcry_md_putc (sigdat->md, 4); + gcry_md_putc (sigdat->md, 0xff); + gcry_md_putc (sigdat->md, (nbytes >> 24) & 0xff); + gcry_md_putc (sigdat->md, (nbytes >> 16) & 0xff); + gcry_md_putc (sigdat->md, (nbytes >> 8) & 0xff); + gcry_md_putc (sigdat->md, nbytes & 0xff); + } + + // Hooray, succeeded! + sigdat->complete = true; + + return pktHALT; +} + +/* Size and allocate a temp buffer to print a representation + of a public key s-expr into, then add that to the extra keys + setting so it persists for the next run. */ +void +add_key_from_sexpr (gcry_sexp_t key) +{ + size_t n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, 0, ~0); + char *sexprbuf = new char[n]; + n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, sexprbuf, n); + // +1 because we want to include the nul-terminator. + n = fold_lfs_and_spaces (sexprbuf, n + 1); + ExtraKeys.add_key (sexprbuf); + MESSAGE ("keep:%d\n'%s'", n, sexprbuf); + delete [] sexprbuf; +} + +/* Verify the signature on an ini file. Takes care of all key-handling. */ +bool +verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner) +{ + /* DSA public key in s-expr format. */ + gcry_sexp_t dsa_key; + + /* Data returned from packet walker. */ + struct sig_data sigdat; + + /* Vector of extra keys to use. */ + std::vector keys_to_try; + + /* Vector of cached extra keys from last run. */ + static std::vector input_keys; + + /* Overall status of signature. */ + bool sig_ok = false; + + // Temps for intermediate processing. + gcry_error_t rv; + size_t n; + + /* Initialise the library. */ + gcry_check_version (NULL); + + /* So first build the built-in key. */ + rv = gcry_sexp_new (&dsa_key, cygwin_pubkey_sexpr, strlen (cygwin_pubkey_sexpr), 1); + if (rv != GPG_ERR_NO_ERROR) + { + ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating pubkey s-expr."); + } + +#if CRYPTODEBUGGING + char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE]; + n = gcry_sexp_sprint (dsa_key, GCRYSEXP_FMT_ADVANCED, sexprbuf, GPG_KEY_SEXPR_BUF_SIZE); + msg ("key:%d\n'%s'", n, sexprbuf); +#endif /* CRYPTODEBUGGING */ + + /* Next we should extract the keys from the last-extrakeys + file, and flush it; we'll only return them to it if they + get used. OTOH, should we do this at all? The extrakeys + file isn't heavily protected. So we only trust the extra + keys if we're told to by the user. We still read them in + and write them back out, which canonicalises and eliminates + any duplicates or garbage lines that may have crept in. */ + static bool input_keys_read = false; + if (!input_keys_read) + { + // We only want to do this once, first time through: + input_keys_read = true; + // Copy all valid keys from ExtraKeysSetting into a + // static vector where we can keep them throughout the + // remainder of the run. + for (size_t i = 0; i < ExtraKeys.num_keys (); i++) + { + const char *keystring = ExtraKeys.get_key (i, &n); + gcry_sexp_t newkey; + rv = gcry_sexp_new (&newkey, keystring, n, 1); + if (rv == GPG_ERR_NO_ERROR) + input_keys.push_back (newkey); + } + + // Now flush out the ExtraKeysSetting; from here on it + // will build up a list of the keys we want to retain. + ExtraKeys.flush (); + + // Which, if we aren't using them, means all the ones + // we just read. + if (KeepUntrustedKeysOption || !UntrustedKeysOption) + { + std::vector::iterator it; + for (it = input_keys.begin (); it < input_keys.end (); ++it) + add_key_from_sexpr (*it); + } + } + + /* Next, there may have been command-line options. */ + std::string SexprExtraKeyString = SexprExtraKeyOption; + MESSAGE ("key str is '%s'\n", SexprExtraKeyString.c_str ()); + if (SexprExtraKeyString.size ()) + { + gcry_sexp_t dsa_key2 = 0; + rv = gcry_sexp_new (&dsa_key2, SexprExtraKeyString.c_str (), + SexprExtraKeyString.size (), 1); + if (rv == GPG_ERR_NO_ERROR) + { + // We probably want to add it to the extra keys setting + // if KeepUntrustedKeysOption is supplied. + if (KeepUntrustedKeysOption) + add_key_from_sexpr (dsa_key2); +#if CRYPTODEBUGGING + n = gcry_sexp_sprint (dsa_key2, GCRYSEXP_FMT_ADVANCED, sexprbuf, + GPG_KEY_SEXPR_BUF_SIZE); + // +1 because we want to include the nul-terminator. + n = fold_lfs_and_spaces (sexprbuf, n + 1); + ExtraKeys.add_key (sexprbuf); + msg ("key2:%d\n'%s'", n, sexprbuf); +#endif /* CRYPTODEBUGGING */ + keys_to_try.push_back (dsa_key2); + } + else + { + ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "invalid command-line pubkey s-expr."); + } + } + + /* Also, we may have to read a key(s) file. */ + std::string ExtraKeysFile = ExtraKeyOption; + if (ExtraKeysFile.size ()) + { + io_stream *keys = get_url_to_membuf (ExtraKeysFile, owner); + if (keys) + { + struct key_data kdat; + pkt_walk_packets (keys, key_file_walker, owner, 0, keys->get_size (), &kdat); + // We now have a vector of (some/any?) keys returned from + // the walker; add them to the list to try. + while (!kdat.keys.empty ()) + { + // We probably want to add it to the extra keys setting + // if KeepUntrustedKeysOption is supplied. + if (KeepUntrustedKeysOption) + add_key_from_sexpr (kdat.keys.back ()); +#if CRYPTODEBUGGING + n = gcry_sexp_sprint (kdat.keys.back (), GCRYSEXP_FMT_ADVANCED, + sexprbuf, GPG_KEY_SEXPR_BUF_SIZE); + // +1 because we want to include the nul-terminator. + n = fold_lfs_and_spaces (sexprbuf, n + 1); + ExtraKeys.add_key (sexprbuf); + msg ("key3:%d\n'%s'", n, sexprbuf); +#endif /* CRYPTODEBUGGING */ + keys_to_try.push_back (kdat.keys.back ()); + kdat.keys.pop_back (); + } + } + } + + // We pass in a pointer to the ini file in the user context data, + // which the packet walker callback uses to create a new hash + // context preloaded with all the signature-covered data. + sigdat.complete = false; + sigdat.sign_data = ini_file; + sigdat.dsa_mpi_r = sigdat.dsa_mpi_s = 0; + sigdat.md = 0; + pkt_walk_packets (ini_sig_file, sig_file_walker, owner, 0, + ini_sig_file->get_size (), &sigdat); + if (sigdat.complete) + { + /* DSA sig coefficients in s-expr format. */ + gcry_sexp_t dsa_sig; + + /* DSA signature hash data in s-expr format. */ + gcry_sexp_t dsa_hash; + + /* So, we have hashed all the data, and found the sig coefficients. + Next stages are to finalise the hash, build everything into + s-exprs, and call the libgcrypt verification routine. */ + + rv = gcry_sexp_build (&dsa_sig, &n, dsa_sig_templ, sigdat.dsa_mpi_r, + sigdat.dsa_mpi_s); + if (rv != GPG_ERR_NO_ERROR) + { + ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr."); + return false; + } + + gcry_md_final (sigdat.md); + + // Make a temp mpi from the hash output, then an s-expr from that. + gcry_mpi_t dsa_mpi_hash = 0; + unsigned char *tmpbuf = gcry_md_read (sigdat.md, 0); + size_t dlen = gcry_md_get_algo_dlen (sigdat.algo); + rv = gcry_mpi_scan (&dsa_mpi_hash, GCRYMPI_FMT_USG, tmpbuf, dlen, 0UL); + if (rv != GPG_ERR_NO_ERROR) + { + ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash MPI."); + return false; + } + + rv = gcry_sexp_build (&dsa_hash, &n, data_hash_templ, dsa_mpi_hash); + if (rv != GPG_ERR_NO_ERROR) + { + ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr."); + return false; + } + +#if CRYPTODEBUGGING + n = gcry_sexp_sprint (dsa_sig, GCRYSEXP_FMT_ADVANCED, sexprbuf, + GPG_KEY_SEXPR_BUF_SIZE); + msg ("sig:%d\n'%s'", n, sexprbuf); + n = gcry_sexp_sprint (dsa_hash, GCRYSEXP_FMT_ADVANCED, sexprbuf, + GPG_KEY_SEXPR_BUF_SIZE); + msg ("hash:%d\n'%s'", n, sexprbuf); +#endif /* CRYPTODEBUGGING */ + + // Well, we're actually there! Try it against the main key. + rv = gcry_pk_verify (dsa_sig, dsa_hash, dsa_key); + // If not that, try any supplied on the commandline. + if (rv != GPG_ERR_NO_ERROR) + { + std::vector::iterator it; + for (it = keys_to_try.begin (); it < keys_to_try.end (); ++it) + { + MESSAGE ("Testing a key to try\n"); + rv = gcry_pk_verify (dsa_sig, dsa_hash, *it); + if (rv != GPG_ERR_NO_ERROR) + continue; + // Found it! This key gets kept! + add_key_from_sexpr (*it); + break; + } + + // We only use the untrusted keys if told to. + it = ((rv != GPG_ERR_NO_ERROR) + && (KeepUntrustedKeysOption || UntrustedKeysOption)) + ? input_keys.begin () + : input_keys.end (); + for ( ; it < input_keys.end (); ++it) + { + MESSAGE ("Testing an input key\n"); + rv = gcry_pk_verify (dsa_sig, dsa_hash, *it); + if (rv != GPG_ERR_NO_ERROR) + continue; + // Found it! This key gets kept! + add_key_from_sexpr (*it); + break; + } + } + + sig_ok = (rv == GPG_ERR_NO_ERROR); + +#if CRYPTODEBUGGING + gcry_err_code_t code; + gcry_err_source_t src; + code = gcry_err_code (rv); + src = gcry_err_source (rv); + msg ("Well, pk verify returned $%08x - code %d src %d\n", rv, code, src); +#endif /* CRYPTODEBUGGING */ + + gcry_mpi_release (dsa_mpi_hash); + gcry_sexp_release (dsa_sig); + gcry_sexp_release (dsa_hash); + } + + // Discard the temp data then. + gcry_sexp_release (dsa_key); + if (sigdat.dsa_mpi_r) + gcry_mpi_release (sigdat.dsa_mpi_r); + if (sigdat.dsa_mpi_s) + gcry_mpi_release (sigdat.dsa_mpi_s); + if (sigdat.md) + gcry_md_close (sigdat.md); + while (keys_to_try.size ()) + { + gcry_sexp_release (keys_to_try.back ()); + keys_to_try.pop_back (); + } + + return sig_ok; +} + diff --git a/crypto.h b/crypto.h new file mode 100755 index 00000000..860df6c7 --- /dev/null +++ b/crypto.h @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2008, Dave Korn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + * Written by Dave Korn + * + */ + +#ifndef SETUP_CRYPTO_H +#define SETUP_CRYPTO_H + +/* This module uses libgcrypt functionality to verify signatures + * on downloaded setup.ini or setup.bz2 files. + */ + +/* for HWND */ +#include "win32.h" +class io_stream; + +/* This is currently the only public API exported by the module; it + takes the contents of setup.ini or setup.bz2 in one (memory-based, + for preference) io_stream, and the contents of the related signature + file in another. It is called from ini.cc/do_remote_ini() and returns + true if the signature verified OK; if it returns false, you MUST NOT + use the failed ini file - doubly so if it's a compressed stream! */ +extern bool verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner); + +/* +5.2.2. Version 3 Signature Packet Format + + The body of a version 3 Signature Packet contains: + + - One-octet version number (3). + + - One-octet length of following hashed material. MUST be 5. + + - One-octet signature type. + + - Four-octet creation time. + + - Eight-octet Key ID of signer. + + - One-octet public-key algorithm. + + - One-octet hash algorithm. + + - Two-octet field holding left 16 bits of signed hash value. + + - One or more multiprecision integers comprising the signature. + This portion is algorithm specific, as described below. + + The concatenation of the data to be signed, the signature type, and + creation time from the Signature packet (5 additional octets) is + hashed. The resulting hash value is used in the signature algorithm. + The high 16 bits (first two octets) of the hash are included in the + Signature packet to provide a quick test to reject some invalid + signatures. + + Algorithm-Specific Fields for RSA signatures: + + - multiprecision integer (MPI) of RSA signature value m**d mod n. + + Algorithm-Specific Fields for DSA signatures: + + - MPI of DSA value r. + + - MPI of DSA value s. + + The signature calculation is based on a hash of the signed data, as + described above. The details of the calculation are different for + DSA signatures than for RSA signatures. + + With RSA signatures, the hash value is encoded using PKCS#1 encoding + type EMSA-PKCS1-v1_5 as described in Section 9.2 of RFC 3447. This + requires inserting the hash value as an octet string into an ASN.1 + structure. The object identifier for the type of hash being used is + included in the structure. The hexadecimal representations for the + currently defined hash algorithms are as follows: + + - MD5: 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05 + + - RIPEMD-160: 0x2B, 0x24, 0x03, 0x02, 0x01 + + - SHA-1: 0x2B, 0x0E, 0x03, 0x02, 0x1A + + - SHA224: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 + + - SHA256: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 + + - SHA384: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 + + - SHA512: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 + + The ASN.1 Object Identifiers (OIDs) are as follows: + + - MD5: 1.2.840.113549.2.5 + + - RIPEMD-160: 1.3.36.3.2.1 + + - SHA-1: 1.3.14.3.2.26 + + - SHA224: 2.16.840.1.101.3.4.2.4 + + - SHA256: 2.16.840.1.101.3.4.2.1 + + - SHA384: 2.16.840.1.101.3.4.2.2 + + - SHA512: 2.16.840.1.101.3.4.2.3 + + The full hash prefixes for these are as follows: + + MD5: 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, + 0x04, 0x10 + + RIPEMD-160: 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, + 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 + + SHA-1: 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E, + 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 + + SHA224: 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, + 0x00, 0x04, 0x1C + + SHA256: 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20 + + SHA384: 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, + 0x00, 0x04, 0x30 + + SHA512: 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40 + + DSA signatures MUST use hashes that are equal in size to the number + of bits of q, the group generated by the DSA key's generator value. + + If the output size of the chosen hash is larger than the number of + bits of q, the hash result is truncated to fit by taking the number + of leftmost bits equal to the number of bits of q. This (possibly + truncated) hash function result is treated as a number and used + directly in the DSA signature algorithm. + + +5.2.3. Version 4 Signature Packet Format + + The body of a version 4 Signature packet contains: + + - One-octet version number (4). + + - One-octet signature type. + + - One-octet public-key algorithm. + + - One-octet hash algorithm. + + - Two-octet scalar octet count for following hashed subpacket data. + Note that this is the length in octets of all of the hashed + subpackets; a pointer incremented by this number will skip over + the hashed subpackets. + + - Hashed subpacket data set (zero or more subpackets). + + - Two-octet scalar octet count for the following unhashed subpacket + data. Note that this is the length in octets of all of the + unhashed subpackets; a pointer incremented by this number will + skip over the unhashed subpackets. + + - Unhashed subpacket data set (zero or more subpackets). + + - Two-octet field holding the left 16 bits of the signed hash + value. + + - One or more multiprecision integers comprising the signature. + This portion is algorithm specific, as described above. + + The concatenation of the data being signed and the signature data + from the version number through the hashed subpacket data (inclusive) + is hashed. The resulting hash value is what is signed. The left 16 + bits of the hash are included in the Signature packet to provide a + quick test to reject some invalid signatures. + + There are two fields consisting of Signature subpackets. The first + field is hashed with the rest of the signature data, while the second + is unhashed. The second set of subpackets is not cryptographically + protected by the signature and should include only advisory + information. + + The algorithms for converting the hash function result to a signature + are described in a section below. + + +5.2.4. Computing Signatures + + All signatures are formed by producing a hash over the signature + data, and then using the resulting hash in the signature algorithm. + + For binary document signatures (type 0x00), the document data is + hashed directly. For text document signatures (type 0x01), the + document is canonicalized by converting line endings to , + and the resulting data is hashed. + + When a signature is made over a key, the hash data starts with the + octet 0x99, [ ... ] + + A certification signature (type 0x10 through 0x13) [ ... ] + + When a signature is made over a Signature packet (type 0x50), [ ... ] + + Once the data body is hashed, then a trailer is hashed. A V3 + signature hashes five octets of the packet body, starting from the + signature type field. This data is the signature type, followed by + the four-octet signature time. A V4 signature hashes the packet body + starting from its first field, the version number, through the end + of the hashed subpacket data. Thus, the fields hashed are the + signature version, the signature type, the public-key algorithm, the + hash algorithm, the hashed subpacket length, and the hashed + subpacket body. + + V4 signatures also hash in a final trailer of six octets: the + version of the Signature packet, i.e., 0x04; 0xFF; and a four-octet, + big-endian number that is the length of the hashed data from the + Signature packet (note that this number does not include these final + six octets). + + After all this has been hashed in a single hash context, the + resulting hash field is used in the signature algorithm and placed + at the end of the Signature packet. + +*/ + +#define RFC4880_SIGV3_HASHED_SIZE 5 +#define RFC4880_SIGV4_HASHED_OVERHEAD 6 + + +/* +5.5. Key Material Packet + + A key material packet contains all the information about a public or + private key. There are four variants of this packet type, and two + major versions. Consequently, this section is complex. + +5.5.1. Key Packet Variants + +5.5.1.1. Public-Key Packet (Tag 6) + + A Public-Key packet starts a series of packets that forms an OpenPGP + key (sometimes called an OpenPGP certificate). + +5.5.1.2. Public-Subkey Packet (Tag 14) +5.5.1.3. Secret-Key Packet (Tag 5) +5.5.1.4. Secret-Subkey Packet (Tag 7) + +5.5.2. Public-Key Packet Formats + + The version 4 format is similar to the version 3 format except for + the absence of a validity period. This has been moved to the + Signature packet. In addition, fingerprints of version 4 keys are + calculated differently from version 3 keys, as described in the + section "Enhanced Key Formats". + + A version 4 packet contains: + + - A one-octet version number (4). + + - A four-octet number denoting the time that the key was created. + + - A one-octet number denoting the public-key algorithm of this key. + + - A series of multiprecision integers comprising the key material. + This algorithm-specific portion is: + + Algorithm-Specific Fields for RSA public keys: + + - multiprecision integer (MPI) of RSA public modulus n; + + - MPI of RSA public encryption exponent e. + + Algorithm-Specific Fields for DSA public keys: + + - MPI of DSA prime p; + + - MPI of DSA group order q (q is a prime divisor of p-1); + + - MPI of DSA group generator g; + + - MPI of DSA public-key value y (= g**x mod p where x + is secret). + + Algorithm-Specific Fields for Elgamal public keys: + + - MPI of Elgamal prime p; + + - MPI of Elgamal group generator g; + + - MPI of Elgamal public key value y (= g**x mod p where x + is secret). + +*/ + +// Big enough to dump the coefficients of a DSA +// signing key of any reasonable size in ASCII +// s-expr representation. +#define GPG_KEY_SEXPR_BUF_SIZE (8192) + +// As long as you respect this maximum coefficient size. +#define GPG_KEY_MAX_COEFF_SIZE (8192) + +#endif /* SETUP_CRYPTO_H */ diff --git a/cyg-pubkey.h b/cyg-pubkey.h new file mode 100755 index 00000000..6d79544a --- /dev/null +++ b/cyg-pubkey.h @@ -0,0 +1,14 @@ + +/* Autogenerated from: ./cygwin.pub + * by: ./gpg-key-to-s-expr.sh + * at: Mon Jun 16 02:20:11 2008 */ + +"(public-key " + "(dsa " + "(p #00b96e7de7db21b47aa365a60fc3ec39195d07c550164dd43e2c2ff36c5ca21242403716c8937a70d80cc142cb73498820dc8a1269acfdb1b3815cdb93047262788fd5fdddb095e03b47bf6daa3b55d61a4c6bfd74096716265311617b304e09977ec178abd22cc8b06821475f9e8ba8bebcbe26458cbcc293022b07fc5f4a91cb#) " + "(q #00d65d896bdd4fb133ba8ad55400260cbca3450871#) " + "(g #17a7506fa4611721f581ee0f01bd2f19218304846743481948192c73181d90e5716051a15eae3dca9ada22acf2fbc010d31c196aa8f9fb91c9c190ebece5167dbd2281eb73130c336ed5a627d0f7537902a81230f3881642ac7b654d150da32f8b7535f7506b346f6688f0917b9863d901d7a8746366667b53ede51802ff02e1#) " + "(y #2592db6bb37125ce400e66dad3c22eed0899cd47cff95589d577adcd106c805e4feaada954a103be18d41f657254bc2a182218a71e1eefcdfa8c4f69758068b416e4942d7ddd6398ec9b455cbbe7fb5cb943a04babc75bc3602bfef500014030cf5ee649939e690c6d5341bbc0155f14eb059d088e61070709b037f02cccf137#)" + ")" +")" + diff --git a/cygwin.pub b/cygwin.pub new file mode 100755 index 0000000000000000000000000000000000000000..8372e5fd2ed9388910d9a685bac9066a74a147e4 GIT binary patch literal 964 zcmV;#13Ubg0ipy*Qu=QZ1OT~iedpUDw0fgurVqpHIT>9C#ZVSa)IKaP^K4wA5<);X z7RZx&aM%pNLd$bWh#=gG5^1dcv9p0(+mi%xVt9|${oSyY;5$dZZK^v})*4J}{d5Uu z7A8{>VS6x6373Asc&pMZ$gpT3M_-6PwK6yRG|aD7}OtSa#Xx37$O*_9v<(_`ixI$b%1EJ z7UYyIecfZ2?3+bgyXX5{xkI2!tH)czU@QLh00BTS&tB$9lb&e|ZBs$Jz!hH<>jj+% zj$sD}39vWtEX?sYv>HQsXLo6CAUtDvXLo6CKx27lcWG`eV{dIfh+YH}5dr`n1p-J? z`fmao0|pBT2nPcK0~G=T0~P`S0v-VZ7k~f?2@t8GV*h7gLArMt0HCIQ1hz!yCG>w4 zXsMkr;D45bQ7-_WPOM*BqWX#Xb+|kzAIp5)^kb0~h(-hu5dr`P1p-J?{-^*72@p-v zg5_#Bj6Pxh0HDFX0LaEg_&lK)Iz{Wi_#7x_N236s>e~j`8l&CBq@aYJ?fuE-|2uzA zxd9CXNK*Q65Ci~K_&((=FNXrIX74d}n@NZB+NeKjs<8JHTHtR+3DJ0u z&%9LTR6^~?D?^Y@wnK#`(qNPR!K0cwjlZyo=RFV|S zi_&B`!qHlp9K2mPh)Dz(5dr`S1p-J?`fvgp3;+rV5UHYK|7T!9x_aOMo+)TpDVR#0 m6f7ayxI-JFleUamZ~&mNmhA$pNo3{d(WT&$b` literal 0 HcmV?d00001 diff --git a/gpg-error-config-fake b/gpg-error-config-fake new file mode 100755 index 00000000..7dcc92da --- /dev/null +++ b/gpg-error-config-fake @@ -0,0 +1,57 @@ +#!/bin/bash +# +# Copyright (c) 2008, Dave Korn. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# A copy of the GNU General Public License can be found at +# http://www.gnu.org/ +# +# Written by Dave Korn +# +# +# Fake gpg-error-config script redirects libgcrypt configure +# process to look for our newly-built libgpg-error. Crudely and +# viciously hacked up by ripping the guts out of the original +# until it does just enough to fool the libgcrypt configure +# process into using our freshly-built libgpg-error for us. +# + +output="" +mydir=`dirname $0` +while test $# -gt 0; do + case "$1" in + -*=*) + optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` + ;; + *) + optarg= + ;; + esac + + case $1 in + --version) + echo "1.6" + exit 0 + ;; + --dir=*) + mydir="${1#--dir=}" + ;; + --cflags) + output="$output -I$mydir/./libgpg-error/src/" + ;; + --libs) + output="$output -L$mydir/./libgpg-error/src/.libs" + output="$output -lgpg-error" + ;; + *) + echo "What's a $1?" + ;; + esac + shift +done + +echo $output diff --git a/gpg-key-to-s-expr.sh b/gpg-key-to-s-expr.sh new file mode 100755 index 00000000..1938bbfc --- /dev/null +++ b/gpg-key-to-s-expr.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# +# Copyright (c) 2008, Dave Korn. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# A copy of the GNU General Public License can be found at +# http://www.gnu.org/ +# +# Written by Dave Korn +# +# +# Converts a gpg dsa pub key file to a text s-expr for +# building into setup.exe's signature verification. +# Relies on having pgpdump installed. +# +# Usage: +# gpg-key-to-s-expr.sh [-q|-e|-Q|-c|-C|-1] KEYFILE +# +# -q means surround output in quotes to make it easier +# for use in a C #include fragment. -e means escape the +# line ends. -Q means both. -c means output a C-style +# string for printing, -C makes the strine a one-liner and +# outputs a C header as well. -1 means generate all the +# output on a single line. Only one option should be +# specified or the behaviour is undefined. + + +# Usage: find_a_line DSACOEFFICIENT PGPDUMPFILE +# Returns the hex data for the named DSA coefficeint.. +function find_a_line() { + grep "DSA $1([0-9]* bits) -" < "$2" \ + | sed -e 's/^.*- //g' | tr -d ' ' +} + +# Usage: line_to_sexpr HEXDATA +# Convert hex data from find_a_line to s-expr format: +# prepends 00 to avoid signedness problems if high bit +# is set, and surrounds with hash marks. +function line_to_sexpr() { + echo "#"`echo "$1" | sed -e's/^\([89A-Fa-f]\)/00\1/g'`"#" +} + +quotes= +escapes= +starts= +mid= +header= +nl="\n" +ind=" " + +if [ x$1 == x-q ] ; +then + quotes='"' + shift ; +fi +if [ x$1 == x-e ] ; +then + escapes='\\' + shift ; +fi +if [ x$1 == x-Q ] ; +then + quotes='"' + escapes='\\' + shift ; +fi +if [ x$1 == x-c ] ; +then + quotes='"' + escapes="\\\\n$quotes" + starts="$quotes" + shift ; +fi +if [ x$1 == x-C ] ; +then + quotes='"' + escapes="$quotes" + mid="$quotes" + header="\\n/* Autogenerated from: $2\\n *\\t\\t by: $0\\n *\\t\\t at: `date "+%c"`\\t\\t\\t*/\\n\\n" + shift ; +fi +if [ x$1 == x-1 ] ; +then + nl= + ind= + shift ; +fi + +if [ $# -ne 1 ] ; +then + echo "Missing KEYFILE arg" + exit 1 ; +fi + +TMPFILE=`mktemp -t "$(basename "$1").XXXXXXXXXX"` || exit 1 + +pgpdump -milpu "$1" >"$TMPFILE" || exit 1 + +# Yes, this could be done *far* more efficiently in any one of +# perl/awk/python/$YOURFAVOURITETOOL than by spawning a whole +# bunch of bashes, greps and seds. No, I don't care. Don't bug +# me about it until we have to run this script a million times a day! + +dsa_p=`find_a_line p $TMPFILE` +dsa_q=`find_a_line q $TMPFILE` +dsa_g=`find_a_line g $TMPFILE` +dsa_y=`find_a_line y $TMPFILE` + +dsa_p=`line_to_sexpr "$dsa_p"` +dsa_q=`line_to_sexpr "$dsa_q"` +dsa_g=`line_to_sexpr "$dsa_g"` +dsa_y=`line_to_sexpr "$dsa_y"` + +echo -e $header$quotes"(public-key $escapes$nl\ +$starts$ind$mid(dsa $escapes$nl\ +$starts$ind$ind$mid(p $dsa_p) $escapes$nl\ +$starts$ind$ind$mid(q $dsa_q) $escapes$nl\ +$starts$ind$ind$mid(g $dsa_g) $escapes$nl\ +$starts$ind$ind$mid(y $dsa_y)$escapes$nl\ +$starts$ind$mid)$escapes$nl\ +$starts$mid)$quotes$nl"; + +rm "$TMPFILE" diff --git a/gpg-packet.cc b/gpg-packet.cc new file mode 100755 index 00000000..fa9e55bf --- /dev/null +++ b/gpg-packet.cc @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2008, Dave Korn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + * This module contains support utilities to assist in reading and + * parsing RFC4880-compliant OpenPGP format signature and key files, + * and related constant definitions. + * + * + * Written by Dave Korn + * + */ + +#if 0 +static const char *cvsid = + "\n%%% $Id$\n"; +#endif + +#include +#include +#include +#include "io_stream.h" +#include "gcrypt.h" +#include "gpg-packet.h" +#include "msg.h" +#include "resource.h" + +#define CRYPTODEBUGGING (0) + +#if CRYPTODEBUGGING +#define ERRKIND __asm__ __volatile__ (".byte 0xcc"); note +#define MESSAGE msg +#else /* !CRYPTODEBUGGING */ +#define ERRKIND note +#define MESSAGE while (0) msg +#endif /* CRYPTODEBUGGING */ + +#define ARRAYSIZE(_ar) (sizeof (_ar) / sizeof (_ar[0])) + +static const struct { char from; char to; } RFC4880HashCodesToGPGHashCodes[] = +{ + { RFC4880_HC_MD5, GCRY_MD_MD5 }, + { RFC4880_HC_SHA1, GCRY_MD_SHA1 }, + { RFC4880_HC_RIPEMD160, GCRY_MD_RMD160 }, + { RFC4880_HC_SHA256, GCRY_MD_SHA256 }, + { RFC4880_HC_SHA384, GCRY_MD_SHA384 }, + { RFC4880_HC_SHA512, GCRY_MD_SHA512 }, + { RFC4880_HC_SHA224, GCRY_MD_SHA224 } +}; + +char +pkt_convert_hashcode (char rfc_hash) +{ + for (unsigned int i = 0; i < ARRAYSIZE(RFC4880HashCodesToGPGHashCodes); i++) + if (RFC4880HashCodesToGPGHashCodes[i].from == rfc_hash) + return RFC4880HashCodesToGPGHashCodes[i].to; + return GCRY_MD_NONE; +} + +/* +4.2.2. New Format Packet Lengths + + New format packets have four possible ways of encoding length: + + 1. A one-octet Body Length header encodes packet lengths of up to 191 + octets. + + 2. A two-octet Body Length header encodes packet lengths of 192 to + 8383 octets. + + 3. A five-octet Body Length header encodes packet lengths of up to + 4,294,967,295 (0xFFFFFFFF) octets in length. (This actually + encodes a four-octet scalar number.) + + 4. When the length of the packet body is not known in advance by the + issuer, Partial Body Length headers encode a packet of + indeterminate length, effectively making it a stream. + +4.2.2.1. One-Octet Lengths + + A one-octet Body Length header encodes a length of 0 to 191 octets. + This type of length header is recognized because the one octet value + is less than 192. The body length is equal to: + + bodyLen = 1st_octet; + +4.2.2.2. Two-Octet Lengths + + A two-octet Body Length header encodes a length of 192 to 8383 + octets. It is recognized because its first octet is in the range 192 + to 223. The body length is equal to: + + bodyLen = ((1st_octet - 192) << 8) + (2nd_octet) + 192 + +4.2.2.3. Five-Octet Lengths + + A five-octet Body Length header consists of a single octet holding + the value 255, followed by a four-octet scalar. The body length is + equal to: + + bodyLen = (2nd_octet << 24) | (3rd_octet << 16) | + (4th_octet << 8) | 5th_octet + + This basic set of one, two, and five-octet lengths is also used + internally to some packets. + +*/ +long +pkt_getlen (io_stream *file) +{ + int ch1, ch2; + + ch1 = pkt_getch (file); + // Obviously these two conditions fold into one, but since + // one is an error test and the other a range check it's + // nice to write them separately and let the compiler take + // care of it. + if (ch1 < 0) + return ch1; + if (ch1 < 192) + return ch1; + + if (ch1 == 255) + return pkt_getdword (file); + + ch2 = pkt_getch (file); + if (ch2 < 0) + return ch2; + if (ch1 < 224) + return ((ch1 - 192) << 8) + (ch2) + 192; + return -2; +} + +/* +3.2. Multiprecision Integers + + Multiprecision integers (also called MPIs) are unsigned integers used + to hold large integers such as the ones used in cryptographic + calculations. + + An MPI consists of two pieces: a two-octet scalar that is the length + of the MPI in bits followed by a string of octets that contain the + actual integer. + + These octets form a big-endian number; a big-endian number can be + made into an MPI by prefixing it with the appropriate length. + + Examples: + + (all numbers are in hexadecimal) + + The string of octets [00 01 01] forms an MPI with the value 1. The + string [00 09 01 FF] forms an MPI with the value of 511. + + Additional rules: + + The size of an MPI is ((MPI.length + 7) / 8) + 2 octets. + + The length field of an MPI describes the length starting from its + most significant non-zero bit. Thus, the MPI [00 02 01] is not + formed correctly. It should be [00 01 01]. + + Unused bits of an MPI MUST be zero. + + Also note that when an MPI is encrypted, the length refers to the + plaintext MPI. It may be ill-formed in its ciphertext. + +*/ +int +pkt_get_mpi (gcry_mpi_t *mpiptr, io_stream *file) +{ + /* "An MPI consists of two pieces: a two-octet scalar that is the + length of the MPI in bits followed by a string of octets that contain + the actual integer." */ + + long nbits = pkt_getword (file); + + if (nbits < 0) + return nbits; + + size_t nbytes = ((nbits + 7) >> 3); + + unsigned char *tmpbuf = new unsigned char [nbytes]; + + if (file->read (tmpbuf, nbytes) != (ssize_t)nbytes) + return -2; + + gcry_error_t rv = gcry_mpi_scan (mpiptr, GCRYMPI_FMT_USG, tmpbuf, nbytes, 0UL); + + delete[] tmpbuf; + + if (rv != GPG_ERR_NO_ERROR) + return -3; + + return 0; +} + +/* Walk the packets in an io_stream. */ +static void +walk_packets_1 (struct packet_walker *wlk) +{ + long size_left = wlk->size_to_walk; + size_t ostartpos = wlk->startpos; + MESSAGE ("walk $%08x bytes at startpos $%08x\n", wlk->size_to_walk, wlk->startpos); + while (size_left) + { + char packet_type; + long packet_len; + enum pkt_cb_resp rv; + size_t newstartpos; + + wlk->pfile->seek (wlk->startpos, IO_SEEK_SET); + + if (wlk->is_subpackets) + packet_len = pkt_getlen (wlk->pfile) - 1; + else + packet_len = -1; + + int tag = pkt_getch (wlk->pfile); + MESSAGE ("tag $%02x size $%08x\n", tag, size_left); + + if (!wlk->is_subpackets && ((tag < 0) || !(tag & 0x80))) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, tag, "illegal tag."); + return; + } + + if (wlk->is_subpackets) + packet_type = tag; + else if (tag & 0x40) + { + packet_type = tag & 0x3f; + packet_len = pkt_getlen (wlk->pfile); + } + else + { + packet_type = (tag >> 2) & 0x0f; + switch (tag & 3) + { + case 0: + packet_len = pkt_getch (wlk->pfile); + break; + case 1: + packet_len = pkt_getword (wlk->pfile); + break; + case 2: + packet_len = pkt_getdword (wlk->pfile); + break; + case 3: + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, tag, "illegal old tag."); + return; + } + } + + MESSAGE ("type $%02x len $%08x pos $%08x\n", packet_type, packet_len, + wlk->pfile->tell ()); + if (packet_len < 0) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, packet_len, "invalid packet"); + return; + } + else if (packet_len > (size_left - (wlk->pfile->tell () - (long)wlk->startpos))) + { + ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, packet_len, "malformed packet"); + return; + } + + newstartpos = wlk->pfile->tell () + packet_len; + rv = wlk->func ? wlk->func (wlk, packet_type, packet_len, wlk->startpos) + : pktCONTINUE; + if (rv == pktHALT) + break; + + wlk->startpos = newstartpos; + wlk->pfile->seek (wlk->startpos, IO_SEEK_SET); + size_left = wlk->size_to_walk - (wlk->startpos - ostartpos); + MESSAGE ("remaining $%08x nextpos $%08x\n", size_left, wlk->startpos); + } +} + +void * +pkt_walk_packets (io_stream *packet_file, packet_walk_cb func, HWND owner, + size_t startpos, size_t size_to_walk, void *userdata) +{ + struct packet_walker wlk; + wlk.pfile = packet_file; + wlk.func = func; + wlk.owner = owner; + wlk.userdata = userdata; + wlk.startpos = startpos; + wlk.size_to_walk = size_to_walk; + wlk.is_subpackets = false; + walk_packets_1 (&wlk); + return wlk.userdata; +} + +void * +pkt_walk_subpackets (io_stream *packet_file, packet_walk_cb func, HWND owner, + size_t startpos, size_t size_to_walk, void *userdata) +{ + struct packet_walker wlk; + wlk.pfile = packet_file; + wlk.func = func; + wlk.owner = owner; + wlk.userdata = userdata; + wlk.startpos = startpos; + wlk.size_to_walk = size_to_walk; + wlk.is_subpackets = true; + walk_packets_1 (&wlk); + return wlk.userdata; +} + diff --git a/gpg-packet.h b/gpg-packet.h new file mode 100755 index 00000000..8792dd34 --- /dev/null +++ b/gpg-packet.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2008, Dave Korn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + * This module contains support utilities to assist in reading and + * parsing RFC4880-compliant OpenPGP format signature and key files, + * and related constant definitions. + * + * + * Written by Dave Korn + * + */ + +#ifndef SETUP_GPG_PACKET_H +#define SETUP_GPG_PACKET_H + + +/* + +4.3. Packet Tags + + The packet tag denotes what type of packet the body holds. Note that + old format headers can only have tags less than 16, whereas new + format headers can have tags as great as 63. The defined tags (in + decimal) are as follows: + + 0 -- Reserved - a packet tag MUST NOT have this value + 1 -- Public-Key Encrypted Session Key Packet + 2 -- Signature Packet + 3 -- Symmetric-Key Encrypted Session Key Packet + 4 -- One-Pass Signature Packet + 5 -- Secret-Key Packet + 6 -- Public-Key Packet + 7 -- Secret-Subkey Packet + 8 -- Compressed Data Packet + 9 -- Symmetrically Encrypted Data Packet + 10 -- Marker Packet + 11 -- Literal Data Packet + 12 -- Trust Packet + 13 -- User ID Packet + 14 -- Public-Subkey Packet + 17 -- User Attribute Packet + 18 -- Sym. Encrypted and Integrity Protected Data Packet + 19 -- Modification Detection Code Packet + 60 to 63 -- Private or Experimental Values + + +*/ + +#define RFC4880_PT_SIGNATURE 2 +#define RFC4880_PT_PUBLIC_KEY 6 + + +/* + +9.1. Public-Key Algorithms + + ID Algorithm + -- --------- + 1 - RSA (Encrypt or Sign) [HAC] + 2 - RSA Encrypt-Only [HAC] + 3 - RSA Sign-Only [HAC] + 16 - Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + 17 - DSA (Digital Signature Algorithm) [FIPS186] [HAC] + 18 - Reserved for Elliptic Curve + 19 - Reserved for ECDSA + 20 - Reserved (formerly Elgamal Encrypt or Sign) + 21 - Reserved for Diffie-Hellman (X9.42, + as defined for IETF-S/MIME) + 100 to 110 - Private/Experimental algorithm + + Implementations MUST implement DSA for signatures, and Elgamal for + encryption. Implementations SHOULD implement RSA keys (1). RSA + Encrypt-Only (2) and RSA Sign-Only are deprecated and SHOULD NOT be + generated, but may be interpreted. See Section 13.5. See Section + 13.8 for notes on Elliptic Curve (18), ECDSA (19), Elgamal Encrypt or + Sign (20), and X9.42 (21). Implementations MAY implement any other + algorithm. + +*/ + +#define RFC4880_PK_RSA 1 +#define RFC4880_PK_RSA_EO 2 +#define RFC4880_PK_RSA_SO 3 +#define RFC4880_PK_ELGAMAL 16 +#define RFC4880_PK_DSA 17 + + +/* +5.2.1. Signature Types + + There are a number of possible meanings for a signature, which are + indicated in a signature type octet in any given signature. Please + note that the vagueness of these meanings is not a flaw, but a + feature of the system. Because OpenPGP places final authority for + validity upon the receiver of a signature, it may be that one + signer's casual act might be more rigorous than some other + authority's positive act. See Section 5.2.4, "Computing Signatures", + for detailed information on how to compute and verify signatures of + each type. + + These meanings are as follows: + + 0x00: Signature of a binary document. + This means the signer owns it, created it, or certifies that it + has not been modified. + + 0x01: Signature of a canonical text document. + This means the signer owns it, created it, or certifies that it + has not been modified. The signature is calculated over the text + data with its line endings converted to . + + 0x02: Standalone signature. + 0x10: Generic certification of a User ID and Public-Key packet. + 0x11: Persona certification of a User ID and Public-Key packet. + 0x12: Casual certification of a User ID and Public-Key packet. + 0x13: Positive certification of a User ID and Public-Key packet. + 0x18: Subkey Binding Signature + 0x19: Primary Key Binding Signature + 0x1F: Signature directly on a key + 0x20: Key revocation signature + 0x28: Subkey revocation signature + 0x30: Certification revocation signature + 0x40: Timestamp signature. + 0x50: Third-Party Confirmation signature. + +*/ +#define RFC4880_ST_BINARY 0 +#define RFC4880_ST_CANONTEXT 1 + + +/* +9.4. Hash Algorithms + + ID Algorithm Text Name + -- --------- --------- + 1 - MD5 [HAC] "MD5" + 2 - SHA-1 [FIPS180] "SHA1" + 3 - RIPE-MD/160 [HAC] "RIPEMD160" + 4 - Reserved + 5 - Reserved + 6 - Reserved + 7 - Reserved + 8 - SHA256 [FIPS180] "SHA256" + 9 - SHA384 [FIPS180] "SHA384" + 10 - SHA512 [FIPS180] "SHA512" + 11 - SHA224 [FIPS180] "SHA224" + 100 to 110 - Private/Experimental algorithm + + Implementations MUST implement SHA-1. Implementations MAY implement + other algorithms. MD5 is deprecated. +*/ + +#define RFC4880_HC_MD5 1 +#define RFC4880_HC_SHA1 2 +#define RFC4880_HC_RIPEMD160 3 +#define RFC4880_HC_SHA256 8 +#define RFC4880_HC_SHA384 9 +#define RFC4880_HC_SHA512 10 +#define RFC4880_HC_SHA224 11 + + +// This enum is returned by the callback function that is +// invoked by the packet walker for every packet walked; +// it tells it to continue or go home early. +enum pkt_cb_resp +{ + pktCONTINUE, + pktHALT +}; + +// Forward declaration of context data struct. +struct packet_walker; + +// The type of callback function that can be called for every +// packet walked. +typedef enum pkt_cb_resp (*packet_walk_cb) + (struct packet_walker *wlk, unsigned char tag, size_t packetsize, + size_t hdrpos); + +// This struct is used to wrap the context data for a packet walk. +struct packet_walker +{ + io_stream *pfile; + packet_walk_cb func; + HWND owner; + void *userdata; + size_t startpos; + size_t size_to_walk; + bool is_subpackets; +}; + +/* + +3. Data Element Formats + + This section describes the data elements used by OpenPGP. + +3.1. Scalar Numbers + + Scalar numbers are unsigned and are always stored in big-endian + format. Using n[k] to refer to the kth octet being interpreted, the + value of a two-octet scalar is ((n[0] << 8) + n[1]). The value of a + four-octet scalar is ((n[0] << 24) + (n[1] << 16) + (n[2] << 8) + + n[3]). + +*/ + +/* Extract a byte/char from file. Returns EOF if none left. */ +static inline int +pkt_getch (io_stream *file) +{ + unsigned char ch; + if (file->read (&ch, 1) != 1) + return EOF; + return ch; +} + +/* Extract a 16-bit BE int from file. Returns EOF if none left. */ +static inline long +pkt_getword (io_stream *file) +{ + unsigned char ch[2]; + if (file->read (&ch, 2) != 2) + return EOF; + return (ch[0] << 8) | ch[1]; +} + +/* Extract a 32-bit BE int from file. Returns EOF if none left. + Determining the difference between EOF and 0xffffffff is left + as an exercise for the caller - in the contexts where we need + a dword (packet len, timestamp, signer id), we wouldn't expect + to find ~0 anyway and so may safely leave it as a false positive. + Note that this would cause problems with setup.ini files signed + in the last second before the epoch rolls over. Workaround: WDDTT. */ +static inline long +pkt_getdword (io_stream *file) +{ + unsigned char ch[4]; + if (file->read (&ch, 4) != 4) + return EOF; + return (ch[0] << 24) | (ch[1] << 16) | (ch[2] << 8) | ch[3]; +} + +/* Extract an RFC4880 variable-length length field from file. + Returns EOF if none left or negative if invalid format. */ +extern long pkt_getlen (io_stream *file); + +/* Extract an RFC4880 MPI field from file. + Returns EOF if none left or negative if invalid format. */ +extern int pkt_get_mpi (gcry_mpi_t *mpiptr, io_stream *file); + +/* Converts from RFC4880 hash codes (9.4 above) to the hash + algorithm constants used in libgcrypt and the rest of the code. */ +extern char pkt_convert_hashcode (char rfc_hash); + +/* Two functions for walking the (sub)packets found within a + seleected region of an io_stream, calling a hook for each one. */ +extern void *pkt_walk_packets (io_stream *packet_file, packet_walk_cb func, + HWND owner, size_t startpos, size_t size_to_walk, void *userdata); + +extern void *pkt_walk_subpackets (io_stream *packet_file, packet_walk_cb func, + HWND owner, size_t startpos, size_t size_to_walk, void *userdata); + + + +#endif /* SETUP_GPG_PACKET_H */ diff --git a/ini.cc b/ini.cc index 211e06e2..5ba481eb 100644 --- a/ini.cc +++ b/ini.cc @@ -53,14 +53,19 @@ static const char *cvsid = #include "threebar.h" +#include "getopt++/BoolOption.h" #include "IniDBBuilderPackage.h" #include "compress.h" #include "Exception.h" +#include "crypto.h" extern ThreeBarProgressPage Progress; unsigned int setup_timestamp = 0; std::string ini_setup_version; +std::string current_ini_sig_name; + +static BoolOption NoVerifyOption (false, 'X', "no-verify", "Don't verify setup.ini signatures"); extern int yyparse (); /*extern int yydebug;*/ @@ -132,7 +137,7 @@ do_remote_ini (HWND owner) size_t ini_count = 0; GuiParseFeedback myFeedback; IniDBBuilderPackage aBuilder(myFeedback); - io_stream *ini_file; + io_stream *ini_file, *ini_sig_file; /* FIXME: Get rid of this io_stream pointer travesty. The need to explicitly delete these things is ridiculous. Note that the @@ -142,10 +147,30 @@ do_remote_ini (HWND owner) for (SiteList::const_iterator n = site_list.begin(); n != site_list.end(); ++n) { - + bool sig_fail = false; /* First try to fetch the .bz2 compressed ini file. */ current_ini_name = n->url + "/" + SETUP_BZ2_FILENAME; - if ((ini_file = get_url_to_membuf (current_ini_name, owner))) + current_ini_sig_name = n->url + "/" + SETUP_BZ2_FILENAME + ".sig"; + ini_file = get_url_to_membuf (current_ini_name, owner); + if (!NoVerifyOption) + ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner); + if (!NoVerifyOption && ini_file && !ini_sig_file) + { + note (owner, IDS_SETUPINI_MISSING, current_ini_sig_name.c_str(), n->url.c_str()); + delete ini_file; + ini_file = NULL; + sig_fail = true; + } + else if (!NoVerifyOption && ini_file && !verify_ini_file_sig (ini_file, ini_sig_file, owner)) + { + note (owner, IDS_SIG_INVALID, current_ini_sig_name.c_str(), n->url.c_str()); + delete ini_file; + ini_file = NULL; + delete ini_sig_file; + ini_sig_file = NULL; + sig_fail = true; + } + if (ini_file) { /* Decompress the entire file in memory right now. This has the advantage that input_stream->get_size() will work during parsing @@ -195,12 +220,33 @@ do_remote_ini (HWND owner) - the .bz2 file didn't look like a valid bzip2 file. - there was an error during bzip2 decompression. */ current_ini_name = n->url + "/" + SETUP_INI_FILENAME; + current_ini_sig_name = n->url + "/" + SETUP_INI_FILENAME + ".sig"; ini_file = get_url_to_membuf (current_ini_name, owner); + if (!NoVerifyOption) + ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner); + + if (!NoVerifyOption && ini_file && !ini_sig_file) + { + note (owner, IDS_SETUPINI_MISSING, current_ini_sig_name.c_str(), n->url.c_str()); + delete ini_file; + ini_file = NULL; + sig_fail = true; + } + else if (!NoVerifyOption && ini_file && !verify_ini_file_sig (ini_file, ini_sig_file, owner)) + { + note (owner, IDS_SIG_INVALID, current_ini_sig_name.c_str(), n->url.c_str()); + delete ini_file; + ini_file = NULL; + delete ini_sig_file; + ini_sig_file = NULL; + sig_fail = true; + } } if (!ini_file) { - note (owner, IDS_SETUPINI_MISSING, SETUP_INI_FILENAME, n->url.c_str()); + if (!sig_fail) + note (owner, IDS_SETUPINI_MISSING, SETUP_INI_FILENAME, n->url.c_str()); continue; } diff --git a/ini.h b/ini.h index 5b5a80ab..5b2cb6d2 100644 --- a/ini.h +++ b/ini.h @@ -47,6 +47,7 @@ excludes; IniParseFindVisitor::visitFile(). */ extern std::string current_ini_name; /* current filename/URL being parsed */ +extern std::string current_ini_sig_name; /* current filename/URL for sig file */ extern std::string yyerror_messages; /* textual parse error messages */ extern int yyerror_count; /* number of parse errors */ diff --git a/libgetopt++/ChangeLog b/libgetopt++/ChangeLog index 73935c13..14e4554c 100644 --- a/libgetopt++/ChangeLog +++ b/libgetopt++/ChangeLog @@ -1,3 +1,9 @@ +2008-06-16 Dave Korn + + * libgetopt++/src/OptionSet.cc (OptionSet::doOption): Move value + string to outer scope so it is not destroyed while optionValue + still has a dangling reference to its c_str(). + 2008-04-08 Brian Dessent * src/OptionSet.cc: Include algorithm. diff --git a/libgetopt++/src/OptionSet.cc b/libgetopt++/src/OptionSet.cc index fff13934..bc3f018b 100644 --- a/libgetopt++/src/OptionSet.cc +++ b/libgetopt++/src/OptionSet.cc @@ -102,6 +102,7 @@ OptionSet::doOption(string &option, string::size_type const &pos) option.erase(0, pos); Option *theOption = findOption(option, pos); char const *optionValue = NULL; + string value; if (theOption == NULL) return; @@ -114,8 +115,6 @@ OptionSet::doOption(string &option, string::size_type const &pos) break; case Option::Optional: { - string value; - if (pos == 1) { if (option.size() == 1) { /* Value in next argv */ @@ -172,8 +171,6 @@ OptionSet::doOption(string &option, string::size_type const &pos) break; case Option::Required: { - string value; - if (pos == 1) { if (option.size() == 1) { /* Value in next argv */ diff --git a/netio.cc b/netio.cc index 37c3a4ed..e7872418 100644 --- a/netio.cc +++ b/netio.cc @@ -84,7 +84,7 @@ NetIO::set_url (char const *Purl) ep = strstr (bp, "://"); if (!ep) { - path = url; + path = strdup (url); return; } diff --git a/res.rc b/res.rc index b5997ca3..f0a85e7a 100644 --- a/res.rc +++ b/res.rc @@ -484,4 +484,6 @@ BEGIN "with names that begin with '_'. Such packages are usually empty " "placeholders for packages that have been removed or renamed, or are " "infrastructure packages that are handled automatically." + IDS_SIG_INVALID "Mirror Error: Setup.ini signature %s from %s failed to verify.\nPossible corrupt mirror? Setup.ini rejected." + IDS_CRYPTO_ERROR "Internal Error: gcrypt library error %d %s" END diff --git a/resource.h b/resource.h index 3aa0576c..e8cc95d5 100644 --- a/resource.h +++ b/resource.h @@ -31,6 +31,8 @@ #define IDS_TRUSTEXP_TOOLTIP 128 #define IDS_VIEWBUTTON_TOOLTIP 129 #define IDS_HIDEOBS_TOOLTIP 130 +#define IDS_SIG_INVALID 131 +#define IDS_CRYPTO_ERROR 132 // Dialogs -- 2.43.5