summaryrefslogtreecommitdiffstats
path: root/kbx
diff options
context:
space:
mode:
authorRepo Admin <nobody@gnupg.org>2003-08-05 19:11:04 +0200
committerRepo Admin <nobody@gnupg.org>2003-08-05 19:11:04 +0200
commit9ca4830a5b8392bc7bb01211407c876fd40b49d4 (patch)
tree5b5eb60b48cea2f86d01b8d9a458a2fa4fc0a2b2 /kbx
parentminor changes to make make distcheck happy (diff)
downloadgnupg2-9ca4830a5b8392bc7bb01211407c876fd40b49d4.tar.xz
gnupg2-9ca4830a5b8392bc7bb01211407c876fd40b49d4.zip
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
Diffstat (limited to 'kbx')
-rw-r--r--kbx/Makefile.am52
-rw-r--r--kbx/kbxutil.c339
-rw-r--r--kbx/keybox-blob.c1008
3 files changed, 1399 insertions, 0 deletions
diff --git a/kbx/Makefile.am b/kbx/Makefile.am
new file mode 100644
index 000000000..4f0c40043
--- /dev/null
+++ b/kbx/Makefile.am
@@ -0,0 +1,52 @@
+# Keybox Makefile
+# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG 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.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+## Process this file with automake to produce Makefile.in
+
+localedir = $(datadir)/locale
+INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
+
+EXTRA_DIST = mkerrors
+AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/intl \
+ $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS)
+
+noinst_LIBRARIES = libkeybox.a
+bin_PROGRAMS = kbxutil
+
+common_sources = \
+ keybox.h keybox-defs.h keybox-search-desc.h \
+ keybox-util.c \
+ keybox-init.c \
+ keybox-blob.c \
+ keybox-file.c \
+ keybox-search.c \
+ keybox-update.c \
+ keybox-dump.c
+
+
+libkeybox_a_SOURCES = $(common_sources)
+
+kbxutil_SOURCES = kbxutil.c $(common_sources)
+kbxutil_LDADD = ../jnlib/libjnlib.a $(KSBA_LIBS) $(LIBGCRYPT_LIBS) \
+ -lgpg-error @INTLLIBS@
+
+
+
+
+
diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c
new file mode 100644
index 000000000..abca4faa9
--- /dev/null
+++ b/kbx/kbxutil.c
@@ -0,0 +1,339 @@
+/* kbxutil.c - The Keybox utility
+ * Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "../jnlib/logging.h"
+#include "../jnlib/argparse.h"
+#include "../jnlib/stringhelp.h"
+#include "../common/i18n.h"
+#include "keybox-defs.h"
+
+#include <gcrypt.h>
+
+
+enum cmd_and_opt_values {
+ aNull = 0,
+ oArmor = 'a',
+ oDryRun = 'n',
+ oOutput = 'o',
+ oQuiet = 'q',
+ oVerbose = 'v',
+
+ aNoSuchCmd = 500, /* force other values not to be a letter */
+ aFindByFpr,
+ aFindByKid,
+ aFindByUid,
+
+ oDebug,
+ oDebugAll,
+
+ oNoArmor,
+
+
+ aTest
+};
+
+
+static ARGPARSE_OPTS opts[] = {
+ { 300, NULL, 0, N_("@Commands:\n ") },
+
+ { aFindByFpr, "find-by-fpr", 0, "|FPR| find key using it's fingerprnt" },
+ { aFindByKid, "find-by-kid", 0, "|KID| find key using it's keyid" },
+ { aFindByUid, "find-by-uid", 0, "|NAME| find key by user name" },
+
+ { 301, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oArmor, "armor", 0, N_("create ascii armored output")},
+ { oArmor, "armour", 0, "@" },
+ { oOutput, "output", 2, N_("use as output file")},
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
+ { oDryRun, "dry-run", 0, N_("do not make any changes") },
+
+ { oDebug, "debug" ,4|16, N_("set debugging flags")},
+ { oDebugAll, "debug-all" ,0, N_("enable full debugging")},
+
+ {0} /* end of list */
+};
+
+
+void myexit (int rc);
+
+int keybox_errors_seen = 0;
+
+
+static const char *
+my_strusage( int level )
+{
+ const char *p;
+ switch( level ) {
+ case 11: p = "kbxutil (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p =
+ _("Please report bugs to " PACKAGE_BUGREPORT ".\n");
+ break;
+ case 1:
+ case 40: p =
+ _("Usage: kbxutil [options] [files] (-h for help)");
+ break;
+ case 41: p =
+ _("Syntax: kbxutil [options] [files]\n"
+ "list, export, import Keybox data\n");
+ break;
+
+
+ default: p = NULL;
+ }
+ return p;
+}
+
+
+static void
+i18n_init(void)
+{
+ #ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file( PACKAGE );
+ #else
+ #ifdef ENABLE_NLS
+ #ifdef HAVE_LC_MESSAGES
+ setlocale( LC_TIME, "" );
+ setlocale( LC_MESSAGES, "" );
+ #else
+ setlocale( LC_ALL, "" );
+ #endif
+ bindtextdomain( PACKAGE, LOCALEDIR );
+ textdomain( PACKAGE );
+ #endif
+ #endif
+}
+
+
+/* static void */
+/* wrong_args( const char *text ) */
+/* { */
+/* log_error("usage: kbxutil %s\n", text); */
+/* myexit ( 1 ); */
+/* } */
+
+
+#if 0
+static int
+hextobyte( const byte *s )
+{
+ int c;
+
+ if( *s >= '0' && *s <= '9' )
+ c = 16 * (*s - '0');
+ else if( *s >= 'A' && *s <= 'F' )
+ c = 16 * (10 + *s - 'A');
+ else if( *s >= 'a' && *s <= 'f' )
+ c = 16 * (10 + *s - 'a');
+ else
+ return -1;
+ s++;
+ if( *s >= '0' && *s <= '9' )
+ c += *s - '0';
+ else if( *s >= 'A' && *s <= 'F' )
+ c += 10 + *s - 'A';
+ else if( *s >= 'a' && *s <= 'f' )
+ c += 10 + *s - 'a';
+ else
+ return -1;
+ return c;
+}
+#endif
+
+#if 0
+static char *
+format_fingerprint ( const char *s )
+{
+ int i, c;
+ byte fpr[20];
+
+ for (i=0; i < 20 && *s; ) {
+ if ( *s == ' ' || *s == '\t' ) {
+ s++;
+ continue;
+ }
+ c = hextobyte(s);
+ if (c == -1) {
+ return NULL;
+ }
+ fpr[i++] = c;
+ s += 2;
+ }
+ return gcry_xstrdup ( fpr );
+}
+#endif
+
+#if 0
+static int
+format_keyid ( const char *s, u32 *kid )
+{
+ char helpbuf[9];
+ switch ( strlen ( s ) ) {
+ case 8:
+ kid[0] = 0;
+ kid[1] = strtoul( s, NULL, 16 );
+ return 10;
+
+ case 16:
+ mem2str( helpbuf, s, 9 );
+ kid[0] = strtoul( helpbuf, NULL, 16 );
+ kid[1] = strtoul( s+8, NULL, 16 );
+ return 11;
+ }
+ return 0; /* error */
+}
+#endif
+
+
+int
+main( int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ enum cmd_and_opt_values cmd = 0;
+
+ set_strusage( my_strusage );
+ /*log_set_name("kbxutil"); fixme */
+#if 0
+ /* check that the libraries are suitable. Do it here because
+ * the option parse may need services of the library */
+ if ( !gcry_check_version ( "1.1.4" ) )
+ {
+ log_fatal(_("libgcrypt is too old (need %s, have %s)\n"),
+ "1.1.4", gcry_check_version(NULL) );
+ }
+#endif
+
+ /*create_dotlock(NULL); register locking cleanup */
+ i18n_init();
+
+ /* We need to use the gcry malloc function because jnlib does use them */
+ keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
+
+
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+ while (arg_parse( &pargs, opts) )
+ {
+ switch (pargs.r_opt)
+ {
+ case oVerbose:
+ /*opt.verbose++;*/
+ /*gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose );*/
+ break;
+ case oDebug:
+ /*opt.debug |= pargs.r.ret_ulong; */
+ break;
+ case oDebugAll:
+ /*opt.debug = ~0;*/
+ break;
+
+ case aFindByFpr:
+ case aFindByKid:
+ case aFindByUid:
+ cmd = pargs.r_opt;
+ break;
+
+ default:
+ pargs.err = 2;
+ break;
+ }
+ }
+ if (log_get_errorcount(0) )
+ myexit(2);
+
+ if (!cmd)
+ { /* default is to list a KBX file */
+ if (!argc)
+ _keybox_dump_file (NULL, stdout);
+ else
+ {
+ for (; argc; argc--, argv++)
+ _keybox_dump_file (*argv, stdout);
+ }
+ }
+#if 0
+ else if ( cmd == aFindByFpr ) {
+ char *fpr;
+ if ( argc != 2 )
+ wrong_args ("kbxfile foingerprint");
+ fpr = format_fingerprint ( argv[1] );
+ if ( !fpr )
+ log_error ("invalid formatted fingerprint\n");
+ else {
+ kbxfile_search_by_fpr ( argv[0], fpr );
+ gcry_free ( fpr );
+ }
+ }
+ else if ( cmd == aFindByKid ) {
+ u32 kid[2];
+ int mode;
+
+ if ( argc != 2 )
+ wrong_args ("kbxfile short-or-long-keyid");
+ mode = format_keyid ( argv[1], kid );
+ if ( !mode )
+ log_error ("invalid formatted keyID\n");
+ else {
+ kbxfile_search_by_kid ( argv[0], kid, mode );
+ }
+ }
+ else if ( cmd == aFindByUid ) {
+ if ( argc != 2 )
+ wrong_args ("kbxfile userID");
+ kbxfile_search_by_uid ( argv[0], argv[1] );
+ }
+#endif
+ else
+ log_error ("unsupported action\n");
+
+ myexit(0);
+ return 8; /*NEVER REACHED*/
+}
+
+
+void
+myexit( int rc )
+{
+ /* if( opt.debug & DBG_MEMSTAT_VALUE ) {*/
+/* gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); */
+/* gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); */
+ /* }*/
+/* if( opt.debug ) */
+/* gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); */
+ rc = rc? rc : log_get_errorcount(0)? 2 :
+ keybox_errors_seen? 1 : 0;
+ exit(rc );
+}
+
+
diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c
new file mode 100644
index 000000000..5ad1d2610
--- /dev/null
+++ b/kbx/keybox-blob.c
@@ -0,0 +1,1008 @@
+/* keybox-blob.c - KBX Blob handling
+ * Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+/* The keybox data formats
+
+The KeyBox uses an augmented OpenPGP/X.509 key format. This makes
+random access to a keyblock/Certificate easier and also gives the
+opportunity to store additional information (e.g. the fingerprint)
+along with the key. All integers are stored in network byte order,
+offsets are counted from the beginning of the Blob.
+
+The first record of a plain KBX file has a special format:
+
+ u32 length of the first record
+ byte Blob type (1)
+ byte version number (1)
+ byte reserved
+ byte reserved
+ u32 magic 'KBXf'
+ byte pgp_marginals used for validity calculation of this file
+ byte pgp_completes ditto.
+ byte pgp_cert_depth ditto.
+
+The OpenPGP and X.509 blob are verry similiar, things which are
+X.509 specific are noted like [X.509: xxx]
+
+ u32 length of this blob (including these 4 bytes)
+ byte Blob type (2) [X509: 3]
+ byte version number of this blob type (1)
+ u16 Blob flags
+ bit 0 = contains secret key material
+ bit 1 = ephemeral blob (e.g. used while quering external resources)
+
+ u32 offset to the OpenPGP keyblock or X509 DER encoded certificate
+ u32 and its length
+ u16 number of keys (at least 1!) [X509: always 1]
+ u16 size of additional key information
+ n times:
+ b20 The keys fingerprint
+ (fingerprints are always 20 bytes, MD5 left padded with zeroes)
+ u32 offset to the n-th key's keyID (a keyID is always 8 byte)
+ or 0 if not known which is the case opnly for X509.
+ u16 special key flags
+ bit 0 =
+ u16 reserved
+ u16 size of serialnumber(may be zero)
+ n u16 (see above) bytes of serial number
+ u16 number of user IDs
+ u16 size of additional user ID information
+ n times:
+ u32 offset to the n-th user ID
+ u32 length of this user ID.
+ u16 special user ID flags.
+ bit 0 =
+ byte validity
+ byte reserved
+ [For X509, the first user ID is the ISsuer, the second the subject
+ and the others are subjectAltNames]
+ u16 number of signatures
+ u16 size of signature information (4)
+ u32 expiration time of signature with some special values:
+ 0x00000000 = not checked
+ 0x00000001 = missing key
+ 0x00000002 = bad signature
+ 0x10000000 = valid and expires at some date in 1978.
+ 0xffffffff = valid and does not expire
+ u8 assigned ownertrust [X509: no used]
+ u8 all_validity [X509: no used]
+ u16 reserved
+ u32 recheck_after
+ u32 Newest timestamp in the keyblock (useful for KS syncronsiation?)
+ u32 Blob created at
+ u32 size of reserved space (not including this field)
+ reserved space
+
+ Here we might want to put other data
+
+ Here comes the keyblock
+
+ maybe we put a signature here later.
+
+ b16 MD5 checksum (useful for KS syncronisation), we might also want to use
+ a mac here.
+ b4 resevered
+
+*/
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "keybox-defs.h"
+#include <gcrypt.h>
+
+#ifdef KEYBOX_WITH_OPENPGP
+/* include stuff to parse the packets */
+#endif
+#ifdef KEYBOX_WITH_X509
+#include <ksba.h>
+#endif
+
+
+
+/* special values of the signature status */
+#define SF_NONE(a) ( !(a) )
+#define SF_NOKEY(a) ((a) & (1<<0))
+#define SF_BAD(a) ((a) & (1<<1))
+#define SF_VALID(a) ((a) & (1<<29))
+
+
+struct membuf {
+ size_t len;
+ size_t size;
+ char *buf;
+ int out_of_core;
+};
+
+
+/* #if MAX_FINGERPRINT_LEN < 20 */
+/* #error fingerprints are 20 bytes */
+/* #endif */
+
+struct keyboxblob_key {
+ char fpr[20];
+ u32 off_kid;
+ ulong off_kid_addr;
+ u16 flags;
+};
+struct keyboxblob_uid {
+ ulong off_addr;
+ char *name; /* used only with x509 */
+ u32 len;
+ u16 flags;
+ byte validity;
+};
+
+struct keyid_list {
+ struct keyid_list *next;
+ int seqno;
+ byte kid[8];
+};
+
+struct fixup_list {
+ struct fixup_list *next;
+ u32 off;
+ u32 val;
+};
+
+
+struct keyboxblob {
+ byte *blob;
+ size_t bloblen;
+ off_t fileoffset;
+
+ /* stuff used only by keybox_create_blob */
+ unsigned char *serialbuf;
+ const unsigned char *serial;
+ size_t seriallen;
+ int nkeys;
+ struct keyboxblob_key *keys;
+ int nuids;
+ struct keyboxblob_uid *uids;
+ int nsigs;
+ u32 *sigs;
+ struct fixup_list *fixups;
+ int fixup_out_of_core;
+
+ struct keyid_list *temp_kids;
+ struct membuf bufbuf; /* temporary store for the blob */
+ struct membuf *buf;
+};
+
+
+
+/* A simple implemnation of a dynamic buffer. Use init_membuf() to
+ create a buffer, put_membuf to append bytes and get_membuf to
+ release and return the buffer. Allocation errors are detected but
+ only returned at the final get_membuf(), this helps not to clutter
+ the code with out of core checks. */
+
+static void
+init_membuf (struct membuf *mb, int initiallen)
+{
+ mb->len = 0;
+ mb->size = initiallen;
+ mb->out_of_core = 0;
+ mb->buf = xtrymalloc (initiallen);
+ if (!mb->buf)
+ mb->out_of_core = 1;
+}
+
+static void
+put_membuf (struct membuf *mb, const void *buf, size_t len)
+{
+ if (mb->out_of_core)
+ return;
+
+ if (mb->len + len >= mb->size)
+ {
+ char *p;
+
+ mb->size += len + 1024;
+ p = xtryrealloc (mb->buf, mb->size);
+ if (!p)
+ {
+ mb->out_of_core = 1;
+ return;
+ }
+ mb->buf = p;
+ }
+ memcpy (mb->buf + mb->len, buf, len);
+ mb->len += len;
+}
+
+static void *
+get_membuf (struct membuf *mb, size_t *len)
+{
+ char *p;
+
+ if (mb->out_of_core)
+ {
+ xfree (mb->buf);
+ mb->buf = NULL;
+ return NULL;
+ }
+
+ p = mb->buf;
+ *len = mb->len;
+ mb->buf = NULL;
+ mb->out_of_core = 1; /* don't allow a reuse */
+ return p;
+}
+
+
+static void
+put8 (struct membuf *mb, byte a )
+{
+ put_membuf (mb, &a, 1);
+}
+
+static void
+put16 (struct membuf *mb, u16 a )
+{
+ unsigned char tmp[2];
+ tmp[0] = a>>8;
+ tmp[1] = a;
+ put_membuf (mb, tmp, 2);
+}
+
+static void
+put32 (struct membuf *mb, u32 a )
+{
+ unsigned char tmp[4];
+ tmp[0] = a>>24;
+ tmp[1] = a>>16;
+ tmp[2] = a>>8;
+ tmp[3] = a;
+ put_membuf (mb, tmp, 4);
+}
+
+
+/* Store a value in the fixup list */
+static void
+add_fixup (KEYBOXBLOB blob, u32 off, u32 val)
+{
+ struct fixup_list *fl;
+
+ if (blob->fixup_out_of_core)
+ return;
+
+ fl = xtrycalloc(1, sizeof *fl);
+ if (!fl)
+ blob->fixup_out_of_core = 1;
+ else
+ {
+ fl->off = off;
+ fl->val = val;
+ fl->next = blob->fixups;
+ blob->fixups = fl;
+ }
+}
+
+
+/*
+ Some wrappers
+*/
+
+static u32
+make_timestamp (void)
+{
+ return time(NULL);
+}
+
+
+
+#ifdef KEYBOX_WITH_OPENPGP
+/*
+ OpenPGP specific stuff
+*/
+
+
+/*
+ We must store the keyid at some place because we can't calculate the
+ offset yet. This is only used for v3 keyIDs. Function returns an
+ index value for later fixup or -1 for out of core. The value must be
+ a non-zero value */
+static int
+pgp_temp_store_kid (KEYBOXBLOB blob, PKT_public_key *pk)
+{
+ struct keyid_list *k, *r;
+
+ k = xtrymalloc (sizeof *k);
+ if (!k)
+ return -1;
+ k->kid[0] = pk->keyid[0] >> 24 ;
+ k->kid[1] = pk->keyid[0] >> 16 ;
+ k->kid[2] = pk->keyid[0] >> 8 ;
+ k->kid[3] = pk->keyid[0] ;
+ k->kid[4] = pk->keyid[0] >> 24 ;
+ k->kid[5] = pk->keyid[0] >> 16 ;
+ k->kid[6] = pk->keyid[0] >> 8 ;
+ k->kid[7] = pk->keyid[0] ;
+ k->seqno = 0;
+ k->next = blob->temp_kids;
+ blob->temp_kids = k;
+ for (r=k; r; r = r->next)
+ k->seqno++;
+
+ return k->seqno;
+}
+
+static int
+pgp_create_key_part (KEYBOXBLOB blob, KBNODE keyblock)
+{
+ KBNODE node;
+ size_t fprlen;
+ int n;
+
+ for (n=0, node = keyblock; node; node = node->next)
+ {
+ if ( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+ char tmp[20];
+
+ fingerprint_from_pk (pk, tmp , &fprlen);
+ memcpy (blob->keys[n].fpr, tmp, 20);
+ if ( fprlen != 20 ) /*v3 fpr - shift right and fill with zeroes*/
+ {
+ assert (fprlen == 16);
+ memmove (blob->keys[n].fpr+4, blob->keys[n].fpr, 16);
+ memset (blob->keys[n].fpr, 0, 4);
+ blob->keys[n].off_kid = pgp_temp_store_kid (blob, pk);
+ }
+ else
+ {
+ blob->keys[n].off_kid = 0; /* will be fixed up later */
+ }
+ blob->keys[n].flags = 0;
+ n++;
+ }
+ else if ( node->pkt->pkttype == PKT_SECRET_KEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY )
+ {
+ never_reached (); /* actually not yet implemented */
+ }
+ }
+ assert (n == blob->nkeys);
+ return 0;
+}
+
+static int
+pgp_create_uid_part (KEYBOXBLOB blob, KBNODE keyblock)
+{
+ KBNODE node;
+ int n;
+
+ for (n=0, node = keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID)
+ {
+ PKT_user_id *u = node->pkt->pkt.user_id;
+
+ blob->uids[n].len = u->len;
+ blob->uids[n].flags = 0;
+ blob->uids[n].validity = 0;
+ n++;
+ }
+ }
+ assert (n == blob->nuids);
+ return 0;
+}
+
+static int
+pgp_create_sig_part (KEYBOXBLOB blob, KBNODE keyblock)
+{
+ KBNODE node;
+ int n;
+
+ for (n=0, node = keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_SIGNATURE)
+ {
+ PKT_signature *sig = node->pkt->pkt.signature;
+
+ blob->sigs[n] = 0; /* FIXME: check the signature here */
+ n++;
+ }
+ }
+ assert( n == blob->nsigs );
+ return 0;
+}
+
+static int
+pgp_create_blob_keyblock (KEYBOXBLOB blob, KBNODE keyblock)
+{
+ struct membuf *a = blob->buf;
+ KBNODE node;
+ int rc;
+ int n;
+ u32 kbstart = a->len;
+
+ add_fixup (blob, kbstart);
+
+ for (n = 0, node = keyblock; node; node = node->next)
+ {
+ rc = build_packet ( a, node->pkt );
+ if ( rc ) {
+ gpg_log_error ("build_packet(%d) for keyboxblob failed: %s\n",
+ node->pkt->pkttype, gpg_errstr(rc) );
+ return GPGERR_WRITE_FILE;
+ }
+ if ( node->pkt->pkttype == PKT_USER_ID )
+ {
+ PKT_user_id *u = node->pkt->pkt.user_id;
+ /* build_packet has set the offset of the name into u ;
+ * now we can do the fixup */
+ add_fixup (blob, blob->uids[n].off_addr, u->stored_at);
+ n++;
+ }
+ }
+ assert (n == blob->nuids);
+
+ add_fixup (blob, a->len - kbstart);
+ return 0;
+}
+
+#endif /*KEYBOX_WITH_OPENPGP*/
+
+
+#ifdef KEYBOX_WITH_X509
+/*
+ X.509 specific stuff
+ */
+
+/* Write the raw certificate out */
+static int
+x509_create_blob_cert (KEYBOXBLOB blob, KsbaCert cert)
+{
+ struct membuf *a = blob->buf;
+ const unsigned char *image;
+ size_t length;
+ u32 kbstart = a->len;
+
+ /* Store our offset for later fixup */
+ add_fixup (blob, 8, kbstart);
+
+ image = ksba_cert_get_image (cert, &length);
+ if (!image)
+ return gpg_error (GPG_ERR_GENERAL);
+ put_membuf (a, image, length);
+
+ add_fixup (blob, 12, a->len - kbstart);
+ return 0;
+}
+
+#endif /*KEYBOX_WITH_X509*/
+
+/* Write a stored keyID out to the buffer */
+static void
+write_stored_kid (KEYBOXBLOB blob, int seqno)
+{
+ struct keyid_list *r;
+
+ for ( r = blob->temp_kids; r; r = r->next )
+ {
+ if (r->seqno == seqno )
+ {
+ put_membuf (blob->buf, r->kid, 8);
+ return;
+ }
+ }
+ never_reached ();
+}
+
+/* Release a list of key IDs */
+static void
+release_kid_list (struct keyid_list *kl)
+{
+ struct keyid_list *r, *r2;
+
+ for ( r = kl; r; r = r2 )
+ {
+ r2 = r->next;
+ xfree (r);
+ }
+}
+
+
+
+static int
+create_blob_header (KEYBOXBLOB blob, int blobtype, int as_ephemeral)
+{
+ struct membuf *a = blob->buf;
+ int i;
+
+ put32 ( a, 0 ); /* blob length, needs fixup */
+ put8 ( a, blobtype);
+ put8 ( a, 1 ); /* blob type version */
+ put16 ( a, as_ephemeral? 2:0 ); /* blob flags */
+
+ put32 ( a, 0 ); /* offset to the raw data, needs fixup */
+ put32 ( a, 0 ); /* length of the raw data, needs fixup */
+
+ put16 ( a, blob->nkeys );
+ put16 ( a, 20 + 4 + 2 + 2 ); /* size of key info */
+ for ( i=0; i < blob->nkeys; i++ )
+ {
+ put_membuf (a, blob->keys[i].fpr, 20);
+ blob->keys[i].off_kid_addr = a->len;
+ put32 ( a, 0 ); /* offset to keyid, fixed up later */
+ put16 ( a, blob->keys[i].flags );
+ put16 ( a, 0 ); /* reserved */
+ }
+
+ put16 (a, blob->seriallen); /*fixme: check that it fits into 16 bits*/
+ if (blob->serial)
+ put_membuf (a, blob->serial, blob->seriallen);
+
+ put16 ( a, blob->nuids );
+ put16 ( a, 4 + 4 + 2 + 1 + 1 ); /* size of uid info */
+ for (i=0; i < blob->nuids; i++)
+ {
+ blob->uids[i].off_addr = a->len;
+ put32 ( a, 0 ); /* offset to userid, fixed up later */
+ put32 ( a, blob->uids[i].len );
+ put16 ( a, blob->uids[i].flags );
+ put8 ( a, 0 ); /* validity */
+ put8 ( a, 0 ); /* reserved */
+ }
+
+ put16 ( a, blob->nsigs );
+ put16 ( a, 4 ); /* size of sig info */
+ for (i=0; i < blob->nsigs; i++)
+ {
+ put32 ( a, blob->sigs[i]);
+ }
+
+ put8 ( a, 0 ); /* assigned ownertrust */
+ put8 ( a, 0 ); /* validity of all user IDs */
+ put16 ( a, 0 ); /* reserved */
+ put32 ( a, 0 ); /* time of next recheck */
+ put32 ( a, 0 ); /* newest timestamp (none) */
+ put32 ( a, make_timestamp() ); /* creation time */
+ put32 ( a, 0 ); /* size of reserved space */
+ /* reserved space (which is currently of size 0) */
+
+ /* space where we write keyIDs and and other stuff so that the
+ pointers can actually point to somewhere */
+ if (blobtype == BLOBTYPE_PGP)
+ {
+ /* We need to store the keyids for all pgp v3 keys because those key
+ IDs are not part of the fingerprint. While we are doing that, we
+ fixup all the keyID offsets */
+ for (i=0; i < blob->nkeys; i++ )
+ {
+ if (blob->keys[i].off_kid)
+ { /* this is a v3 one */
+ add_fixup (blob, blob->keys[i].off_kid_addr, a->len);
+ write_stored_kid (blob, blob->keys[i].off_kid);
+ }
+ else
+ { /* the better v4 key IDs - just store an offset 8 bytes back */
+ add_fixup (blob, blob->keys[i].off_kid_addr,
+ blob->keys[i].off_kid_addr - 8);
+ }
+ }
+ }
+
+ if (blobtype == BLOBTYPE_X509)
+ {
+ /* We don't want to point to ASN.1 encoded UserIDs (DNs) but to
+ the utf-8 string represenation of them */
+ for (i=0; i < blob->nuids; i++ )
+ {
+ if (blob->uids[i].name)
+ { /* this is a v3 one */
+ add_fixup (blob, blob->uids[i].off_addr, a->len);
+ put_membuf (blob->buf, blob->uids[i].name, blob->uids[i].len);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+static int
+create_blob_trailer (KEYBOXBLOB blob)
+{
+ return 0;
+}
+
+
+static int
+create_blob_finish (KEYBOXBLOB blob)
+{
+ struct membuf *a = blob->buf;
+ byte *p;
+ char *pp;
+ int i;
+ size_t n;
+
+ /* write a placeholder for the checksum */
+ for (i = 0; i < 16; i++ )
+ put32 (a, 0); /* Hmmm: why put32() ?? */
+
+ /* get the memory area */
+ p = get_membuf (a, &n);
+ if (!p)
+ return gpg_error (GPG_ERR_ENOMEM);
+ assert (n >= 20);
+
+ /* fixup the length */
+ add_fixup (blob, 0, n);
+
+ /* do the fixups */
+ if (blob->fixup_out_of_core)
+ return gpg_error (GPG_ERR_ENOMEM);
+
+ {
+ struct fixup_list *fl;
+ for (fl = blob->fixups; fl; fl = fl->next)
+ {
+ assert (fl->off+4 <= n);
+ p[fl->off+0] = fl->val >> 24;
+ p[fl->off+1] = fl->val >> 16;
+ p[fl->off+2] = fl->val >> 8;
+ p[fl->off+3] = fl->val;
+ }
+ }
+
+ /* calculate and store the MD5 checksum */
+ gcry_md_hash_buffer (GCRY_MD_MD5, p + n - 16, p, n - 16);
+
+ pp = xtrymalloc (n);
+ if ( !pp )
+ return gpg_error (gpg_err_code_from_errno (errno));
+ memcpy (pp , p, n);
+ blob->blob = pp;
+ blob->bloblen = n;
+
+ return 0;
+}
+
+
+#ifdef KEYBOX_WITH_OPENPGP
+
+int
+_keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock, int as_ephemeral)
+{
+ int rc = 0;
+ KBNODE node;
+ KEYBOXBLOB blob;
+
+ *r_blob = NULL;
+ blob = xtrycalloc (1, sizeof *blob);
+ if (!blob)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ /* fixme: Do some sanity checks on the keyblock */
+
+ /* count userids and keys so that we can allocate the arrays */
+ for (node = keyblock; node; node = node->next)
+ {
+ switch (node->pkt->pkttype)
+ {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_SECRET_SUBKEY: blob->nkeys++; break;
+ case PKT_USER_ID: blob->nuids++; break;
+ case PKT_SIGNATURE: blob->nsigs++; break;
+ default: break;
+ }
+ }
+
+ blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys );
+ blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids );
+ blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs );
+ if (!blob->keys || !blob->uids || !blob->sigs)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ rc = pgp_create_key_part ( blob, keyblock );
+ if (rc)
+ goto leave;
+ rc = pgp_create_uid_part ( blob, keyblock );
+ if (rc)
+ goto leave;
+ rc = pgp_create_sig_part ( blob, keyblock );
+ if (rc)
+ goto leave;
+
+ init_membuf (&blob->bufbuf, 1024);
+ blob->buf = &blob->bufbuf;
+ rc = create_blob_header (blob, BLOBTYPE_OPENPGP, as_ephemeral);
+ if (rc)
+ goto leave;
+ rc = pgp_create_blob_keyblock (blob, keyblock);
+ if (rc)
+ goto leave;
+ rc = create_blob_trailer (blob);
+ if (rc)
+ goto leave;
+ rc = create_blob_finish ( blob );
+ if (rc)
+ goto leave;
+
+
+ leave:
+ release_kid_list (blob->temp_kids);
+ blob->temp_kids = NULL;
+ if (rc)
+ {
+ keybox_release_blob (blob);
+ *r_blob = NULL;
+ }
+ else
+ {
+ *r_blob = blob;
+ }
+ return rc;
+}
+#endif /*KEYBOX_WITH_OPENPGP*/
+
+#ifdef KEYBOX_WITH_X509
+
+/* return an allocated string with the email address extracted from a
+ DN */
+static char *
+x509_email_kludge (const char *name)
+{
+ const unsigned char *p;
+ unsigned char *buf;
+ int n;
+
+ if (strncmp (name, "1.2.840.113549.1.9.1=#", 22))
+ return NULL;
+ /* This looks pretty much like an email address in the subject's DN
+ we use this to add an additional user ID entry. This way,
+ openSSL generated keys get a nicer and usable listing */
+ name += 22;
+ for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
+ ;
+ if (*p != '#' || !n)
+ return NULL;
+ buf = xtrymalloc (n+3);
+ if (!buf)
+ return NULL; /* oops, out of core */
+ *buf = '<';
+ for (n=1, p=name; *p != '#'; p +=2, n++)
+ buf[n] = xtoi_2 (p);
+ buf[n++] = '>';
+ buf[n] = 0;
+ return buf;
+}
+
+
+
+/* Note: We should move calculation of the digest into libksba and
+ remove that parameter */
+int
+_keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
+ unsigned char *sha1_digest, int as_ephemeral)
+{
+ int i, rc = 0;
+ KEYBOXBLOB blob;
+ unsigned char *p;
+ unsigned char **names = NULL;
+ size_t max_names;
+
+ *r_blob = NULL;
+ blob = xtrycalloc (1, sizeof *blob);
+ if( !blob )
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ p = ksba_cert_get_serial (cert);
+ if (p)
+ {
+ size_t n, len;
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (n < 2)
+ {
+ xfree (p);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ blob->serialbuf = p;
+ p++; n--; /* skip '(' */
+ for (len=0; n && *p && *p != ':' && digitp (p); n--, p++)
+ len = len*10 + atoi_1 (p);
+ if (*p != ':')
+ {
+ xfree (blob->serialbuf);
+ blob->serialbuf = NULL;
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ p++;
+ blob->serial = p;
+ blob->seriallen = len;
+ }
+
+ blob->nkeys = 1;
+
+ /* create list of names */
+ blob->nuids = 0;
+ max_names = 100;
+ names = xtrymalloc (max_names * sizeof *names);
+ if (!names)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ goto leave;
+ }
+ p = ksba_cert_get_issuer (cert, 0);
+ if (!p)
+ {
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+ names[blob->nuids++] = p;
+ for (i=0; (p = ksba_cert_get_subject (cert, i)); i++)
+ {
+
+ if (blob->nuids >= max_names)
+ {
+ unsigned char **tmp;
+
+ max_names += 100;
+ tmp = xtryrealloc (names, max_names * sizeof *names);
+ if (!tmp)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ goto leave;
+ }
+ }
+ names[blob->nuids++] = p;
+ if (!i && (p=x509_email_kludge (p)))
+ names[blob->nuids++] = p; /* due to !i we don't need to check bounds*/
+ }
+
+ /* space for signature information */
+ blob->nsigs = 1;
+
+ blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys );
+ blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids );
+ blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs );
+ if (!blob->keys || !blob->uids || !blob->sigs)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ memcpy (blob->keys[0].fpr, sha1_digest, 20);
+ blob->keys[0].off_kid = 0; /* We don't have keyids */
+ blob->keys[0].flags = 0;
+
+ /* issuer and subject names */
+ for (i=0; i < blob->nuids; i++)
+ {
+ blob->uids[i].name = names[i];
+ blob->uids[i].len = strlen(names[i]);
+ names[i] = NULL;
+ blob->uids[i].flags = 0;
+ blob->uids[i].validity = 0;
+ }
+ xfree (names);
+ names = NULL;
+
+ /* signatures */
+ blob->sigs[0] = 0; /* not yet checked */
+
+ /* Create a temporary buffer for further processing */
+ init_membuf (&blob->bufbuf, 1024);
+ blob->buf = &blob->bufbuf;
+ /* write out what we already have */
+ rc = create_blob_header (blob, BLOBTYPE_X509, as_ephemeral);
+ if (rc)
+ goto leave;
+ rc = x509_create_blob_cert (blob, cert);
+ if (rc)
+ goto leave;
+ rc = create_blob_trailer (blob);
+ if (rc)
+ goto leave;
+ rc = create_blob_finish ( blob );
+ if (rc)
+ goto leave;
+
+
+ leave:
+ release_kid_list (blob->temp_kids);
+ blob->temp_kids = NULL;
+ if (blob && names)
+ {
+ for (i=0; i < blob->nuids; i++)
+ xfree (names[i]);
+ }
+ xfree (names);
+ if (rc)
+ {
+ _keybox_release_blob (blob);
+ *r_blob = NULL;
+ }
+ else
+ {
+ *r_blob = blob;
+ }
+ return rc;
+}
+#endif /*KEYBOX_WITH_X509*/
+
+
+
+int
+_keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen, off_t off)
+{
+ KEYBOXBLOB blob;
+
+ *r_blob = NULL;
+ blob = xtrycalloc (1, sizeof *blob);
+ if (!blob)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ blob->blob = image;
+ blob->bloblen = imagelen;
+ blob->fileoffset = off;
+ *r_blob = blob;
+ return 0;
+}
+
+void
+_keybox_release_blob (KEYBOXBLOB blob)
+{
+ int i;
+ if (!blob)
+ return;
+ /* hmmm: release membuf here?*/
+ xfree (blob->keys );
+ xfree (blob->serialbuf);
+ for (i=0; i < blob->nuids; i++)
+ xfree (blob->uids[i].name);
+ xfree (blob->uids );
+ xfree (blob->sigs );
+ xfree (blob->blob );
+ xfree (blob );
+}
+
+
+
+const char *
+_keybox_get_blob_image ( KEYBOXBLOB blob, size_t *n )
+{
+ *n = blob->bloblen;
+ return blob->blob;
+}
+
+off_t
+_keybox_get_blob_fileoffset (KEYBOXBLOB blob)
+{
+ return blob->fileoffset;
+}
+