See ChangeLog: Thu May 25 18:39:11 CEST 2000 Werner Koch
authorWerner Koch <wk@gnupg.org>
Thu, 25 May 2000 16:28:04 +0000 (16:28 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 25 May 2000 16:28:04 +0000 (16:28 +0000)
doc/gcryptref-digest.sgml
g10/ChangeLog
g10/Makefile.am
g10/build-packet.c
g10/gpg.c
g10/gpgd.c
g10/kbx.h
g10/kbxblob.c
g10/kbxfile.c
g10/kbxio.c [new file with mode: 0644]
g10/kbxutil.c [new file with mode: 0644]

index f0d5eff..a123c9b 100644 (file)
             </indexterm>
   <function>gcry_md_open</function> creates the context required for
   the message digest functions.  The hash algorithm may optionally be
-  specified.
+  specified. It is possible to use these functions as MAC functons; therefore
+  the flag <literal/GCRY_MD_FLAG_HMAC/ must be given along with the
+  hash functions.  Other MAC algorithms than  HMAC are currently not
+  supported.  The key for the MAC must be set using the gcry_md_setkey macro.
   <function>gcry_md_close</function> releases all resources associated
   with the context.
   <function>gcry_md_enable</function> may be used to enable hash
   <refnamediv>
     <refname>gcry_md_ctl</refname>
     <refname>gcry_md_final</refname>
+    <refname>gcry_md_setkey</refname>
     <refpurpose>perform special operations on a digest context</refpurpose>
   </refnamediv>
 
   </para>
   <para>
   Currently defined values for <parameter>cmd</> are:
-    <literal>GCRYCTL_FINALIZE</> and the conevnience macro
+  </para>
+  <para>
+    <literal>GCRYCTL_FINALIZE</> and the convenience macro
     <function>gcry_md_final(a)</>
   </para>
+  <para>
+    <literal>GCRYCTL_SET_KEY</> and the convenience macro
+    <function>gcry_md_setkey(a)</>.  This is used to turn these
+    hash functions into MAC functions. The key may be any string
+    of the speicified length.  The type of the MAC is determined
+    by special flags set with the open function.
+  </para>
 </refentry>
 
 <!--**********************************************
index d20c26a..64056be 100644 (file)
@@ -1,3 +1,10 @@
+Thu May 25 18:39:11 CEST 2000  Werner Koch  <wk@openit.de>
+
+  * kbxio.c: New.
+
+  * kbxfile.c (print_kbxfile): Add a loop
+  (do_print_kbxfile): Fixed passing to kbx_dump_blob.
+
 Fri Mar 24 11:25:45 CET 2000  Werner Koch  <wk@openit.de>
 
        * gpg.c (print_mds): Add arg keys as a kludge to print hmacs
index 9018c14..55d0ed2 100644 (file)
@@ -9,7 +9,7 @@ LDFLAGS = -static @LDFLAGS@ @DYNLINK_LDFLAGS@
 needed_libs = ../util/libutil.la ../gcrypt/libgcrypt.la ../jnlib/libjnlib.la ../util/libutil.la
 
 #noinst_PROGRAMS = gpgd
-bin_PROGRAMS = gpg
+bin_PROGRAMS = gpg  kbxutil
 
 common_source =  \
              build-packet.c    \
@@ -26,6 +26,7 @@ common_source =  \
              kbnode.c          \
              kbx.h             \
              kbxblob.c         \
+             kbxio.c           \
              kbxfile.c         \
              main.h            \
              mainproc.c        \
@@ -73,6 +74,11 @@ gpg_SOURCES  = gpg.c         \
              dearmor.c         \
              keygen.c
 
+# fixme: remove unused sources from kbxutil
+kbxutil_SOURCES = kbxutil.c   \
+             $(common_source)
+
+
 #gpgd_SOURCES = gpgd.c \
 #             ks-proto.h \
 #             ks-proto.c \
index 5772b13..84912ac 100644 (file)
@@ -196,7 +196,8 @@ static int
 do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
 {
     write_header(out, ctb, uid->len);
-    uid->stored_at = iobuf_tell( out ); /* what a hack */
+    uid->stored_at = iobuf_get_temp_length ( out ); /* what a hack ... */
+       /* ... and it does only work when used with a temp iobuf */
     if( iobuf_write( out, uid->name, uid->len ) )
        return GPGERR_WRITE_FILE;
     return 0;
index 0c0b625..2b66509 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1461,9 +1461,11 @@ main( int argc, char **argv )
        break;
 
       case aFixTrustDB:
-       log_error("this command ist not yet implemented.\"\n");
+       log_error("this command is not yet implemented.\"\n");
        log_error("A workaround is to use \"--export-ownertrust\", remove\n");
        log_error("the trustdb file and do an \"--import-ownertrust\".\n" );
+       #warning removed the next line
+       export_as_kbxfile();
        break;
 
       case aListTrustPath:
index 54a1059..99c91f6 100644 (file)
@@ -1,4 +1,4 @@
-/* ggpd.c - The GnuPG daemon (keyserver)
+/* gpg.c - The GnuPG daemon (keyserver)
  *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
index 6508d3e..25825a4 100644 (file)
--- a/g10/kbx.h
+++ b/g10/kbx.h
 
 #include "keydb.h"
 
+/*-- kbxblob.c */
+struct kbxblob;
 typedef struct kbxblob *KBXBLOB;
 
-int  kbx_create_blob ( KBXBLOB *retkbx, KBNODE keyblock );
+int kbx_new_blob ( KBXBLOB *r_blob,  char *image, size_t imagelen );
+int kbx_create_blob ( KBXBLOB *r_blob, KBNODE keyblock );
 void kbx_release_blob ( KBXBLOB blob );
+const char *kbx_get_blob_image ( KBXBLOB blob, size_t *n );
+
+int kbx_dump_blob ( FILE *fp, KBXBLOB blob  );
+int kbx_blob_has_fpr ( KBXBLOB blob, const byte *fpr );
+int kbx_blob_has_kid ( KBXBLOB blob, const byte *keyidbuf, size_t keyidlen );
+int kbx_blob_has_uid ( KBXBLOB blob,
+                      int (*cmp)(const byte *, size_t, void *), void *opaque );
+
+/*-- kbxio.c --*/
+int kbx_read_blob ( KBXBLOB *r_blob, FILE *a );
+
+/*-- kbxfile.c --*/
+int kbxfile_search_by_fpr( const char *filename, const byte *fpr );
+int kbxfile_search_by_kid ( const char *filename, u32 *kid, int mode );
+int kbxfile_search_by_uid ( const char *filename, const char *name );
+void print_kbxfile( const char *filename );
 
 
 #endif /*GPG_KBX_H*/
index 301671f..c298d58 100644 (file)
@@ -88,7 +88,7 @@ The standard KBX Blob looks like this:
 
     Here comes the keyblock
 
-    maybe we put a sigture here later.
+    maybe we put a signature here later.
 
  b16   MD5 checksum  (useful for KS syncronisation)
  *
@@ -136,20 +136,31 @@ struct keyid_list {
     byte kid[8];
 };
 
+struct fixup_list {
+    struct fixup_list *next;
+    u32 off;
+    u32 val;
+};
+
+
 struct kbxblob {
+    byte *blob;
+    size_t bloblen;
+
+    /* stuff used only by kbx_create_blob */
     int nkeys;
     struct kbxblob_key *keys;
     int nuids;
     struct kbxblob_uid *uids;
     int nsigs;
     u32  *sigs;
+    struct fixup_list *fixups;
 
     struct keyid_list *temp_kids;
-    IOBUF buf; /* the KBX is stored here */
+    IOBUF buf; /* the KBX is temporarly stored here */
 };
 
-
-
+void kbx_release_blob ( KBXBLOB blob );
 
 /* Note: this functions are only used for temportay iobufs and therefore
  * they can't fail */
@@ -183,28 +194,9 @@ putn ( IOBUF out, const byte *p, size_t n )
     }
 }
 
-/****************
- * special version of put 32, which is used to fixup a value at file offset OFF
- */
-static void
-put32at ( IOBUF out, u32 a, size_t pos )
-{
-    size_t n;
-    byte *p;
-
-    iobuf_flush_temp ( out );
-    p = iobuf_get_temp_buffer( out );
-    n = iobuf_get_temp_length( out );
-    assert( n >= pos+4 );
-    p[0] = a >> 24 ;
-    p[1] = a >> 16 ;
-    p[2] = a >>  8 ;
-    p[3] = a      ;
-}
-
 
 /****************
- * We must store the keyid at some place becuase we can't calculate the
+ * 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 fixupd; this must be a non-zero value
  */
@@ -269,8 +261,10 @@ create_key_part( KBXBLOB blob, KBNODE keyblock )
        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, blob->keys[n].fpr, &fprlen );
+           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);
@@ -346,10 +340,10 @@ create_blob_header( KBXBLOB blob )
     put32 ( a, 0 ); /* length of the keyblock, needs fixup */
 
     put16 ( a, blob->nkeys );
-    put16 ( a, 20 + 8 + 2 + 2 );  /* size of key info */
+    put16 ( a, 20 + 4 + 2 + 2 );  /* size of key info */
     for ( i=0; i < blob->nkeys; i++ ) {
        putn ( a, blob->keys[i].fpr, 20 );
-       blob->keys[i].off_kid_addr = iobuf_tell ( a );
+       blob->keys[i].off_kid_addr = iobuf_get_temp_length (a);
        put32 ( a, 0 ); /* offset to keyid, fixed up later */
        put16 ( a, blob->keys[i].flags );
        put16 ( a, 0 ); /* reserved */
@@ -358,7 +352,7 @@ create_blob_header( KBXBLOB blob )
     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 = iobuf_tell ( a );
+       blob->uids[i].off_addr = iobuf_get_temp_length ( a );
        put32 ( a, 0 ); /* offset to userid, fixed up later */
        put32 ( a, blob->uids[i].len );
        put16 ( a, blob->uids[i].flags );
@@ -385,13 +379,17 @@ create_blob_header( KBXBLOB blob )
      * not part of the fingerprint.  While we are doing that, we fixup all
      * the keyID offsets */
     for ( i=0; i < blob->nkeys; i++ ) {
+       struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl );
+       fl->off = blob->keys[i].off_kid_addr;
+       fl->next = blob->fixups;
+       blob->fixups = fl;
+
        if ( blob->keys[i].off_kid ) { /* this is a v3 one */
-           put32at ( a, iobuf_tell(a), blob->keys[i].off_kid_addr );
+           fl->val = iobuf_get_temp_length (a);
            put_stored_kid ( blob, blob->keys[i].off_kid );
        }
        else { /* the better v4 key IDs - just store an offset 8 bytes back */
-           put32at ( a, blob->keys[i].off_kid_addr-8,
-                                   blob->keys[i].off_kid_addr );
+           fl->val = blob->keys[i].off_kid_addr-8;
        }
     }
 
@@ -405,9 +403,17 @@ create_blob_keyblock( KBXBLOB blob, KBNODE keyblock )
     IOBUF a = blob->buf;
     KBNODE node;
     int rc;
-    int nsig;
-
-    for ( nsig = 0, node = keyblock; node; node = node->next ) {
+    int n;
+    u32 kbstart = iobuf_get_temp_length ( a );
+
+    {
+           struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl );
+           fl->off = 8;
+           fl->val = kbstart;
+           fl->next = blob->fixups;
+           blob->fixups = fl;
+    }
+    for ( n = 0, node = keyblock; node; node = node->next ) {
        rc = build_packet ( a, node->pkt );
        if ( rc ) {
            gpg_log_error("build_packet(%d) for kbxblob failed: %s\n",
@@ -418,11 +424,22 @@ create_blob_keyblock( KBXBLOB blob, KBNODE keyblock )
            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 */
-           put32at ( a, u->stored_at, blob->uids[nsig].off_addr );
-           nsig++;
+           struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl );
+           fl->off = blob->uids[n].off_addr;
+           fl->val = u->stored_at;
+           fl->next = blob->fixups;
+           blob->fixups = fl;
+           n++;
        }
     }
-    assert( nsig == blob->nsigs );
+    assert( n == blob->nuids );
+    {
+           struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl );
+           fl->off = 12;
+           fl->val = iobuf_get_temp_length (a) - kbstart;
+           fl->next = blob->fixups;
+           blob->fixups = fl;
+    }
     return 0;
 }
 
@@ -438,10 +455,13 @@ create_blob_finish( KBXBLOB blob )
 {
     IOBUF a = blob->buf;
     byte *p;
+    char *pp;
+    int i;
     size_t n;
 
     /* write a placeholder for the checksum */
-    put32( a, 0 ); put32( a, 0 ); put32( a, 0 ); put32( a, 0 );
+    for ( i = 0; i < 16; i++ )
+       put32( a, 0 );
     /* get the memory area */
     iobuf_flush_temp ( a );
     p = iobuf_get_temp_buffer ( a );
@@ -449,23 +469,48 @@ create_blob_finish( KBXBLOB blob )
     assert( n >= 20 );
 
     /* fixup the length */
-    put32at ( a, 0, n );
+    {
+       struct fixup_list *fl = gcry_xcalloc(1, sizeof *fl );
+       fl->off = 0;
+       fl->val = n;
+       fl->next = blob->fixups;
+       blob->fixups = fl;
+    }
+    /* do the fixups */
+    {
+       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 = gcry_malloc ( n );
+    if ( !pp )
+       return GCRYERR_NO_MEM;
+    memcpy ( pp , p, n );
+    blob->blob = pp;
+    blob->bloblen = n;
+
     return 0;
 }
 
 
 int
-kbx_create_blob ( KBXBLOB *retkbx, KBNODE keyblock )
+kbx_create_blob ( KBXBLOB *r_blob,  KBNODE keyblock )
 {
     int rc = 0;
     KBNODE node;
     KBXBLOB blob;
 
-    *retkbx = NULL;
+    *r_blob = NULL;
     blob = gcry_calloc (1, sizeof *blob );
     if( !blob )
        return GCRYERR_NO_MEM;
@@ -484,9 +529,9 @@ kbx_create_blob ( KBXBLOB *retkbx, KBNODE keyblock )
          default: break;
        }
     }
-    blob->keys = gcry_calloc ( blob->nkeys, sizeof ( blob->keys ) );
-    blob->uids = gcry_calloc ( blob->nuids, sizeof ( blob->uids ) );
-    blob->sigs = gcry_calloc ( blob->nsigs, sizeof ( blob->sigs ) );
+    blob->keys = gcry_calloc ( blob->nkeys, sizeof ( *blob->keys ) );
+    blob->uids = gcry_calloc ( blob->nuids, sizeof ( *blob->uids ) );
+    blob->sigs = gcry_calloc ( blob->nsigs, sizeof ( *blob->sigs ) );
     if ( !blob->keys || !blob->uids || !blob->sigs ) {
        rc = GCRYERR_NO_MEM;
        goto leave;
@@ -516,17 +561,44 @@ kbx_create_blob ( KBXBLOB *retkbx, KBNODE keyblock )
     if( rc )
        goto leave;
 
-    *retkbx = blob;
 
   leave:
     release_kid_list( blob->temp_kids );
     blob->temp_kids = NULL;
     if ( rc ) {
        kbx_release_blob ( blob );
+       *r_blob = NULL;
+    }
+    else  {
+       *r_blob = blob;
     }
     return rc;
 }
 
+int
+kbx_new_blob ( KBXBLOB *r_blob,  char *image, size_t imagelen )
+{
+    KBXBLOB blob;
+
+    *r_blob = NULL;
+    blob = gcry_calloc (1, sizeof *blob );
+    if( !blob )
+       return GCRYERR_NO_MEM;
+    blob->blob = image;
+    blob->bloblen = imagelen;
+    *r_blob = blob;
+    return 0;
+}
+
+
+
+const char *
+kbx_get_blob_image ( KBXBLOB blob, size_t *n )
+{
+    *n = blob->bloblen;
+    return blob->blob;
+}
+
 void
 kbx_release_blob ( KBXBLOB blob )
 {
@@ -537,6 +609,9 @@ kbx_release_blob ( KBXBLOB blob )
     gcry_free( blob->keys );
     gcry_free( blob->uids );
     gcry_free( blob->sigs );
+
+    gcry_free ( blob->blob );
+
     gcry_free( blob );
 }
 
@@ -556,17 +631,21 @@ get16( const byte *buffer )
 {
     ulong a;
     a =  *buffer << 8;
-    a |= buffer[0];
+    a |= buffer[1];
     return a;
 }
 
 
 int
-kbx_dump_blob ( FILE *fp, const byte* buffer, size_t length  )
+kbx_dump_blob ( FILE *fp, KBXBLOB blob )
 {
-  #if 0
-    ulong n;
+    const byte *buffer = blob->blob;
+    size_t length = blob->bloblen;
+    ulong n, nkeys, keyinfolen;
+    ulong nuids, uidinfolen;
+    ulong nsigs, siginfolen;
     ulong keyblock_off, keyblock_len;
+    const byte *p;
 
     if( length < 40 )  {
        fprintf( fp, "blob too short\n");
@@ -579,8 +658,8 @@ kbx_dump_blob ( FILE *fp, const byte* buffer, size_t length  )
     else
        length = n;  /* ignore the rest */
     fprintf( fp, "Length: %lu\n", n );
-    fprintf( fp, "Type:   %d\n", buffer[4] ),
-    fprintf( fp, "Version: %d\n", buffer[5] ),
+    fprintf( fp, "Type:   %d\n", buffer[4] );
+    fprintf( fp, "Version: %d\n", buffer[5] );
     if( buffer[4] != 2 ) {
        fprintf( fp, "can't dump this blob type\n" );
        return 0;
@@ -593,8 +672,224 @@ kbx_dump_blob ( FILE *fp, const byte* buffer, size_t length  )
     fprintf( fp, "Keyblock-Offset: %lu\n", keyblock_off );
     fprintf( fp, "Keyblock-Length: %lu\n", keyblock_len );
 
-  #endif
+    nkeys = get16( buffer + 16 );
+    fprintf( fp, "Key-Count: %lu\n", nkeys );
+    keyinfolen = get16( buffer + 18 );
+    fprintf( fp, "Key-Info-Length: %lu\n", keyinfolen );
+    /* fixme: check bounds */
+    p = buffer + 20;
+    for(n=0; n < nkeys; n++, p += keyinfolen ) {
+       int i;
+       ulong kidoff, kflags;
+
+       fprintf( fp, "Key-%lu-Fpr: ", n );
+       for(i=0; i < 20; i++ )
+           fprintf( fp, "%02X", p[i] );
+       kidoff = get32( p + 20 );
+       fprintf( fp, "\nKey-%lu-Kid-Off: %lu\n", n, kidoff );
+       fprintf( fp, "Key-%lu-Kid: ", n );
+       /* fixme: check bounds */
+       for(i=0; i < 8; i++ )
+           fprintf( fp, "%02X", buffer[kidoff+i] );
+       kflags = get16( p + 24 );
+       fprintf( fp, "\nKey-%lu-Flags: %04lX\n", n, kflags );
+    }
+
+
+    nuids = get16( p );
+    fprintf( fp, "Uid-Count: %lu\n", nuids );
+    uidinfolen = get16( p + 2 );
+    fprintf( fp, "Uid-Info-Length: %lu\n", uidinfolen );
+    /* fixme: check bounds */
+    p += 4;
+    for(n=0; n < nuids; n++, p += uidinfolen ) {
+       ulong uidoff, uidlen, uflags;
+
+       uidoff = get32( p );
+       uidlen = get32( p+4 );
+       fprintf( fp, "Uid-%lu-Off: %lu\n", n, uidoff );
+       fprintf( fp, "Uid-%lu-Len: %lu\n", n, uidlen );
+       fprintf( fp, "Uid-%lu: \"", n );
+       print_string( fp, buffer+uidoff, uidlen, '\"' );
+       fputs("\"\n", fp );
+       uflags = get16( p + 8 );
+       fprintf( fp, "Uid-%lu-Flags: %04lX\n", n, uflags );
+       fprintf( fp, "Uid-%lu-Validity: %d\n", n, p[10] );
+    }
+
+    nsigs = get16( p );
+    fprintf( fp, "Sig-Count: %lu\n", nsigs );
+    siginfolen = get16( p + 2 );
+    fprintf( fp, "Sig-Info-Length: %lu\n", siginfolen );
+    /* fixme: check bounds  */
+    p += 4;
+    for(n=0; n < nsigs; n++, p += siginfolen ) {
+       ulong sflags;
+
+       sflags = get32( p );
+       fprintf( fp, "Sig-%lu-Expire: ", n );
+       if( !sflags )
+           fputs( "[not checked]", fp );
+       else if( sflags == 1 )
+           fputs( "[missing key]", fp );
+       else if( sflags == 2 )
+           fputs( "[bad signature]", fp );
+       else if( sflags < 0x10000000 )
+           fprintf( fp, "[bad flag %0lx]", sflags );
+       else if( sflags == 0xffffffff )
+           fputs( "0", fp );
+       else
+           fputs( strtimestamp( sflags ), fp );
+       putc('\n', fp );
+    }
+
+    fprintf( fp, "Ownertrust: %d\n", p[0] );
+    fprintf( fp, "All-Validity: %d\n", p[1] );
+    p += 4;
+    n = get32( p ); p += 4;
+    fprintf( fp, "Recheck-After: %s\n", n? strtimestamp(n) : "0" );
+    n = get32( p ); p += 4;
+    fprintf( fp, "Latest-Timestamp: %s\n", strtimestamp(n) );
+    n = get32( p ); p += 4;
+    fprintf( fp, "Created-At: %s\n", strtimestamp(n) );
+    n = get32( p ); p += 4;
+    fprintf( fp, "Reserved-Space: %lu\n", n );
+
+
+    /* check that the keyblock is at the correct offset and other bounds */
+
+
+    fprintf( fp, "Blob-Checksum: [MD5-hash]\n" );
+    return 0;
+}
+
+/****************
+ * Check whether the given fingerprint (20 bytes) is in the
+ * given keyblob.  fpr is always 20 bytes.
+ * Return: 0 = found
+ *        -1 = not found
+         other = error  (fixme: do not always reurn gpgerr_general)
+ */
+int
+kbx_blob_has_fpr ( KBXBLOB blob, const byte *fpr )
+{
+    ulong n, nkeys, keyinfolen;
+    const byte *p, *pend;
+    byte *buffer = blob->blob;
+    size_t buflen = blob->bloblen;
+
+    if ( buflen < 40 )
+       return GPGERR_GENERAL; /* blob too short */
+    n = get32( buffer );
+    if ( n > buflen )
+       return GPGERR_GENERAL; /* blob larger than announced length */
+    buflen = n;  /* ignore trailing stuff */
+    pend = buffer + n - 1;
+
+    if ( buffer[4] != 2 )
+       return GPGERR_GENERAL; /* invalid blob type */
+    if ( buffer[5] != 1 )
+       return GPGERR_GENERAL; /* invalid blob format version */
+
+    nkeys = get16( buffer + 16 );
+    keyinfolen = get16( buffer + 18 );
+    p = buffer + 20;
+    for(n=0; n < nkeys; n++, p += keyinfolen ) {
+       if ( p+20 > pend )
+           return GPGERR_GENERAL; /* blob shorter than required */
+       if (!memcmp ( p, fpr, 20 ) )
+           return 0; /* found */
+    }
+    return -1;
+}
+
+/****************
+ * Check whether the given keyID (20 bytes) is in the
+ * given keyblob.
+ * Return: 0 = found
+ *        -1 = not found
+         other = error  (fixme: do not always return gpgerr_general)
+ */
+int
+kbx_blob_has_kid ( KBXBLOB blob, const byte *keyidbuf, size_t keyidlen )
+{
+    ulong n, nkeys, keyinfolen, off;
+    const byte *p, *pend;
+    byte *buffer = blob->blob;
+    size_t buflen = blob->bloblen;
+
+    if ( buflen < 40 )
+       return GPGERR_GENERAL; /* blob too short */
+    n = get32( buffer );
+    if ( n > buflen )
+       return GPGERR_GENERAL; /* blob larger than announced length */
+    buflen = n;  /* ignore trailing stuff */
+    pend = buffer + n - 1;
+
+    if ( buffer[4] != 2 )
+       return GPGERR_GENERAL; /* invalid blob type */
+    if ( buffer[5] != 1 )
+       return GPGERR_GENERAL; /* invalid blob format version */
+
+    nkeys = get16( buffer + 16 );
+    keyinfolen = get16( buffer + 18 );
+    p = buffer + 20;
+    for(n=0; n < nkeys; n++, p += keyinfolen ) {
+       if ( p+24 > pend )
+           return GPGERR_GENERAL; /* blob shorter than required */
+       off = get32 ( p + 20 );
+       if (keyidlen < 8 ) /* actually keyidlen may either be 4 or 8 */
+           off +=4;
+       if ( off+keyidlen > buflen )
+           return GPGERR_GENERAL; /* offset out of bounds */
+       if ( !memcmp ( buffer+off, keyidbuf, keyidlen ) )
+           return 0; /* found */
+    }
+    return -1;
+}
+
+
+
+int
+kbx_blob_has_uid ( KBXBLOB blob,
+                  int (*cmp)(const byte *, size_t, void *), void *opaque )
+{
+    ulong n, nuids, uidinfolen, off, len;
+    const byte *p, *pend;
+    byte *buffer = blob->blob;
+    size_t buflen = blob->bloblen;
+
+    if ( buflen < 40 )
+       return GPGERR_GENERAL; /* blob too short */
+    n = get32( buffer );
+    if ( n > buflen )
+       return GPGERR_GENERAL; /* blob larger than announced length */
+    buflen = n;  /* ignore trailing stuff */
+    pend = buffer + n - 1;
+
+    if ( buffer[4] != 2 )
+       return GPGERR_GENERAL; /* invalid blob type */
+    if ( buffer[5] != 1 )
+       return GPGERR_GENERAL; /* invalid blob format version */
+
+    p = buffer + 20 + get16( buffer + 16 ) * get16( buffer + 18 );
+    if ( p+4 > pend )
+       return GPGERR_GENERAL; /* blob shorter than required */
+
+    nuids = get16( p ); p+= 2;
+    uidinfolen = get16( p ); p+=2;
+    for(n=0; n < nuids; n++, p += uidinfolen ) {
+       if ( p+8 > pend )
+           return GPGERR_GENERAL; /* blob shorter than required */
+       off = get32 ( p );
+       len = get32 ( p + 4 );
+       if ( off+len > buflen )
+           return GPGERR_GENERAL; /* offset out of bounds */
+       if ( (*cmp) ( buffer+off, len, opaque ) )
+           return 0; /* found */
+    }
 
+    return -1;
 }
 
 
index 33bac3a..7bf6150 100644 (file)
@@ -22,7 +22,7 @@
  * We will change the whole system to use only KBX.  This file here
  * will implement the methods needed to operate on plain KBXfiles.
  * Most stuff from getkey and ringedit will be replaced by stuff here.
- * To make things even mor easier we will only allow one updateable kbxfile
+ * To make things even more easier we will only allow one updateable kbxfile
  * and optionally some read-only files.
  */
 
 #include <gcrypt.h>
 
 #include "kbx.h"
+#include "options.h"
+#include "util.h"
+#include "i18n.h"
+#include "main.h"
+
+/****************
+ * Read the blob at the current fileposition and return an allocated
+ * pointer nto the blob if it was found.
+ * Fixme: return a blob object.
+ */
+static int
+do_search_by_fpr ( const char *filename, FILE *a, const char *fpr,
+                                                 KBXBLOB *r_blob )
+{
+    KBXBLOB blob;
+    int rc;
+
+    *r_blob = NULL;
+    rc = kbx_read_blob ( &blob, a );
+    if ( rc && rc != -1 ) {
+       log_error (_("file `%s': error reading blob\n"), filename );
+    }
+    else if ( !rc ) {
+       rc = kbx_blob_has_fpr ( blob, fpr );
+    }
+    else
+       log_info ("eof\n");
+
+    if ( !rc ) {
+       *r_blob = blob;
+    }
+    else {
+       kbx_release_blob ( blob );
+    }
+    return rc;
+}
+
+int
+kbxfile_search_by_fpr( const char *filename, const byte *fpr )
+{
+    FILE *fp;
+    KBXBLOB blob;
+    int rc;
+
+    fp = fopen ( filename, "rb" );
+    if( !fp ) {
+       log_error(_("can't open `%s': %s\n"), filename, strerror(errno) );
+       return 1;
+    }
+
+    while ( (rc=do_search_by_fpr ( filename, fp, fpr, &blob )) == -1 )
+       ;
+    if ( !rc ) {
+       fputs ("FOUND\n", stderr );
+       kbx_dump_blob ( stderr, blob );
+       kbx_release_blob ( blob );
+    }
+
+    fclose (fp);
+    return -1;
+}
+
+
+/****************
+ * Read the blob at the current fileposition and return an allocated
+ * pointer nto the blob if it was found.
+ * Fixme: return a blob object.
+ */
+static int
+do_search_by_keyid ( const char *filename, FILE *a,
+                    const byte *keyidbuf, size_t keyidlen, KBXBLOB *r_blob )
+{
+    KBXBLOB blob;
+    int rc;
+
+    *r_blob = NULL;
+    rc = kbx_read_blob ( &blob, a );
+    if ( rc && rc != -1 ) {
+       log_error (_("file `%s': error reading blob\n"), filename );
+    }
+    else if ( !rc ) {
+       rc = kbx_blob_has_kid ( blob, keyidbuf, keyidlen );
+    }
+    else
+       rc = GPGERR_GENERAL;  /* eof */
+
+    if ( !rc ) {
+       *r_blob = blob;
+    }
+    else {
+       kbx_release_blob ( blob );
+    }
+    return rc;
+}
+
+/****************
+ * Look for a KBX described by an keyid.  This function will in
+ * turn return each matching keyid because there may me duplicates
+ * (which can't happen for fingerprints)
+ * mode 10 = short keyid
+ *     11 = long keyid
+ */
+int
+kbxfile_search_by_kid ( const char *filename, u32 *kid, int mode )
+{
+    FILE *fp;
+    KBXBLOB blob;
+    int rc;
+    byte kbuf[8], *kbufptr;
+    int kbuflen;
+
+    fp = fopen ( filename, "rb" );
+    if( !fp ) {
+       log_error(_("can't open `%s': %s\n"), filename, strerror(errno) );
+       return 1;
+    }
+
+    kbuf[0] = kid[0] >> 24;
+    kbuf[1] = kid[0] >> 16;
+    kbuf[2] = kid[0] >> 8;
+    kbuf[3] = kid[0];
+    kbuf[4] = kid[1] >> 24;
+    kbuf[5] = kid[1] >> 16;
+    kbuf[6] = kid[1] >> 8;
+    kbuf[7] = kid[1];
+    if ( mode == 10 ) {
+       kbufptr=kbuf+4;
+       kbuflen = 4;
+    }
+    else if (mode == 11 ) {
+       kbufptr=kbuf;
+       kbuflen = 8;
+    }
+    else {
+       BUG();
+    }
+
+    do {
+       while ( (rc=do_search_by_keyid ( filename, fp,
+                                        kbufptr, kbuflen, &blob )) == -1 )
+           ;
+       if ( !rc ) {
+           fputs ("FOUND:\n", stderr );
+           kbx_dump_blob ( stderr, blob );
+           kbx_release_blob ( blob );
+       }
+    } while ( !rc );
+
+    fclose (fp);
+    return -1;
+}
+
+
+static int
+do_search_by_uid ( const char *filename, FILE *a,
+                   int (*cmpfnc)(const byte*,size_t,void*), void *cmpdata,
+                                                          KBXBLOB *r_blob )
+{
+    KBXBLOB blob;
+    int rc;
+
+    *r_blob = NULL;
+    rc = kbx_read_blob ( &blob, a );
+    if ( rc && rc != -1 ) {
+       log_error (_("file `%s': error reading blob\n"), filename );
+    }
+    else if ( !rc ) {
+       rc = kbx_blob_has_uid ( blob, cmpfnc, cmpdata );
+    }
+    else
+       rc = GPGERR_GENERAL;  /* eof */
+
+    if ( !rc ) {
+       *r_blob = blob;
+    }
+    else {
+       kbx_release_blob ( blob );
+    }
+    return rc;
+}
+
+
+static int
+substr_compare ( const byte *buf, size_t buflen, void *opaque )
+{
+    return !!memistr ( buf, buflen, opaque );
+}
 
 
 int
-kbxfile_search_by_fpr( void )
+kbxfile_search_by_uid ( const char *filename, const char *name )
 {
+    FILE *fp;
+    KBXBLOB blob;
+    int rc;
+    byte kbuf[8], *kbufptr;
+    int kbuflen;
+
+    fp = fopen ( filename, "rb" );
+    if( !fp ) {
+       log_error(_("can't open `%s': %s\n"), filename, strerror(errno) );
+       return 1;
+    }
+
+
+    do {
+       while ( (rc=do_search_by_uid ( filename, fp,
+                                       substr_compare, name, &blob )) == -1 )
+           ;
+       if ( !rc ) {
+           fputs ("FOUND:\n", stderr );
+           kbx_dump_blob ( stderr, blob );
+           kbx_release_blob ( blob );
+       }
+    } while ( !rc );
+
+    fclose ( fp );
     return -1;
 }
 
+
+
+void
+export_as_kbxfile(void)
+{
+
+    KBPOS kbpos;
+    KBNODE keyblock = NULL;
+    int rc=0;
+
+    rc = enum_keyblocks( 0, &kbpos, &keyblock );
+    if( rc ) {
+       if( rc != -1 )
+           log_error("enum_keyblocks(open) failed: %s\n", gpg_errstr(rc) );
+       goto leave;
+    }
+
+    while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) {
+       KBXBLOB blob;
+       const char *p;
+       size_t n;
+
+       merge_keys_and_selfsig( keyblock );
+       rc = kbx_create_blob ( &blob, keyblock );
+       if( rc ) {
+           log_error("kbx_create_blob failed: %s\n", gpg_errstr(rc) );
+           goto leave;
+       }
+       p = kbx_get_blob_image ( blob, &n );
+       fwrite( p, n, 1, stdout );
+       kbx_release_blob ( blob );
+    }
+
+    if( rc && rc != -1 )
+       log_error("enum_keyblocks(read) failed: %s\n", gpg_errstr(rc));
+
+  leave:
+    enum_keyblocks( 2, &kbpos, &keyblock ); /* close */
+    release_kbnode( keyblock );
+}
+
+
+static int
+do_print_kbxfile( const char *filename, FILE *a )
+{
+    KBXBLOB blob;
+    int rc;
+
+    rc = kbx_read_blob ( &blob, a );
+    if ( rc && rc != -1 ) {
+       log_error (_("file `%s': error reading blob\n"), filename );
+    }
+    else if ( ! rc )
+       kbx_dump_blob ( stdout, blob );
+    kbx_release_blob ( blob );
+    return rc;
+}
+
+void
+print_kbxfile( const char *filename )
+{
+    FILE *fp;
+
+    fp = fopen ( filename, "rb" );
+    if( !fp ) {
+       log_error(_("can't open `%s': %s\n"), filename, strerror(errno) );
+       return 1;
+    }
+
+    while ( !do_print_kbxfile( filename, fp ) )
+       ;
+
+    fclose (fp);
+}
+
diff --git a/g10/kbxio.c b/g10/kbxio.c
new file mode 100644 (file)
index 0000000..6c4437b
--- /dev/null
@@ -0,0 +1,75 @@
+/* kbxio.c - KBX I/O handling
+ *     Copyright (C) 2000 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <gcrypt.h>
+
+#include "iobuf.h"
+#include "util.h"
+#include "kbx.h"
+
+
+int
+kbx_read_blob ( KBXBLOB *r_blob, FILE *a )
+{
+    char *image;
+    size_t imagelen = 0;
+    int c1, c2, c3, c4;
+    int rc;
+
+    *r_blob = NULL;
+    if (    (c1 = getc ( a )) == EOF
+        || (c2 = getc ( a )) == EOF
+        || (c3 = getc ( a )) == EOF
+        || (c4 = getc ( a )) == EOF ) {
+       if ( c1 == EOF && !ferror ( a ) )
+           return -1;
+       return GPGERR_GENERAL;
+    }
+    imagelen = (c1 << 24) | (c2 << 16) | (c3 << 8 ) | c4;
+    if ( imagelen > 500000 ) { /* sanity check:blob too large */
+       return GPGERR_GENERAL;
+    }
+    else if ( imagelen < 4 ) { /* blobtoo short */
+       return GPGERR_GENERAL;
+    }
+    image = gcry_malloc ( imagelen );
+    if ( !image ) {
+       return GPGERR_GENERAL;
+    }
+
+    image[0] = c1; image[1] = c2; image[2] = c3; image[3] = c4;
+    if ( fread ( image+4, imagelen-4, 1, a ) != 1 )  {
+       gcry_free ( image );
+       return GPGERR_GENERAL;
+    }
+
+    rc = kbx_new_blob ( r_blob, image, imagelen );
+    return rc;
+}
+
+
+
diff --git a/g10/kbxutil.c b/g10/kbxutil.c
new file mode 100644 (file)
index 0000000..95fcb9c
--- /dev/null
@@ -0,0 +1,442 @@
+/* gpg.c - The GnuPG utility (main for gpg)
+ *     Copyright (C) 1998, 1999, 2000 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 <gcrypt.h>
+
+#include "packet.h"
+#include "iobuf.h"
+#include "util.h"
+#include "main.h"
+#include "options.h"
+#include "keydb.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "i18n.h"
+#include "gnupg-defs.h"
+#include "kbx.h"
+
+
+enum cmd_and_opt_values { aNull = 0,
+    oArmor       = 'a',
+    aDetachedSign = 'b',
+    aSym         = 'c',
+    aDecrypt     = 'd',
+    aEncr        = 'e',
+    oInteractive  = 'i',
+    oKOption     = 'k',
+    oDryRun      = 'n',
+    oOutput      = 'o',
+    oQuiet       = 'q',
+    oRecipient   = 'r',
+    aSign        = 's',
+    oTextmodeShort= 't',
+    oUser        = 'u',
+    oVerbose     = 'v',
+    oCompress    = 'z',
+    oNotation    = 'N',
+    oBatch       = 500,
+    aClearsign,
+    aStore,
+    aKeygen,
+    aSignEncr,
+    aSignKey,
+    aLSignKey,
+    aListPackets,
+    aEditKey,
+    aDeleteKey,
+    aDeleteSecretKey,
+    aKMode,
+    aKModeC,
+    aImport,
+    aFastImport,
+    aVerify,
+    aListKeys,
+    aListSigs,
+    aListSecretKeys,
+    aSendKeys,
+    aRecvKeys,
+    aExport,
+    aExportAll,
+    aExportSecret,
+    aCheckKeys,
+    aGenRevoke,
+    aPrimegen,
+    aPrintMD,
+    aPrintHMAC,
+    aPrintMDs,
+    aCheckTrustDB,
+    aUpdateTrustDB,
+    aFixTrustDB,
+    aListTrustDB,
+    aListTrustPath,
+    aExportOwnerTrust,
+    aImportOwnerTrust,
+    aDeArmor,
+    aEnArmor,
+    aGenRandom,
+
+    oTextmode,
+    oFingerprint,
+    oWithFingerprint,
+    oAnswerYes,
+    oAnswerNo,
+    oKeyring,
+    oSecretKeyring,
+    oDefaultKey,
+    oDefRecipient,
+    oDefRecipientSelf,
+    oNoDefRecipient,
+    oOptions,
+    oDebug,
+    oDebugAll,
+    oStatusFD,
+    oNoComment,
+    oNoVersion,
+    oEmitVersion,
+    oCompletesNeeded,
+    oMarginalsNeeded,
+    oMaxCertDepth,
+    oLoadExtension,
+    oRFC1991,
+    oOpenPGP,
+    oCipherAlgo,
+    oDigestAlgo,
+    oCompressAlgo,
+    oPasswdFD,
+    oNoVerbose,
+    oTrustDBName,
+    oNoSecmemWarn,
+    oNoArmor,
+    oNoDefKeyring,
+    oNoGreeting,
+    oNoTTY,
+    oNoOptions,
+    oNoBatch,
+    oHomedir,
+    oWithColons,
+    oWithKeyData,
+    oSkipVerify,
+    oCompressKeys,
+    oCompressSigs,
+    oAlwaysTrust,
+    oEmuChecksumBug,
+    oRunAsShmCP,
+    oSetFilename,
+    oSetPolicyURL,
+    oUseEmbeddedFilename,
+    oComment,
+    oDefaultComment,
+    oThrowKeyid,
+    oForceV3Sigs,
+    oForceMDC,
+    oS2KMode,
+    oS2KDigest,
+    oS2KCipher,
+    oCharset,
+    oNotDashEscaped,
+    oEscapeFrom,
+    oLockOnce,
+    oLockMultiple,
+    oKeyServer,
+    oEncryptTo,
+    oNoEncryptTo,
+    oLoggerFD,
+    oUtf8Strings,
+    oNoUtf8Strings,
+    oDisableCipherAlgo,
+    oDisablePubkeyAlgo,
+    oAllowNonSelfsignedUID,
+    oNoLiteral,
+    oSetFilesize,
+    oEntropyDLLName,
+
+    aFindByFpr,
+    aFindByKid,
+    aFindByUid,
+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, "@" },
+    { oCompress, NULL,       1, N_("|N|set compress level N (0 disables)") },
+    { 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") },
+    { oOptions, "options"   , 2, N_("read options from file")},
+
+    { oDebug, "debug"     ,4|16, N_("set debugging flags")},
+    { oDebugAll, "debug-all" ,0, N_("enable full debugging")},
+
+
+{0} };
+
+
+
+int gpg_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 <gnupg-bugs@gnu.org>.\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 KBX 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, GNUPG_LOCALEDIR );
+    textdomain( PACKAGE );
+  #endif
+  #endif
+}
+
+
+static void
+wrong_args( const char *text )
+{
+    log_error("usage: kbxutil %s\n", text);
+    gpg_exit ( 1 );
+}
+
+
+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;
+}
+
+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 );
+}
+
+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 */
+}
+
+
+
+int
+main( int argc, char **argv )
+{
+    ARGPARSE_ARGS pargs;
+    enum cmd_and_opt_values cmd = 0;
+
+    set_strusage( my_strusage );
+    log_set_name("kbxutil");
+    /* 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.0a" ) ) {
+       log_fatal(_("libgcrypt is too old (need %s, have %s)\n"),
+                               VERSION, gcry_check_version(NULL) );
+    }
+
+    create_dotlock(NULL); /* register locking cleanup */
+    i18n_init();
+
+
+    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) )
+       gpg_exit(2);
+
+    if ( !cmd ) { /* default is to list a KBX file */
+       if( !argc ) {
+           print_kbxfile( NULL );
+       }
+       else {
+           for ( ; argc; argc--, argv++ ) {
+               print_kbxfile( *argv );
+           }
+       }
+    }
+    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] );
+    }
+    else
+       log_error ("unsupported action\n");
+
+    gpg_exit(0);
+    return 8; /*NEVER REACHED*/
+}
+
+
+void
+gpg_exit( 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 :
+                       gpg_errors_seen? 1 : 0;
+    exit(rc );
+}
+
+