edit-key is now complete
authorWerner Koch <wk@gnupg.org>
Wed, 29 Jul 1998 19:35:05 +0000 (19:35 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 29 Jul 1998 19:35:05 +0000 (19:35 +0000)
48 files changed:
AUTHORS
NEWS
README
THANKS
TODO
checks/defs.inc
cipher/ChangeLog
cipher/Makefile.am
cipher/cipher.c
cipher/tiger.c
doc/DETAILS
doc/gpg.1pod
g10/ChangeLog
g10/Makefile.am
g10/OPTIONS
g10/build-packet.c
g10/cipher.c
g10/encr-data.c
g10/free-packet.c
g10/g10.c
g10/import.c
g10/kbnode.c
g10/keydb.h
g10/keyedit.c
g10/keygen.c
g10/keyid.c
g10/keylist.c
g10/main.h
g10/mainproc.c
g10/misc.c
g10/options.h
g10/packet.h
g10/parse-packet.c
g10/seckey-cert.c
g10/sign.c
g10/tdbio.c
g10/tdbio.h
g10/trustdb.c
g10/trustdb.h
include/cipher.h
include/ttyio.h
mpi/g10m.c
mpi/mpi-inv.c
po/de.po
util/ChangeLog
util/miscutil.c
util/secmem.c
util/ttyio.c

diff --git a/AUTHORS b/AUTHORS
index 86dab48..779b4ed 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,3 +6,7 @@ Werner Koch.  Designed and implemented gnupg.
 TRANSLATIONS   Marco d'Itri    1997-02-22
 Disclaim
 
+Twofish        Matthew Skala   ????????????
+
+
+
diff --git a/NEWS b/NEWS
index 6197de5..0a375b1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,12 +1,37 @@
 Noteworthy changes in version 0.3.3
 -----------------------------------
+    * The format of the trust database has changed; you must delete
+      the old one, so gnupg can create a new one.
+      IMPORTANT: Use version 0.3.[12] to save your assigned ownertrusts
+      ("gpgm --list-ownertrust >saved-trust"); then build this new version
+      and restore the ownertrust with this new version
+      ("gpgm --import-ownertrust saved-trust").
+
+    * The command --edit-key now provides a commandline driven menu
+      which can be used vor vaious tasks.  --sign-key is only an
+      an alias to --edit-key and maybe removed in future: use the
+      command "sign" of this new menu - you can select which user ids
+      you want to sign.
+
+    * Alternate user ids can now be created an signed.
+
     * Removed options --gen-prime and --gen-random.
 
+    * Removed option --add-key; use --edit-key instead.
+
+    * Removed option --change-passphrase; use --edit-key instead.
+
     * Signatures are now checked even if the output file could not
       be created. Command "--verify" tries to find the detached data.
 
     * gpg now disables core dumps.
 
+    * We have added the Twofish as an experimental cipher algorithm.
+      Many thanks to Matthew Skala for doing this work.
+      Twofish is the AES submission from Schneier et al.; see
+      "www.counterpane.com/twofish.html" for more information.
+
+
 
 Noteworthy changes in version 0.3.2
 -----------------------------------
diff --git a/README b/README
index c220c00..240b319 100644 (file)
--- a/README
+++ b/README
 
     Creates a signature of file, but writes the output to the file "out".
 
+    If you use the option "--rfc1991", gnupg tries to me more compatible
+    to RFC1991 (pgp 2.x).
+
 
     Encrypt
     -------
diff --git a/THANKS b/THANKS
index b30466c..d0c53d5 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -5,6 +5,7 @@ errors.
 
 Anand Kumria           wildfire@progsoc.uts.edu.au
 Brian Warner           warner@lothar.com
+Caskey L. Dickson      caskey@technocage.com
 Charles Levert         charles@comm.polymtl.ca
 Christian von Roques   roques@pond.sub.org
 Daniel Eisenbud        eisenbud@cs.swarthmore.edu
@@ -25,8 +26,10 @@ Martin Schulte               schulte@thp.uni-koeln.de
 Matthew Skala          mskala@ansuz.sooke.bc.ca
 Max Valianskiy         maxcom@maxcom.ml.org
 Nicolas Graner         Nicolas.Graner@cri.u-psud.fr
+Oskari Jääskeläinen    f33003a@cc.hut.fi
 Peter Gutmann          pgut001@cs.auckland.ac.nz
 Ralph Gillen           gillen@theochem.uni-duesseldorf.de
+Steffen Ullrich        ccrlphr@xensei.com
 Thomas Roessler        roessler@guug.de
 Tom Spindler           dogcow@home.merit.edu
 Tom Zerucha            tzeruch@ceddec.com
diff --git a/TODO b/TODO
index 3c05ad0..9ceeb72 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,6 +2,16 @@
       can also hold the localid and extend the localid to hold information
       of the subkey number because two subkeys may have the same keyid.
 
+    * Fix Oscaris problems with the trustdb.
+
+    * add test cases for invalid data (scrambled armor or other random data)
+
+    * fix the expire stuff for v4 packets.
+
+    * check whether it is valid to pack the signature stuff (onepass, data,
+      sig) into a compressed packet - or should we only compress the data?
+      what does pgp 5 do, what does OpenPGP say=
+
     * invalid packets (Marco)
 
     * add some sanity checks to read_keyblock, so that we are sure that
@@ -9,9 +19,7 @@
 
     * what about the CR,LF in cleartext singatures?
 
-    * add option --restore-ownertrust
-
-    * always put key signatures before the first subkey.
+    * add option --import-ownertrust
 
     * add a way to delete subkeys (in edit-keys?)
 
@@ -33,8 +41,6 @@
     * add checking of armor trailers
     * remove all "Fixmes"
 
-    * add an option to create a new user id.
-
     * add an option to re-create a public key from a secret key. Think about
       a backup system of only the secret part of the secret key.
 
index f3ff66f..49cbcf2 100755 (executable)
@@ -61,10 +61,10 @@ chdir () {
 
 
 set -e
-pgmname=$(basename $0)
+pgmname=`basename $0`
 #trap cleanup SIGHUP SIGINT SIGQUIT
 
-[ -z $srcdir ] && fatal "not called from make"
+[ -z "$srcdir" ] && fatal "not called from make"
 
 cat <<EOF  >./options
 no-greeting
index 0109528..528de20 100644 (file)
@@ -1,3 +1,10 @@
+Mon Jul 27 10:30:22 1998  Werner Koch  (wk@(none))
+
+       * cipher.c : Support for other blocksizes
+       (cipher_get_blocksize): New.
+       * twofish.c: New.
+       * Makefile.am: Add twofish module.
+
 Mon Jul 13 21:30:52 1998  Werner Koch  (wk@isil.d.shuttle.de)
 
        * random.c (read_pool): Simple alloc if secure_alloc is not set.
index f01bb78..36dd0c5 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-gnupg_extensions = tiger
+gnupg_extensions = tiger twofish
 
 INCLUDES =  -I$(top_srcdir)/include -I$(top_srcdir)/intl -I../intl
 
@@ -42,9 +42,13 @@ libcipher_a_SOURCES = cipher.c       \
                 smallprime.c
 
 EXTRA_tiger_SOURCES = tiger.c
+EXTRA_twofish_SOURCES = twofish.c
 
 tiger: tiger.c
-       $(COMPILE) -shared -fPIC -o tiger tiger.c
+       $(COMPILE) -shared  -fPIC -o tiger tiger.c
+
+twofish: twofish.c
+       $(COMPILE) -shared  -fPIC -o twofish twofish.c
 
 install-exec-hook:
        @list='$(pkglib_PROGRAMS)'; for p in $$list; do \
index 25b1e8f..049207b 100644 (file)
 #include "dynload.h"
 
 
-#define STD_BLOCKSIZE 8
+#define MAX_BLOCKSIZE 16
 #define TABLE_SIZE 10
 
 struct cipher_table_s {
     const char *name;
     int algo;
+    size_t blocksize;
     size_t keylen;
     size_t contextsize; /* allocate this amount of context */
     void (*setkey)( void *c, byte *key, unsigned keylen );
@@ -53,8 +54,9 @@ static struct cipher_table_s cipher_table[TABLE_SIZE];
 struct cipher_handle_s {
     int  algo;
     int  mode;
-    byte iv[STD_BLOCKSIZE];    /* (this should be ulong aligned) */
-    byte lastiv[STD_BLOCKSIZE];
+    size_t blocksize;
+    byte iv[MAX_BLOCKSIZE];    /* (this should be ulong aligned) */
+    byte lastiv[MAX_BLOCKSIZE];
     int  unused;  /* in IV */
     void (*setkey)( void *c, byte *key, unsigned keylen );
     void (*encrypt)( void *c, byte *outbuf, byte *inbuf );
@@ -80,44 +82,44 @@ setup_cipher_table()
 {
 
     int i;
-    size_t blocksize;
 
     i = 0;
     cipher_table[i].algo = CIPHER_ALGO_BLOWFISH;
     cipher_table[i].name = blowfish_get_info( cipher_table[i].algo,
                                         &cipher_table[i].keylen,
-                                        &blocksize,
+                                        &cipher_table[i].blocksize,
                                         &cipher_table[i].contextsize,
                                         &cipher_table[i].setkey,
                                         &cipher_table[i].encrypt,
                                         &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name || blocksize != STD_BLOCKSIZE )
+    if( !cipher_table[i].name )
        BUG();
     i++;
     cipher_table[i].algo = CIPHER_ALGO_CAST5;
     cipher_table[i].name = cast5_get_info( cipher_table[i].algo,
                                         &cipher_table[i].keylen,
-                                        &blocksize,
+                                        &cipher_table[i].blocksize,
                                         &cipher_table[i].contextsize,
                                         &cipher_table[i].setkey,
                                         &cipher_table[i].encrypt,
                                         &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name || blocksize != STD_BLOCKSIZE )
+    if( !cipher_table[i].name )
        BUG();
     i++;
     cipher_table[i].algo = CIPHER_ALGO_BLOWFISH160;
     cipher_table[i].name = blowfish_get_info( cipher_table[i].algo,
                                         &cipher_table[i].keylen,
-                                        &blocksize,
+                                        &cipher_table[i].blocksize,
                                         &cipher_table[i].contextsize,
                                         &cipher_table[i].setkey,
                                         &cipher_table[i].encrypt,
                                         &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name || blocksize != STD_BLOCKSIZE )
+    if( !cipher_table[i].name )
        BUG();
     i++;
     cipher_table[i].algo = CIPHER_ALGO_DUMMY;
     cipher_table[i].name = "DUMMY";
+    cipher_table[i].blocksize = 8;
     cipher_table[i].keylen = 128;
     cipher_table[i].contextsize = 0;
     cipher_table[i].setkey = dummy_setkey;
@@ -141,7 +143,6 @@ load_cipher_modules()
     void *context = NULL;
     struct cipher_table_s *ct;
     int ct_idx;
-    size_t blocksize;
     int i;
     const char *name;
     int any = 0;
@@ -164,9 +165,9 @@ load_cipher_modules()
        BUG(); /* table already full */
     /* now load all extensions */
     while( (name = enum_gnupgext_ciphers( &context, &ct->algo,
-                               &ct->keylen, &blocksize, &ct->contextsize,
+                               &ct->keylen, &ct->blocksize, &ct->contextsize,
                                &ct->setkey, &ct->encrypt, &ct->decrypt)) ) {
-       if( blocksize != STD_BLOCKSIZE ) {
+       if( ct->blocksize != 8 && ct->blocksize != 16 ) {
            log_info("skipping cipher %d: unsupported blocksize\n", ct->algo);
            continue;
        }
@@ -271,6 +272,26 @@ cipher_get_keylen( int algo )
     return 0;
 }
 
+unsigned
+cipher_get_blocksize( int algo )
+{
+    int i;
+    unsigned len = 0;
+
+    do {
+       for(i=0; cipher_table[i].name; i++ ) {
+           if( cipher_table[i].algo == algo ) {
+               len = cipher_table[i].blocksize;
+               if( !len )
+                   log_bug("cipher %d w/o blocksize\n", algo );
+               return len;
+           }
+       }
+    } while( load_cipher_modules() );
+    log_bug("cipher %d not found\n", algo );
+    return 0;
+}
+
 
 /****************
  * Open a cipher handle for use with algorithm ALGO, in mode MODE
@@ -299,6 +320,7 @@ cipher_open( int algo, int mode, int secure )
                                        + cipher_table[i].contextsize )
                : m_alloc_clear( sizeof *hd + cipher_table[i].contextsize );
     hd->algo = algo;
+    hd->blocksize = cipher_table[i].blocksize;
     hd->setkey = cipher_table[i].setkey;
     hd->encrypt = cipher_table[i].encrypt;
     hd->decrypt = cipher_table[i].decrypt;
@@ -336,9 +358,9 @@ void
 cipher_setiv( CIPHER_HANDLE c, const byte *iv )
 {
     if( iv )
-       memcpy( c->iv, iv, STD_BLOCKSIZE );
+       memcpy( c->iv, iv, c->blocksize );
     else
-       memset( c->iv, 0, STD_BLOCKSIZE );
+       memset( c->iv, 0, c->blocksize );
     c->unused = 0;
 }
 
@@ -351,8 +373,8 @@ do_ecb_encrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nblocks )
 
     for(n=0; n < nblocks; n++ ) {
        (*c->encrypt)( &c->context, outbuf, inbuf );
-       inbuf  += STD_BLOCKSIZE;;
-       outbuf += STD_BLOCKSIZE;
+       inbuf  += c->blocksize;
+       outbuf += c->blocksize;
     }
 }
 
@@ -363,8 +385,8 @@ do_ecb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nblocks )
 
     for(n=0; n < nblocks; n++ ) {
        (*c->decrypt)( &c->context, outbuf, inbuf );
-       inbuf  += STD_BLOCKSIZE;;
-       outbuf += STD_BLOCKSIZE;
+       inbuf  += c->blocksize;
+       outbuf += c->blocksize;
     }
 }
 
@@ -373,11 +395,12 @@ static void
 do_cfb_encrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
 {
     byte *ivp;
+    size_t blocksize = c->blocksize;
 
     if( nbytes <= c->unused ) {
        /* short enough to be encoded by the remaining XOR mask */
        /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; nbytes; nbytes--, c->unused-- )
+       for(ivp=c->iv+c->blocksize - c->unused; nbytes; nbytes--, c->unused-- )
            *outbuf++ = (*ivp++ ^= *inbuf++);
        return;
     }
@@ -385,26 +408,26 @@ do_cfb_encrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
     if( c->unused ) {
        /* XOR the input with the IV and store input into IV */
        nbytes -= c->unused;
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; c->unused; c->unused-- )
+       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- )
            *outbuf++ = (*ivp++ ^= *inbuf++);
     }
 
     /* now we can process complete blocks */
-    while( nbytes >= STD_BLOCKSIZE ) {
+    while( nbytes >= blocksize ) {
        int i;
        /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
+       memcpy( c->lastiv, c->iv, blocksize );
        (*c->encrypt)( &c->context, c->iv, c->iv );
        /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv,i=0; i < STD_BLOCKSIZE; i++ )
+       for(ivp=c->iv,i=0; i < blocksize; i++ )
            *outbuf++ = (*ivp++ ^= *inbuf++);
-       nbytes -= STD_BLOCKSIZE;
+       nbytes -= blocksize;
     }
     if( nbytes ) { /* process the remaining bytes */
        /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
+       memcpy( c->lastiv, c->iv, blocksize );
        (*c->encrypt)( &c->context, c->iv, c->iv );
-       c->unused = STD_BLOCKSIZE;
+       c->unused = blocksize;
        /* and apply the xor */
        c->unused -= nbytes;
        for(ivp=c->iv; nbytes; nbytes-- )
@@ -417,11 +440,12 @@ do_cfb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
 {
     byte *ivp;
     ulong temp;
+    size_t blocksize = c->blocksize;
 
     if( nbytes <= c->unused ) {
        /* short enough to be encoded by the remaining XOR mask */
        /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; nbytes; nbytes--,c->unused--){
+       for(ivp=c->iv+blocksize - c->unused; nbytes; nbytes--,c->unused--){
            temp = *inbuf++;
            *outbuf++ = *ivp ^ temp;
            *ivp++ = temp;
@@ -432,7 +456,7 @@ do_cfb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
     if( c->unused ) {
        /* XOR the input with the IV and store input into IV */
        nbytes -= c->unused;
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; c->unused; c->unused-- ) {
+       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- ) {
            temp = *inbuf++;
            *outbuf++ = *ivp ^ temp;
            *ivp++ = temp;
@@ -440,70 +464,24 @@ do_cfb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
     }
 
     /* now we can process complete blocks */
-  #ifdef BIG_ENDIAN_HOST
-    /* This does only make sense for big endian hosts, due to ... ivp = temp*/
-    if( !((ulong)inbuf % SIZEOF_UNSIGNED_LONG) ) {
-       while( nbytes >= STD_BLOCKSIZE ) {
-           /* encrypt the IV (and save the current one) */
-           memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
-           (*c->encrypt)( &c->context, c->iv, c->iv );
-           ivp = c->iv;
-           /* XOR the input with the IV and store input into IV */
-         #if SIZEOF_UNSIGNED_LONG == STD_BLOCKSIZE
-           temp = *(ulong*)inbuf;
-           *(ulong*)outbuf = *(ulong*)c->iv ^ temp;
-           *(ulong*)ivp    = temp;
-         #elif (2*SIZEOF_UNSIGNED_LONG) == STD_BLOCKSIZE
-           temp = ((ulong*)inbuf)[0];
-           ((ulong*)outbuf)[0] = ((ulong*)c->iv)[0] ^ temp;
-           ((ulong*)ivp)[0] = temp;
-           temp = ((ulong*)inbuf)[1];
-           ((ulong*)outbuf)[1] = ((ulong*)c->iv)[1] ^ temp;
-           ((ulong*)ivp)[1] = temp;
-         #elif (4*SIZEOF_UNSIGNED_LONG) == STD_BLOCKSIZE
-           temp = ((ulong*)inbuf)[0];
-           ((ulong*)outbuf)[0] = ((ulong*)c->iv)[0] ^ temp;
-           ((ulong*)ivp)[0] = temp;
-           temp = ((ulong*)inbuf)[1];
-           ((ulong*)outbuf)[1] = ((ulong*)c->iv)[1] ^ temp;
-           ((ulong*)ivp)[1] = temp;
-           temp = ((ulong*)inbuf)[2];
-           ((ulong*)outbuf)[2] = ((ulong*)c->iv)[2] ^ temp;
-           ((ulong*)ivp)[2] = temp;
-           temp = ((ulong*)inbuf)[3];
-           ((ulong*)outbuf)[3] = ((ulong*)c->iv)[3] ^ temp;
-           ((ulong*)ivp)[3] = temp;
-         #else
-           #error Please disable the align test.
-         #endif
-           nbytes -= STD_BLOCKSIZE;
-           inbuf  += STD_BLOCKSIZE;
-           outbuf += STD_BLOCKSIZE;
-       }
-    }
-    else { /* non aligned version */
-  #endif /* BIG_ENDIAN_HOST */
-       while( nbytes >= STD_BLOCKSIZE ) {
-           int i;
-           /* encrypt the IV (and save the current one) */
-           memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
-           (*c->encrypt)( &c->context, c->iv, c->iv );
-           /* XOR the input with the IV and store input into IV */
-           for(ivp=c->iv,i=0; i < STD_BLOCKSIZE; i++ ) {
-               temp = *inbuf++;
-               *outbuf++ = *ivp ^ temp;
-               *ivp++ = temp;
-           }
-           nbytes -= STD_BLOCKSIZE;
+    while( nbytes >= blocksize ) {
+       int i;
+       /* encrypt the IV (and save the current one) */
+       memcpy( c->lastiv, c->iv, blocksize );
+       (*c->encrypt)( &c->context, c->iv, c->iv );
+       /* XOR the input with the IV and store input into IV */
+       for(ivp=c->iv,i=0; i < blocksize; i++ ) {
+           temp = *inbuf++;
+           *outbuf++ = *ivp ^ temp;
+           *ivp++ = temp;
        }
-   #ifdef BIG_ENDIAN_HOST
+       nbytes -= blocksize;
     }
-   #endif
     if( nbytes ) { /* process the remaining bytes */
        /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
+       memcpy( c->lastiv, c->iv, blocksize );
        (*c->encrypt)( &c->context, c->iv, c->iv );
-       c->unused = STD_BLOCKSIZE;
+       c->unused = blocksize;
        /* and apply the xor */
        c->unused -= nbytes;
        for(ivp=c->iv; nbytes; nbytes-- ) {
@@ -576,8 +554,8 @@ void
 cipher_sync( CIPHER_HANDLE c )
 {
     if( c->mode == CIPHER_MODE_PHILS_CFB && c->unused ) {
-       memmove(c->iv + c->unused, c->iv, STD_BLOCKSIZE - c->unused );
-       memcpy(c->iv, c->lastiv + STD_BLOCKSIZE - c->unused, c->unused);
+       memmove(c->iv + c->unused, c->iv, c->blocksize - c->unused );
+       memcpy(c->iv, c->lastiv + c->blocksize - c->unused, c->unused);
        c->unused = 0;
     }
 }
index 2560e8a..d02f5cb 100644 (file)
@@ -939,7 +939,6 @@ gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
     void *ret;
     int i = *sequence;
 
-    /*log_info("gnupgext_enum_func in rsa+idea called what=%d i=%d: ", what, i);*/
     do {
        if( i >= DIM(func_table) || i < 0 ) {
            /*fprintf(stderr, "failed\n");*/
index 661a4a1..04236b7 100644 (file)
@@ -56,23 +56,18 @@ Record type 1:
 --------------
     Version information for this TrustDB.  This is always the first
     record of the DB and the only one with type 1.
-     1 byte value 2
+     1 byte value 1
      3 bytes 'gpg'  magic value
      1 byte Version of the TrustDB
      3 byte reserved
-     1 u32  locked by (pid) 0 = not locked.
+     1 u32  locked flags
      1 u32  timestamp of trustdb creation
      1 u32  timestamp of last modification
      1 u32  timestamp of last validation
            (Used to keep track of the time, when this TrustDB was checked
             against the pubring)
-     1 u32  reserved
-     1 byte marginals needed
-     1 byte completes needed
-     1 byte max. cert depth
-           If any of this 3 values are changed, all cache records
-           must be invalidated.
-     9 bytes reserved
+     1 u32  record number of keyhashtable
+     12 bytes reserved
 
 
 Record type 2: (directory record)
@@ -183,9 +178,9 @@ Record type 9:      (cache record)
 
 Record Type 10 (hash table)
 --------------
-    Due to the fact that we use the keyid to lookup keys, we can
+    Due to the fact that we use fingerprints to lookup keys, we can
     implement quick access by some simple hash methods, and avoid
-    the overhead of gdbm.  A property of keyids is that they can be
+    the overhead of gdbm.  A property of fingerprints is that they can be
     used directly as hash values.  (They can be considered as strong
     random numbers.)
       What we use is a dynamic multilevel architecture, which combines
@@ -194,11 +189,11 @@ Record Type 10 (hash table)
     This record is a hashtable of 256 entries; a special property
     is that all these records are stored consecutively to make one
     big table. The hash value is simple the 1st, 2nd, ... byte of
-    the keyid (depending on the indirection level).
+    the fingerprint (depending on the indirection level).
 
      1 byte value 10
      1 byte reserved
-     n u32  recnum; n depends on th record length:
+     n u32  recnum; n depends on the record length:
            n = (reclen-2)/4  which yields 9 for the current record length
            of 40 bytes.
 
@@ -206,18 +201,15 @@ Record Type 10 (hash table)
         m = (256+n-1) / n
     which is 29 for a record length of 40.
 
-    To look up a key we use its lsb to get the recnum from this
-    hashtable and look up the addressed record:
-       - If this record is another hashtable, we use 2nd lsb
+    To look up a key we use the first byte of the fingerprint to get
+    the recnum from this hashtable and look up the addressed record:
+       - If this record is another hashtable, we use 2nd byte
         to index this hast table and so on.
-       - if this record is a hashlist, we walk thru the
-        reclist records until we found one whose hash field
-        matches the MSB of our keyid, and lookup this record
-       - if this record is a dir record, we compare the
-        keyid and if this is correct, we get the keyrecod and compare
-        the fingerprint to decide whether it is the requested key;
-        if this is not the correct dir record, we look at the next
-        dir record which is linked by the link field.
+       - if this record is a hashlist, we walk all entries
+        until we found one a matching one.
+       - if this record is a key record, we compare the
+        fingerprint and to decide whether it is the requested key;
+
 
 Record type 11 (hash list)
 --------------
@@ -226,11 +218,10 @@ Record type 11 (hash list)
     1 byte value 11
     1 byte reserved
     1 u32  next         next hash list record
-    n times             n = (reclen-6)/5
-       1 byte hash
+    n times             n = (reclen-5)/5
        1 u32  recnum
 
-    For the current record length of 40, n is 6
+    For the current record length of 40, n is 7
 
 
 
index 1fde885..5be04b6 100644 (file)
@@ -5,6 +5,7 @@ gpg - GNU Privacy Guard
 =head1 SYNOPSIS
 
 B<gpg> [--homedir name] [--options file] [options] command [args]
+
 B<gpgm> [--homedir name] [--options file] [options] command [args]
 
 =head1 DESCRIPTION
@@ -98,23 +99,46 @@ B<--gen-key>
     Generate a new key pair. This command can only be
     used interactive.
 
-B<--add-key> I<name>
-    Add a subkey to an already existing key. This
-    command is similiar to B<--gen-key> but a primary
-    key must already exit.
-
-B<--sign-key> I<name>
-    Make a signature on key of user I<name>.
-    This looks for the key, displays the key and checks
-    all existing signatures of this key. If the key is
-    not yet signed by the default user (or the users given
-    with B<-u>), the program displays the information of
-    the key again, together with its fingerprint and
-    asks whether it should be signed. This question
-    is repeated for all users specified with B<-u>.
-    The key is then signed and the keyring which
-    contains the key is updated.
 
+B<--edit-key> I<name>
+    Present a menu which enables you to do all key
+    related tasks:
+    B<sign>
+      Make a signature on key of user I<name>.
+      If the key is not yet signed by the default
+      user (or the users given with B<-u>), the
+      program displays the information of the key
+      again, together with its fingerprint and
+      asks whether it should be signed. This
+      question is repeated for all users specified
+      with B<-u>.
+    B<adduid>
+      Create an alternate user id.
+    B<deluid>
+      Delete an user id.
+    B<addkey>
+       Add a subkey to this key.
+    B<delkey>
+       Remove a subkey.
+    B<passwd>
+       Change the passphrase of the secret key.
+    B<check>
+       Check signatures
+    B<uid> I<n>
+       Toggle selection of user id with index I<n>.
+       Use 0 to deselect all.
+    B<key> I<n>
+       Toggle selection of subkey with index I<n>.
+       Use 0 to deselect all.
+    B<check>
+       Check all selected user ids.
+    B<toggle>
+       Toggle between public and secret key listing.
+    B<save>
+       Save all changes to the key rings and quit.
+    B<quit>
+       Quit the program without updating the
+       key rings.
 
 B<--delete-key>
     Remove key from the public keyring
@@ -122,12 +146,6 @@ B<--delete-key>
 B<--delete-secret-key>
     Remove key from the secret and public keyring
 
-B<--edit-key>
-    Edit/remove a key signature.
-
-B<--change-passphrase>
-    Change the passphrase of your secret keyring
-
 B<--gen-revoke>
     Generate a revocation certificate.
 
@@ -142,10 +160,15 @@ B<--export> [I<names>]
 B<--import>
     import/merge keys
 
-B<--list-ownertrust>
+B<--export-ownertrust>
     List the assigned ownertrust values in ascii format for
     backup purposes [B<gpgm> only].
 
+B<--import-ownertrust> [I<filename>]
+    Update the trustdb with the ownertrust values stored in
+    I<filename> (or stdin if not given); existing values will be
+    overwritten. [B<gpgm> only].
+
 =head1 OPTIONS
 
 Long options can be put in an options file (default F<~/.gnupg/options>);
index badb5cd..39e528d 100644 (file)
@@ -1,3 +1,26 @@
+Wed Jul 29 12:53:03 1998  Werner Koch  (wk@(none))
+
+       * free-packet.c (copy_signature): New.
+
+       * keygen.c (generate_subkeypair): rewritten
+       * g10.c (aKeyadd): Removed option --add-key
+
+Mon Jul 27 10:37:28 1998  Werner Koch  (wk@(none))
+
+       * seckey-cert.c (do_check): Additional check on cipher blocksize.
+       (protect_secret_key): Ditto.
+       * encr-data.c: Support for other blocksizes.
+       * cipher.c (write_header): Ditto.
+
+Fri Jul 24 16:47:59 1998  Werner Koch  (wk@(none))
+
+       * kbnode.c (insert_kbnode): Changed semantics and all callers.
+       * keyedit.c : More or less a complete rewrite
+
+Wed Jul 22 17:10:04 1998  Werner Koch  (wk@(none))
+
+       * build-packet.c (write_sign_packet_header): New.
+
 Tue Jul 21 14:37:09 1998  Werner Koch  (wk@(none))
 
        * import.c (import_one): Now creates a trustdb record.
index e97fb8c..0097ace 100644 (file)
@@ -48,7 +48,6 @@ common_source =  \
              status.c          \
              status.h          \
              sign.c            \
-             keyedit.c         \
              plaintext.c       \
              encr-data.c       \
              encode.c          \
@@ -61,6 +60,7 @@ gpg_SOURCES  = g10.c          \
              $(common_source)  \
              verify.c          \
              decrypt.c         \
+             keyedit.c         \
              keygen.c
 
 
index aa8b46d..4def8e6 100644 (file)
@@ -57,3 +57,9 @@ compress-sigs
 # Normally, compressing of signatures does not make sense; so this
 # is disabled for detached signatures unless this option is used.
 
+
+emulate-pgp-sign-bug
+# PGP 2.x can only cope with 2 byte length headers of the
+# signature packets, this option forces.
+
+
index ae5e08f..c7ed8fa 100644 (file)
@@ -51,6 +51,7 @@ static int calc_header_length( u32 len );
 static int write_16(IOBUF inp, u16 a);
 static int write_32(IOBUF inp, u32 a);
 static int write_header( IOBUF out, int ctb, u32 len );
+static int write_sign_packet_header( IOBUF out, int ctb, u32 len );
 static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen, int blkmode );
 static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen );
 static int write_version( IOBUF out, int ctb );
@@ -669,7 +670,10 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig )
     for(i=0; i < n; i++ )
        mpi_write(a, sig->data[i] );
 
-    write_header(out, ctb, iobuf_get_temp_length(a) );
+    if( is_RSA(sig->pubkey_algo) && sig->version < 4 )
+       write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) );
+    else
+       write_header(out, ctb, iobuf_get_temp_length(a) );
     if( iobuf_write_temp( out, a ) )
        rc = G10ERR_WRITE_FILE;
 
@@ -747,6 +751,18 @@ write_header( IOBUF out, int ctb, u32 len )
     return write_header2( out, ctb, len, 0, 1 );
 }
 
+
+static int
+write_sign_packet_header( IOBUF out, int ctb, u32 len )
+{
+    /* work around a bug in the pgp read function for signature packets,
+     * which are not correctly coded and silently assume at some
+     * point 2 byte length headers.*/
+    iobuf_put(out, 0x89 );
+    iobuf_put(out, len >> 8 );
+    return iobuf_put(out, len ) == -1 ? -1:0;
+}
+
 /****************
  * if HDRLEN is > 0, try to build a header of this length.
  * we need this, so that we can hash packets without reading them again.
index 0891d52..4c4e85f 100644 (file)
@@ -42,7 +42,8 @@ write_header( cipher_filter_context_t *cfx, IOBUF a )
 {
     PACKET pkt;
     PKT_encrypted ed;
-    byte temp[10];
+    byte temp[18];
+    unsigned blocksize;
 
     memset( &ed, 0, sizeof ed );
     ed.len = cfx->datalen;
@@ -52,15 +53,18 @@ write_header( cipher_filter_context_t *cfx, IOBUF a )
     pkt.pkt.encrypted = &ed;
     if( build_packet( a, &pkt ))
        log_bug("build_packet(ENCR_DATA) failed\n");
-    randomize_buffer( temp, 8, 1 );
-    temp[8] = temp[6];
-    temp[9] = temp[7];
+    blocksize = cipher_get_blocksize( cfx->dek->algo );
+    if( blocksize < 8 || blocksize > 16 )
+       log_fatal("unsupported blocksize %u\n", blocksize );
+    randomize_buffer( temp, blocksize, 1 );
+    temp[blocksize] = temp[blocksize-2];
+    temp[blocksize+1] = temp[blocksize-1];
     cfx->cipher_hd = cipher_open( cfx->dek->algo, CIPHER_MODE_AUTO_CFB, 1 );
     cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen );
     cipher_setiv( cfx->cipher_hd, NULL );
-    cipher_encrypt( cfx->cipher_hd, temp, temp, 10);
+    cipher_encrypt( cfx->cipher_hd, temp, temp, blocksize+2);
     cipher_sync( cfx->cipher_hd );
-    iobuf_write(a, temp, 10);
+    iobuf_write(a, temp, blocksize+2);
     cfx->header=1;
 }
 
index e7e5640..b5eb0e0 100644 (file)
@@ -49,7 +49,8 @@ decrypt_data( PKT_encrypted *ed, DEK *dek )
     decode_filter_ctx_t dfx;
     byte *p;
     int rc, c, i;
-    byte temp[16];
+    byte temp[32];
+    unsigned blocksize;
 
     if( opt.verbose ) {
        const char *s = cipher_algo_to_string( dek->algo );
@@ -60,7 +61,10 @@ decrypt_data( PKT_encrypted *ed, DEK *dek )
     }
     if( (rc=check_cipher_algo(dek->algo)) )
        return rc;
-    if( ed->len && ed->len < 10 )
+    blocksize = cipher_get_blocksize(dek->algo);
+    if( !blocksize || blocksize > 16 )
+       log_fatal("unsupported blocksize %u\n", blocksize );
+    if( ed->len && ed->len < (blocksize+2) )
        log_bug("Nanu\n");   /* oops: found a bug */
 
     dfx.cipher_hd = cipher_open( dek->algo, CIPHER_MODE_AUTO_CFB, 1 );
@@ -70,20 +74,20 @@ decrypt_data( PKT_encrypted *ed, DEK *dek )
     if( ed->len ) {
        iobuf_set_limit( ed->buf, ed->len );
 
-       for(i=0; i < 10 && ed->len; i++, ed->len-- )
+       for(i=0; i < (blocksize+2) && ed->len; i++, ed->len-- )
            temp[i] = iobuf_get(ed->buf);
     }
     else {
-       for(i=0; i < 10; i++ )
+       for(i=0; i < (blocksize+2); i++ )
            if( (c=iobuf_get(ed->buf)) == -1 )
                break;
            else
                temp[i] = c;
     }
-    cipher_decrypt( dfx.cipher_hd, temp, temp, 10);
+    cipher_decrypt( dfx.cipher_hd, temp, temp, blocksize+2);
     cipher_sync( dfx.cipher_hd );
     p = temp;
-    if( p[6] != p[8] || p[7] != p[9] ) {
+    if( p[blocksize-2] != p[blocksize] || p[blocksize-1] != p[blocksize+1] ) {
        cipher_close(dfx.cipher_hd);
        return G10ERR_BAD_KEY;
     }
index 9d623ec..14a4665 100644 (file)
@@ -55,7 +55,7 @@ void
 free_seckey_enc( PKT_signature *sig )
 {
     int n, i;
-    n = pubkey_get_nenc( sig->pubkey_algo );
+    n = pubkey_get_nsig( sig->pubkey_algo );
     if( !n ) {
        m_free(sig->data[0]);
        sig->data[0] = NULL;
@@ -107,6 +107,20 @@ cp_fake_data( MPI a )
     return d;
 }
 
+static void *
+cp_data_block( byte *s )
+{
+    byte *d;
+    u16 len;
+
+    if( !s )
+       return NULL;
+    len = (s[0] << 8) | s[1];
+    d = m_alloc( len+2 );
+    memcpy(d, s, len+2);
+    return d;
+}
+
 
 PKT_public_key *
 copy_public_key( PKT_public_key *d, PKT_public_key *s )
@@ -126,6 +140,39 @@ copy_public_key( PKT_public_key *d, PKT_public_key *s )
     return d;
 }
 
+
+PKT_signature *
+copy_signature( PKT_signature *d, PKT_signature *s )
+{
+    int n, i;
+
+    if( !d )
+       d = m_alloc(sizeof *d);
+    memcpy( d, s, sizeof *d );
+    n = pubkey_get_nsig( s->pubkey_algo );
+    if( !n )
+       d->data[0] = cp_fake_data(s->data[0]);
+    else {
+       for(i=0; i < n; i++ )
+           d->data[i] = mpi_copy( s->data[i] );
+    }
+    d->hashed_data = cp_data_block(s->hashed_data);
+    d->unhashed_data = cp_data_block(s->unhashed_data);
+    return d;
+}
+
+
+PKT_user_id *
+copy_user_id( PKT_user_id *d, PKT_user_id *s )
+{
+    if( !d )
+       d = m_alloc(sizeof *d + s->len - 1 );
+    memcpy( d, s, sizeof *d + s->len - 1 );
+    return d;
+}
+
+
+
 void
 release_secret_key_parts( PKT_secret_key *sk )
 {
index dbd277d..c7d8dfc 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -68,11 +68,8 @@ static ARGPARSE_OPTS opts[] = {
     { 558, "list-secret-keys", 256, N_("list secret keys")},
   #ifdef IS_G10
     { 503, "gen-key",   256, N_("generate a new key pair")},
-    { 554, "add-key",   256, N_("add a subkey to a key pair")},
-    { 506, "sign-key"  ,256, N_("make a signature on a key in the keyring")},
     { 505, "delete-key",256, N_("remove key from the public keyring")},
-    { 524, "edit-key"  ,256, N_("edit a key signature")},
-    { 525, "change-passphrase", 256, N_("change the passphrase of your secret keyring")},
+    { 524, "edit-key"  ,256, N_("sign or edit a key")},
     { 542, "gen-revoke",256, N_("generate a revocation certificate")},
   #endif
     { 537, "export"          , 256, N_("export keys") },
@@ -81,7 +78,8 @@ static ARGPARSE_OPTS opts[] = {
     { 530, "import",      256     , N_("import/merge keys")},
     { 521, "list-packets",256,N_("list only the sequence of packets")},
   #ifdef IS_G10MAINT
-    { 564, "list-ownertrust", 256, N_("list the ownertrust values")},
+    { 564, "export-ownertrust", 256, N_("export the ownertrust values")},
+    { 525, "import-ownertrust", 256 , N_("import ownertrust values")},
     { 567, "check-trustdb",0 , N_("|[NAMES]|check the trust database")},
     { 546, "dearmor", 256, N_("De-Armor a file or stdin") },
     { 547, "enarmor", 256, N_("En-Armor a file or stdin") },
@@ -153,6 +151,7 @@ static ARGPARSE_OPTS opts[] = {
     { 504, "delete-secret-key",0, "@" },
     { 524, "edit-sig"  ,0, "@"}, /* alias for edit-key */
     { 523, "passphrase-fd",1, "@" },
+    { 506, "sign-key"  ,256, "@" }, /* alias for edit-key */
   #endif
     { 532, "quick-random", 0, "@"},
     { 526, "no-verbose", 0, "@"},
@@ -173,18 +172,18 @@ static ARGPARSE_OPTS opts[] = {
     { 566, "compress-sigs",0, "@"},
     { 559, "always-trust", 0, "@"},
     { 562, "emulate-checksum-bug", 0, "@"},
-
+   /*554 is unused */
 {0} };
 
 
 enum cmd_values { aNull = 0,
     aSym, aStore, aEncr, aKeygen, aSign, aSignEncr,
     aSignKey, aClearsign, aListPackets, aEditKey, aDeleteKey, aDeleteSecretKey,
-    aKMode, aKModeC, aChangePass, aImport, aVerify, aDecrypt, aListKeys,
-    aListSigs, aKeyadd, aListSecretKeys,
-    aExport, aExportSecret,
+    aKMode, aKModeC,  aImport, aVerify, aDecrypt, aListKeys,
+    aListSigs, aListSecretKeys, aExport, aExportSecret,
     aCheckKeys, aGenRevoke, aPrimegen, aPrintMD, aPrintMDs,
-    aCheckTrustDB, aListTrustDB, aListTrustPath, aListOwnerTrust,
+    aCheckTrustDB, aListTrustDB, aListTrustPath,
+    aExportOwnerTrust, aImportOwnerTrust,
     aDeArmor, aEnArmor, aGenRandom,
 aTest };
 
@@ -521,7 +520,6 @@ main( int argc, char **argv )
          case 507: set_cmd( &cmd, aStore); break;
          case 523: set_passphrase_fd( pargs.r.ret_int ); break;
          case 524: set_cmd( &cmd, aEditKey); break;
-         case 525: set_cmd( &cmd, aChangePass); break;
          case 527: def_cipher_string = m_strdup(pargs.r.ret_str); break;
          case 529: def_digest_string = m_strdup(pargs.r.ret_str); break;
          case 539: set_cmd( &cmd, aClearsign); break;
@@ -548,7 +546,8 @@ main( int argc, char **argv )
          case 546: set_cmd( &cmd, aDeArmor); break;
          case 547: set_cmd( &cmd, aEnArmor); break;
          case 555: set_cmd( &cmd, aPrintMD); break;
-         case 564: set_cmd( &cmd, aListOwnerTrust); break;
+         case 564: set_cmd( &cmd, aExportOwnerTrust); break;
+         case 525: set_cmd( &cmd, aImportOwnerTrust); break;
        #endif /* IS_G10MAINT */
 
          case 'o': opt.outfile = pargs.r.ret_str; break;
@@ -564,7 +563,7 @@ main( int argc, char **argv )
          case 510: opt.debug |= pargs.r.ret_ulong; break;
          case 511: opt.debug = ~0; break;
          case 512: set_status_fd( pargs.r.ret_int ); break;
-         case 515: opt.fingerprint = 1; break;
+         case 515: opt.fingerprint++; break;
          case 517: append_to_strlist( &sec_nrings, pargs.r.ret_str); break;
          case 518:
            /* config files may not be nested (silently ignore them) */
@@ -595,17 +594,17 @@ main( int argc, char **argv )
          case 551: set_cmd( &cmd, aListKeys); break;
          case 552: set_cmd( &cmd, aListSigs); break;
          case 553: opt.skip_verify=1; break;
-         case 554: set_cmd( &cmd, aKeyadd); break;
          case 556: opt.def_compress_algo = pargs.r.ret_int; break;
          case 557: opt.compress_keys = 1; break;
          case 558: set_cmd( &cmd, aListSecretKeys); break;
          case 559: opt.always_trust = 1; break;
          case 560: register_cipher_extension(pargs.r.ret_str); break;
-         case 561: opt.rfc1991 = 1; break;
-         case 562: opt.emulate_bugs |= 1; break;
+         case 561: opt.rfc1991 = 1; opt.no_comment = 1; break;
+         case 562: opt.emulate_bugs |= EMUBUG_GPGCHKSUM; break;
          case 563: set_cmd( &cmd, aExportSecret); break;
          case 565: opt.do_not_export_rsa = 1; break;
          case 566: opt.compress_sigs = 1; break;
+         case 554:
          default : errors++; pargs.err = configfp? 1:2; break;
        }
     }
@@ -722,7 +721,7 @@ main( int argc, char **argv )
        if( opt.with_colons ) /* need this to list the trust */
            rc = init_trustdb(1, trustdb_name );
        break;
-      case aListOwnerTrust: rc = init_trustdb( 0, trustdb_name ); break;
+      case aExportOwnerTrust: rc = init_trustdb( 0, trustdb_name ); break;
       case aListTrustDB: rc = init_trustdb( argc? 1:0, trustdb_name ); break;
       default: rc = init_trustdb(1, trustdb_name ); break;
     }
@@ -808,19 +807,10 @@ main( int argc, char **argv )
 
 
       case aSignKey: /* sign the key given as argument */
-       if( argc != 1 )
-           wrong_args(_("--sign-key username"));
-       /* note: fname is the user id! */
-       if( (rc = sign_key(fname, locusr)) )
-           log_error("%s: sign key failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) );
-       break;
-
       case aEditKey: /* Edit a key signature */
        if( argc != 1 )
            wrong_args(_("--edit-key username"));
-       /* note: fname is the user id! */
-       if( (rc = edit_keysigs(fname)) )
-           log_error("%s: edit signature failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) );
+       keyedit_menu(fname, locusr );
        break;
 
       case aDeleteSecretKey:
@@ -834,14 +824,6 @@ main( int argc, char **argv )
            log_error("%s: delete key failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) );
        break;
 
-      case aChangePass: /* Change the passphrase */
-       if( argc > 1 ) /* no arg: use default, 1 arg use this one */
-           wrong_args(_("--change-passphrase [username]"));
-       /* note: fname is the user id! */
-       if( (rc = change_passphrase(fname)) )
-           log_error("%s: change passphrase failed: %s\n", print_fname_stdin(fname),
-                                                      g10_errstr(rc) );
-       break;
       #endif /* IS_G10 */
 
       case aCheckKeys:
@@ -880,11 +862,6 @@ main( int argc, char **argv )
            wrong_args("--gen-key");
        generate_keypair();
        break;
-      case aKeyadd: /* add a subkey (interactive) */
-       if( argc != 1 )
-           wrong_args("--add-key userid");
-       generate_subkeypair(*argv);
-       break;
     #endif
 
       case aImport:
@@ -1049,10 +1026,16 @@ main( int argc, char **argv )
        list_trust_path( atoi(*argv), argv[1] );
        break;
 
-      case aListOwnerTrust:
+      case aExportOwnerTrust:
        if( argc )
-           wrong_args("--list-ownertrust");
-       list_ownertrust();
+           wrong_args("--export-ownertrust");
+       export_ownertrust();
+       break;
+
+      case aImportOwnerTrust:
+       if( argc > 1 )
+           wrong_args("--import-ownertrust [file]");
+       import_ownertrust( argc? *argv:NULL );
        break;
 
      #endif /* IS_G10MAINT */
index ec143dc..c5eaf9b 100644 (file)
@@ -851,7 +851,7 @@ merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
         * We add a clone to the original keyblock, because this
         * one is released first */
        n2 = clone_kbnode(n);
-       insert_kbnode( dst, n2, PKT_USER_ID );
+       insert_kbnode( dst, n2, PKT_SIGNATURE );
        n2->flag |= 1;
        n->flag |= 1;
        ++*n_sigs;
index 4578f82..4287681 100644 (file)
@@ -94,7 +94,8 @@ add_kbnode( KBNODE root, KBNODE node )
 }
 
 /****************
- * Insert NODE into the list after root but before a packet with type PKTTYPE
+ * Insert NODE into the list after root but before a packet which is not of
+ * type PKTTYPE
  * (only if PKTTYPE != 0)
  */
 void
@@ -108,7 +109,7 @@ insert_kbnode( KBNODE root, KBNODE node, int pkttype )
        KBNODE n1;
 
        for(n1=root; n1->next;  n1 = n1->next)
-           if( pkttype == n1->next->pkt->pkttype ) {
+           if( pkttype != n1->next->pkt->pkttype ) {
                node->next = n1->next;
                n1->next = node;
                return;
index bbd803f..85ab88b 100644 (file)
@@ -133,6 +133,8 @@ unsigned nbits_from_sk( PKT_secret_key *sk );
 const char *datestr_from_pk( PKT_public_key *pk );
 const char *datestr_from_sk( PKT_secret_key *sk );
 const char *datestr_from_sig( PKT_signature *sig );
+const char *expirestr_from_pk( PKT_public_key *pk );
+const char *expirestr_from_sk( PKT_secret_key *sk );
 byte *fingerprint_from_sk( PKT_secret_key *sk, byte *buf, size_t *ret_len );
 byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len );
 
@@ -149,6 +151,7 @@ KBNODE find_kbnode( KBNODE node, int pkttype );
 KBNODE walk_kbnode( KBNODE root, KBNODE *context, int all );
 void clear_kbnode_flags( KBNODE n );
 int  commit_kbnode( KBNODE *root );
+void dump_kbnode( KBNODE node );
 
 /*-- ringedit.c --*/
 int add_keyblock_resource( const char *filename, int force, int secret );
index 4369e9a..7377232 100644 (file)
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <ctype.h>
 
 #include "options.h"
 #include "packet.h"
 #include "ttyio.h"
 #include "i18n.h"
 
+static void show_key_with_all_names( KBNODE keyblock,
+               int only_marked, int with_fpr, int with_subkeys );
+static void show_key_and_fingerprint( KBNODE keyblock );
+static void show_fingerprint( PKT_public_key *pk );
+static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock );
+static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_select_uid( KBNODE keyblock, int index );
+static int menu_select_key( KBNODE keyblock, int index );
+static int count_uids( KBNODE keyblock );
+static int count_uids_with_flag( KBNODE keyblock, unsigned flag );
+static int count_keys_with_flag( KBNODE keyblock, unsigned flag );
+static int count_selected_uids( KBNODE keyblock );
+static int count_selected_keys( KBNODE keyblock );
 
 
-static void
-show_fingerprint( PKT_public_key *pk )
-{
-    byte *array, *p;
-    size_t i, n;
 
-    p = array = fingerprint_from_pk( pk, NULL, &n );
-    tty_printf("             Fingerprint:");
-    if( n == 20 ) {
-       for(i=0; i < n ; i++, i++, p += 2 ) {
-           if( i == 10 )
-               tty_printf(" ");
-           tty_printf(" %02X%02X", *p, p[1] );
-       }
-    }
-    else {
-       for(i=0; i < n ; i++, p++ ) {
-           if( i && !(i%8) )
-               tty_printf(" ");
-           tty_printf(" %02X", *p );
-       }
-    }
-    tty_printf("\n");
-    m_free(array);
-}
+#define NODFLG_BADSIG (1<<0)  /* bad signature */
+#define NODFLG_NOKEY  (1<<1)  /* no public key */
+#define NODFLG_SIGERR (1<<2)  /* other sig error */
+
+#define NODFLG_MARK_A (1<<4)  /* temporary mark */
+
+#define NODFLG_SELUID (1<<8)  /* indicate the selected userid */
+#define NODFLG_SELKEY (1<<9)  /* indicate the selected key */
 
 
-/****************
- * Ask whether the user is willing to sign the key. Return true if so.
- */
 static int
-sign_it_p( PKT_public_key *pk, PKT_user_id *uid )
+get_keyblock_byname( KBNODE *keyblock, KBPOS *kbpos, const char *username )
 {
-    char *answer;
-    int yes;
+    int rc;
 
-    tty_printf("\n");
-    tty_printf(_("Are you really sure that you want to sign this key:\n\n"));
-    tty_printf("pub  %4u%c/%08lX %s ",
-             nbits_from_pk( pk ),
-             pubkey_letter( pk->pubkey_algo ),
-             (ulong)keyid_from_pk( pk, NULL ),
-             datestr_from_pk( pk )               );
-    tty_print_string( uid->name, uid->len );
-    tty_printf("\n");
-    show_fingerprint(pk);
-    tty_printf("\n");
-    answer = tty_get(_("Sign this key? "));
-    tty_kill_prompt();
-    yes = answer_is_yes(answer);
-    m_free(answer);
-    return yes;
+    *keyblock = NULL;
+    /* search the userid */
+    rc = find_keyblock_byname( kbpos, username );
+    if( rc ) {
+       log_error(_("%s: user not found\n"), username );
+       return rc;
+    }
+
+    /* read the keyblock */
+    rc = read_keyblock( kbpos, keyblock );
+    if( rc )
+       log_error("%s: keyblock read problem: %s\n", username, g10_errstr(rc));
+    return rc;
 }
 
 
 /****************
  * Check the keysigs and set the flags to indicate errors.
- * Usage of nodes flag bits:
- * Bit 0 = bad signature
- *     1 = no public key
- *     2 = other error
  * Returns true if error found.
  */
 static int
-check_all_keysigs( KBNODE keyblock )
+check_all_keysigs( KBNODE keyblock, int only_selected )
 {
     KBNODE kbctx;
     KBNODE node;
@@ -111,337 +99,217 @@ check_all_keysigs( KBNODE keyblock )
     int inv_sigs = 0;
     int no_key = 0;
     int oth_err = 0;
+    int has_selfsig = 0;
+    int mis_selfsig = 0;
+    int selected = !only_selected;
+    int anyuid = 0;
 
     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-       if( node->pkt->pkttype == PKT_SIGNATURE
-           && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
-           PKT_signature *sig = node->pkt->pkt.signature;
-           int sigrc;
-
-           tty_printf("sig");
-           switch( (rc = check_key_signature( keyblock, node,NULL)) ) {
-             case 0:                node->flag = 0; sigrc = '!'; break;
-             case G10ERR_BAD_SIGN:  inv_sigs++; node->flag = 1; sigrc = '-'; break;
-             case G10ERR_NO_PUBKEY: no_key++;   node->flag = 2; sigrc = '?'; break;
-             default:               oth_err++;  node->flag = 4; sigrc = '%'; break;
-           }
-           tty_printf("%c       %08lX %s   ",
-                   sigrc, sig->keyid[1], datestr_from_sig(sig));
-           if( sigrc == '%' )
-               tty_printf("[%s] ", g10_errstr(rc) );
-           else if( sigrc == '?' )
-               ;
-           else {
-               size_t n;
-               char *p = get_user_id( sig->keyid, &n );
-               tty_print_string( p, n > 40? 40 : n );
-               m_free(p);
+       if( node->pkt->pkttype == PKT_USER_ID ) {
+           PKT_user_id *uid = node->pkt->pkt.user_id;
+
+           if( only_selected )
+               selected = (node->flag & NODFLG_SELUID);
+           if( selected ) {
+               tty_printf("uid  ");
+               tty_print_string( uid->name, uid->len );
+               tty_printf("\n");
+               if( anyuid && !has_selfsig )
+                   mis_selfsig++;
+               has_selfsig = 0;
+               anyuid = 1;
            }
-           tty_printf("\n");
-           /* FIXME: update the trustdb */
        }
-    }
-    if( inv_sigs )
-       tty_printf(_("%d bad signatures\n"), inv_sigs );
-    if( no_key )
-       tty_printf(_("No public key for %d signatures\n"), no_key );
-    if( oth_err )
-       tty_printf(_("%d signatures not checked due to errors\n"), oth_err );
-    return inv_sigs || no_key || oth_err;
-}
-
-
-/****************
- * Ask and remove invalid signatures that are to be removed.
- */
-static int
-remove_keysigs( KBNODE keyblock, u32 *keyid, int all )
-{
-    KBNODE kbctx;
-    KBNODE node;
-    char *answer;
-    int yes;
-    int count;
-
-    count = 0;
-    for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-       if( ((node->flag & 7) || all )
-           && node->pkt->pkttype == PKT_SIGNATURE
-           && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
+       else if( selected && node->pkt->pkttype == PKT_SIGNATURE
+                && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
            PKT_signature *sig = node->pkt->pkt.signature;
+           int sigrc, selfsig;
 
-           tty_printf("\n \"%08lX %s   ",
-                       sig->keyid[1], datestr_from_sig(sig));
-           if( node->flag & 6 )
-               tty_printf(_("[User name not available] "));
-           else {
-               size_t n;
-               char *p = get_user_id( sig->keyid, &n );
-               tty_print_string( p, n );
-               m_free(p);
-           }
-           tty_printf("\"\n");
-           if( node->flag & 1 )
-               tty_printf(_("This is a BAD signature!\n"));
-           else if( node->flag & 2 )
-               tty_printf(_("Public key not available.\n"));
-           else if( node->flag & 4 )
-               tty_printf(_("The signature could not be checked!\n"));
-
-           if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
-               tty_printf(_("Skipped self-signature\n"));
-               continue; /* do not remove self-signatures */
+           switch( (rc = check_key_signature( keyblock, node, &selfsig)) ) {
+             case 0:
+               node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR);
+               sigrc = '!';
+               break;
+             case G10ERR_BAD_SIGN:
+               node->flag = NODFLG_BADSIG;
+               sigrc = '-';
+               inv_sigs++;
+               break;
+             case G10ERR_NO_PUBKEY:
+               node->flag = NODFLG_NOKEY;
+               sigrc = '?';
+               no_key++;
+               break;
+             default:
+               node->flag = NODFLG_SIGERR;
+               sigrc = '%';
+               oth_err++;
+               break;
            }
-
-           tty_printf("\n");
-           answer = tty_get(_("Remove this signature? "));
-           tty_kill_prompt();
-           if( answer_is_yes(answer) ) {
-               node->flag |= 128;     /* use bit 7 to mark this node */
-               count++;
+           if( sigrc != '?' ) {
+               tty_printf("sig%c       %08lX %s   ",
+                       sigrc, sig->keyid[1], datestr_from_sig(sig));
+               if( sigrc == '%' )
+                   tty_printf("[%s] ", g10_errstr(rc) );
+               else if( sigrc == '?' )
+                   ;
+               else if( selfsig ) {
+                   tty_printf( _("[self-signature]") );
+                   if( sigrc == '!' )
+                       has_selfsig = 1;
+               }
+               else {
+                   size_t n;
+                   char *p = get_user_id( sig->keyid, &n );
+                   tty_print_string( p, n > 40? 40 : n );
+                   m_free(p);
+               }
+               tty_printf("\n");
+               /* fixme: Should we update the trustdb here */
            }
-           m_free(answer);
        }
     }
+    if( !has_selfsig )
+       mis_selfsig++;
+    if( inv_sigs == 1 )
+       tty_printf(_("1 bad signature\n"), inv_sigs );
+    else if( inv_sigs )
+       tty_printf(_("%d bad signatures\n"), inv_sigs );
+    if( no_key == 1 )
+       tty_printf(_("1 signature not checked due to a missing key\n") );
+    else if( no_key )
+       tty_printf(_("%d signatures not checked due to missing keys\n"), no_key );
+    if( oth_err == 1 )
+       tty_printf(_("1 signature not checked due to an error\n") );
+    else if( oth_err )
+       tty_printf(_("%d signatures not checked due to errors\n"), oth_err );
+    if( mis_selfsig == 1 )
+       tty_printf(_("1 user id without valid self-signature detected\n"));
+    else if( mis_selfsig  )
+       tty_printf(_("%d user ids without valid self-signatures detected\n"),
+                                                                   mis_selfsig);
 
-    if( !count )
-       return 0; /* nothing to remove */
-    answer = tty_get(_("Do you really want to remove the selected signatures? "));
-    tty_kill_prompt();
-    yes = answer_is_yes(answer);
-    m_free(answer);
-    if( !yes )
-       return 0;
-
-    for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 1)) ; ) {
-       if( node->flag & 128)
-           delete_kbnode(node );
-    }
-
-    return 1;
+    return inv_sigs || no_key || oth_err || mis_selfsig;
 }
 
 
+
 /****************
- * This function signs the key of USERNAME with all users listed in
- * LOCUSR. If LOCUSR is NULL the default secret certificate will
- * be used.  This works on all keyrings, so there is no armor or
- * compress stuff here.
+ * Loop over all locusr and and sign the uids after asking.
+ * If no user id is marked, all user ids will be signed;
+ * if some user_ids are marked those will be signed.
+ *
+ * fixme: Add support for our proposed sign-all scheme
  */
-int
-sign_key( const char *username, STRLIST locusr )
+static int
+sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
 {
-    md_filter_context_t mfx;
     int rc = 0;
     SK_LIST sk_list = NULL;
     SK_LIST sk_rover = NULL;
-    KBNODE keyblock = NULL;
-    KBNODE kbctx, node;
-    KBPOS kbpos;
-    PKT_public_key *pk;
-    u32 pk_keyid[2];
-    char *answer;
-
-    memset( &mfx, 0, sizeof mfx);
-
-    /* search the userid */
-    rc = find_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error(_("%s: user not found\n"), username );
-       goto leave;
-    }
+    KBNODE node, uidnode;
+    PKT_public_key *primary_pk;
+    int select_all = !count_selected_uids(keyblock);
 
     /* build a list of all signators */
     rc=build_sk_list( locusr, &sk_list, 0, 1 );
     if( rc )
        goto leave;
 
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("error reading the certificate: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; public key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
-
-    pk = node->pkt->pkt.public_key;
-    keyid_from_pk( pk, pk_keyid );
-    tty_printf(_("Checking signatures of this public key certificate:\n"));
-    tty_printf("pub  %4u%c/%08lX %s   ",
-             nbits_from_pk( pk ),
-             pubkey_letter( pk->pubkey_algo ),
-             pk_keyid[1], datestr_from_pk(pk) );
-    {
+    /* loop over all signaturs */
+    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+       u32 sk_keyid[2];
        size_t n;
-       char *p = get_user_id( pk_keyid, &n );
-       tty_print_string( p, n > 40? 40 : n );
-       m_free(p);
-       tty_printf("\n");
-    }
+       char *p;
 
-    clear_kbnode_flags( keyblock );
-    if( check_all_keysigs( keyblock ) ) {
-       if( !opt.batch ) {
-           /* ask whether we really should do anything */
-           answer = tty_get(
-               _("Do you want to remove some of the invalid signatures? "));
-           tty_kill_prompt();
-           if( answer_is_yes(answer) )
-               remove_keysigs( keyblock, pk_keyid, 0 );
-           m_free(answer);
+       keyid_from_sk( sk_rover->sk, sk_keyid );
+       /* set mark A for all selected user ids */
+       for( node=keyblock; node; node = node->next ) {
+           if( select_all || (node->flag & NODFLG_SELUID) )
+               node->flag |= NODFLG_MARK_A;
+           else
+               node->flag &= ~NODFLG_MARK_A;
        }
-    }
-
-    /* check whether we it is possible to sign this key */
-    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
-       u32 akeyid[2];
-
-       keyid_from_sk( sk_rover->sk, akeyid );
-       for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-           if( node->pkt->pkttype == PKT_USER_ID )
-               sk_rover->mark = 1;
-           else if( node->pkt->pkttype == PKT_SIGNATURE
+       /* reset mark for uids which are already signed */
+       uidnode = NULL;
+       for( node=keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_USER_ID ) {
+               uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
+           }
+           else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE
                && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
-               if( akeyid[0] == node->pkt->pkt.signature->keyid[0]
-                   && akeyid[1] == node->pkt->pkt.signature->keyid[1] ) {
-                   log_info(_("Already signed by keyid %08lX\n"),
-                                                       (ulong)akeyid[1] );
-                   sk_rover->mark = 0;
+               if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
+                   && sk_keyid[1] == node->pkt->pkt.signature->keyid[1] ) {
+                   tty_printf(_("Already signed by key %08lX\n"),
+                                                       (ulong)sk_keyid[1] );
+                   uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */
                }
            }
        }
-    }
-    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
-       if( sk_rover->mark )
-           break;
-    }
-    if( !sk_rover ) {
-       log_info(_("Nothing to sign\n"));
-       goto leave;
-    }
-
-    /* Loop over all signers and all user ids and sign */
-    /* FIXME: we have to change it: Present all user-ids and
-     * then ask whether all those ids shall be signed if the user
-     * answers yes, go and make a 0x14 sign class packet and remove
-     * old one-user-id-only-sigs (user should be noted of this
-     * condition while presenting the user-ids); if he had answered
-     * no, present each user-id in turn and ask which one should be signed
-     * (only one) - if there is already a single-user-sig, do nothing.
-     * (this is propably already out in the world) */
-    for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
-       if( !sk_rover->mark )
+       /* check whether any uids are left for signing */
+       if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) ) {
+           tty_printf(_("Nothing to sign with key %08lX\n"),
+                                                 (ulong)sk_keyid[1] );
            continue;
-       for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
-           if( node->pkt->pkttype == PKT_USER_ID ) {
-               if( sign_it_p( pk, node->pkt->pkt.user_id ) ) {
-                   PACKET *pkt;
-                   PKT_signature *sig;
-
-                   rc = make_keysig_packet( &sig, pk,
-                                                  node->pkt->pkt.user_id,
-                                                  NULL,
-                                                  sk_rover->sk,
-                                                  0x10, 0, NULL, NULL );
-                   if( rc ) {
-                       log_error("make_keysig_packet failed: %s\n", g10_errstr(rc));
-                       goto leave;
-                   }
-
-                   pkt = m_alloc_clear( sizeof *pkt );
-                   pkt->pkttype = PKT_SIGNATURE;
-                   pkt->pkt.signature = sig;
-                   insert_kbnode( node, new_kbnode(pkt), PKT_USER_ID );
+       }
+       /* Ask whether we realy should sign these user id(s) */
+       tty_printf("\n");
+       show_key_with_all_names( keyblock, 1, 1, 0 );
+       tty_printf("\n");
+       tty_printf(_(
+            "Are you really sure that you want to sign this key\n"
+            "with your key: \""));
+       p = get_user_id( sk_keyid, &n );
+       tty_print_string( p, n );
+       tty_printf("\"\n\n");
+       m_free(p);
+       p = tty_get(_("Really sign? "));
+       tty_kill_prompt();
+       if( !answer_is_yes(p) ) {
+           m_free(p);
+           continue; /* No */
+       }
+       m_free(p);
+       /* now we can sign the user ids */
+      reloop: /* (must use this, because we are modifing the list) */
+       primary_pk = NULL;
+       for( node=keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+               primary_pk = node->pkt->pkt.public_key;
+           else if( node->pkt->pkttype == PKT_USER_ID
+                    && (node->flag & NODFLG_MARK_A) ) {
+               PACKET *pkt;
+               PKT_signature *sig;
+
+               assert( primary_pk );
+               node->flag &= ~NODFLG_MARK_A;
+               rc = make_keysig_packet( &sig, primary_pk,
+                                              node->pkt->pkt.user_id,
+                                              NULL,
+                                              sk_rover->sk,
+                                              0x10, 0, NULL, NULL );
+               if( rc ) {
+                   log_error(_("signing failed: %s\n"), g10_errstr(rc));
+                   goto leave;
                }
+               *ret_modified = 1; /* we changed the keyblock */
+
+               pkt = m_alloc_clear( sizeof *pkt );
+               pkt->pkttype = PKT_SIGNATURE;
+               pkt->pkt.signature = sig;
+               insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE );
+               goto reloop;
            }
        }
-    }
-
-    rc = update_keyblock( &kbpos, keyblock );
-    if( rc ) {
-       log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
+    } /* end loop over signators */
 
   leave:
-    release_kbnode( keyblock );
     release_sk_list( sk_list );
-    md_close( mfx.md );
     return rc;
 }
 
 
 
-int
-edit_keysigs( const char *username )
-{
-    int rc = 0;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KBPOS kbpos;
-    PKT_public_key *pk;
-    u32 pk_keyid[2];
-
-    /* search the userid */
-    rc = find_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error(_("%s: user not found\n"), username );
-       goto leave;
-    }
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("%s: certificate read problem: %s\n", username, g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error("Oops; public key not found anymore!\n");
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
-
-    pk = node->pkt->pkt.public_key;
-    keyid_from_pk( pk, pk_keyid );
-    tty_printf(_("Checking signatures of this public key certificate:\n"));
-    tty_printf("pub  %4u%c/%08lX %s   ",
-             nbits_from_pk( pk ),
-             pubkey_letter( pk->pubkey_algo ),
-             pk_keyid[1], datestr_from_pk(pk) );
-    {
-       size_t n;
-       char *p = get_user_id( pk_keyid, &n );
-       tty_print_string( p, n > 40? 40 : n );
-       m_free(p);
-       tty_printf("\n");
-    }
-
-    clear_kbnode_flags( keyblock );
-    check_all_keysigs( keyblock );
-    if( remove_keysigs( keyblock, pk_keyid, 1 ) ) {
-       rc = update_keyblock( &kbpos, keyblock );
-       if( rc ) {
-           log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
-           goto leave;
-       }
-    }
-
-  leave:
-    release_kbnode( keyblock );
-    return rc;
-}
-
 
 /****************
  * Delete a public or secret key from a keyring.
@@ -560,66 +428,26 @@ delete_key( const char *username, int secret )
 }
 
 
-int
-change_passphrase( const char *username )
+/****************
+ * Change the passphrase of the primary and all secondary keys.
+ * We use only one passphrase for all keys.
+ */
+static int
+change_passphrase( KBNODE keyblock )
 {
     int rc = 0;
-    KBNODE keyblock = NULL;
+    int changed=0;
     KBNODE node;
-    KBPOS kbpos;
     PKT_secret_key *sk;
-    u32 keyid[2];
-    char *answer;
-    int changed=0;
     char *passphrase = NULL;
 
-    /* find the userid */
-    rc = find_secret_keyblock_byname( &kbpos, username );
-    if( rc ) {
-       log_error("secret key for user '%s' not found\n", username );
-       goto leave;
-    }
-
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error("error reading the certificate: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* get the keyid from the keyblock */
     node = find_kbnode( keyblock, PKT_SECRET_KEY );
     if( !node ) {
        log_error("Oops; secret key not found anymore!\n");
-       rc = G10ERR_GENERAL;
        goto leave;
     }
-
     sk = node->pkt->pkt.secret_key;
-    keyid_from_sk( sk, keyid );
-    tty_printf("sec  %4u%c/%08lX %s   ",
-             nbits_from_sk( sk ),
-             pubkey_letter( sk->pubkey_algo ),
-             keyid[1], datestr_from_sk(sk) );
-    {
-       size_t n;
-       char *p = get_user_id( keyid, &n );
-       tty_print_string( p, n );
-       m_free(p);
-       tty_printf("\n");
-    }
-    for(node=keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
-           PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-           keyid_from_sk( subsk, keyid );
-           tty_printf("sub  %4u%c/%08lX %s\n",
-                     nbits_from_sk( subsk ),
-                     pubkey_letter( subsk->pubkey_algo ),
-                     keyid[1], datestr_from_sk(subsk) );
-       }
-    }
 
-    clear_kbnode_flags( keyblock );
     switch( is_secret_key_protected( sk ) ) {
       case -1:
        rc = G10ERR_PUBKEY_ALGO;
@@ -636,13 +464,11 @@ change_passphrase( const char *username )
     }
 
     /* unprotect all subkeys (use the supplied passphrase or ask)*/
-    for(node=keyblock; node; node = node->next ) {
+    for(node=keyblock; !rc && node; node = node->next ) {
        if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
            PKT_secret_key *subsk = node->pkt->pkt.secret_key;
            set_next_passphrase( passphrase );
            rc = check_secret_key( subsk );
-           if( rc )
-               break;
        }
     }
 
@@ -666,11 +492,9 @@ change_passphrase( const char *username )
                rc = 0;
                tty_printf(_( "You don't want a passphrase -"
                            " this is probably a *bad* idea!\n\n"));
-               answer = tty_get(_("Do you really want to do this? "));
-               tty_kill_prompt();
-               if( answer_is_yes(answer) )
+               if( tty_get_answer_is_yes(_(
+                               "Do you really want to do this? ")))
                    changed++;
-               m_free(answer);
                break;
            }
            else { /* okay */
@@ -696,128 +520,746 @@ change_passphrase( const char *username )
        m_free(dek);
     }
 
+  leave:
+    m_free( passphrase );
+    set_next_passphrase( NULL );
+    return changed && !rc;
+}
+
+
+
+
+/****************
+ * Menu driven key editor
+ *
+ * Note: to keep track of some selection we use node->mark MARKBIT_xxxx.
+ */
+
+void
+keyedit_menu( const char *username, STRLIST locusr )
+{
+    enum cmdids { cmdNONE = 0,
+          cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
+          cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
+          cmdTOGGLE, cmdSELKEY, cmdPASSWD,
+          cmdNOP };
+    static struct { const char *name;
+                   enum cmdids id;
+                   int need_sk;
+                   const char *desc;
+                 } cmds[] = {
+       { N_("quit")    , cmdQUIT   , 0, N_("quit this menu") },
+       { N_("q")       , cmdQUIT   , 0, NULL   },
+       { N_("save")    , cmdSAVE   , 0, N_("save and quit") },
+       { N_("help")    , cmdHELP   , 0, N_("show this help") },
+       {    "?"        , cmdHELP   , 0, NULL   },
+       { N_("fpr")     , cmdFPR    , 0, N_("show fingerprint") },
+       { N_("list")    , cmdLIST   , 0, N_("list key and user ids") },
+       { N_("l")       , cmdLIST   , 0, NULL   },
+       { N_("uid")     , cmdSELUID , 0, N_("select user id N") },
+       { N_("key")     , cmdSELKEY , 0, N_("select secondary key N") },
+       { N_("check")   , cmdCHECK  , 0, N_("list signatures") },
+       { N_("c")       , cmdCHECK  , 0, NULL },
+       { N_("sign")    , cmdSIGN   , 0, N_("sign the key") },
+       { N_("s")       , cmdSIGN   , 0, NULL },
+       { N_("debug")   , cmdDEBUG  , 0, NULL },
+       { N_("adduid")  , cmdADDUID , 1, N_("add a user id") },
+       { N_("deluid")  , cmdDELUID , 0, N_("delete user id") },
+       { N_("addkey")  , cmdADDKEY , 1, N_("add a secondary key") },
+       { N_("delkey")  , cmdDELKEY , 0, N_("delete a secondary key") },
+       { N_("toggle")  , cmdTOGGLE , 1, N_("toggle between secret "
+                                           "and public key listing") },
+       { N_("t"     )  , cmdTOGGLE , 1, NULL },
+       { N_("passwd")  , cmdPASSWD , 1, N_("change the passphrase") },
+
+    { NULL, cmdNONE } };
+    enum cmdids cmd;
+    int rc = 0;
+    KBNODE keyblock = NULL;
+    KBPOS keyblockpos;
+    KBNODE sec_keyblock = NULL;
+    KBPOS sec_keyblockpos;
+    KBNODE cur_keyblock;
+    char *answer = NULL;
+    int redisplay = 1;
+    int modified = 0;
+    int sec_modified = 0;
+    int toggle;
+
+
+    if( opt.batch ) {
+       log_error(_("can't do that in batch-mode\n"));
+       goto leave;
+    }
 
-    if( changed ) {
-       rc = update_keyblock( &kbpos, keyblock );
+    /* first try to locate it as secret key */
+    rc = find_secret_keyblock_byname( &sec_keyblockpos, username );
+    if( !rc ) {
+       rc = read_keyblock( &sec_keyblockpos, &sec_keyblock );
        if( rc ) {
-           log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
+           log_error("%s: secret keyblock read problem: %s\n",
+                                           username, g10_errstr(rc));
            goto leave;
        }
     }
 
+    /* and now get the public key */
+    rc = get_keyblock_byname( &keyblock, &keyblockpos, username );
+    if( rc )
+       goto leave;
+
+    if( sec_keyblock ) { /* check that they match */
+       /* FIXME: check that they both match */
+       tty_printf(_("Secret key is available.\n"));
+    }
+
+    toggle = 0;
+    cur_keyblock = keyblock;
+    for(;;) { /* main loop */
+       int i, arg_number;
+       char *p;
+
+       tty_printf("\n");
+       if( redisplay ) {
+           show_key_with_all_names( cur_keyblock, 0, 0, 1 );
+           tty_printf("\n");
+           redisplay = 0;
+       }
+       m_free(answer);
+       answer = tty_get(_("Command> "));
+       tty_kill_prompt();
+       trim_spaces(answer);
+
+       arg_number = 0;
+       if( !*answer )
+           cmd = cmdLIST;
+       else if( isdigit( *answer ) ) {
+           cmd = cmdSELUID;
+           arg_number = atoi(answer);
+       }
+       else {
+           if( (p=strchr(answer,' ')) ) {
+               *p++ = 0;
+               trim_spaces(answer);
+               trim_spaces(p);
+               arg_number = atoi(p);
+           }
+
+           for(i=0; cmds[i].name; i++ )
+               if( !stricmp( answer, cmds[i].name ) )
+                   break;
+           if( cmds[i].need_sk && !sec_keyblock ) {
+               tty_printf(_("Need the secret key to to this.\n"));
+               cmd = cmdNOP;
+           }
+           else
+               cmd = cmds[i].id;
+       }
+       switch( cmd )  {
+         case cmdHELP:
+           for(i=0; cmds[i].name; i++ ) {
+               if( cmds[i].need_sk && !sec_keyblock )
+                   ; /* skip if we do not have the secret key */
+               else if( cmds[i].desc )
+                   tty_printf("%-10s %s\n", cmds[i].name, cmds[i].desc );
+           }
+           break;
+
+         case cmdQUIT:
+           if( !modified )
+               goto leave;
+           m_free(answer);
+           answer = tty_get(_("Save changes? "));
+           if( !answer_is_yes(answer) )  {
+               m_free(answer);
+               answer = tty_get(_("Quit without saving? "));
+               if( answer_is_yes(answer) )
+                   goto leave;
+               break;
+           }
+           /* fall thru */
+         case cmdSAVE:
+           if( modified ) {
+               rc = update_keyblock( &keyblockpos, keyblock );
+               if( rc ) {
+                   log_error(_("update failed: %s\n"), g10_errstr(rc) );
+                   break;
+               }
+               if( sec_modified ) {
+                   rc = update_keyblock( &sec_keyblockpos, sec_keyblock );
+                   if( rc ) {
+                       log_error(_("update secret failed: %s\n"),
+                                                           g10_errstr(rc) );
+                       break;
+                   }
+               }
+               /* FIXME: UPDATE/INVALIDATE trustdb !! */
+           }
+           else
+               tty_printf(_("Key not changed so no update needed.\n"));
+           goto leave;
+
+         case cmdLIST:
+           redisplay = 1;
+           break;
+
+         case cmdFPR:
+           show_key_and_fingerprint( keyblock );
+           break;
+
+         case cmdSELUID:
+           if( menu_select_uid( cur_keyblock, arg_number ) )
+               redisplay = 1;
+           break;
+
+         case cmdSELKEY:
+           if( menu_select_key( cur_keyblock, arg_number ) )
+               redisplay = 1;
+           break;
+
+         case cmdCHECK:
+           /* we can only do this with the public key becuase the
+            * check functions can't cope with secret keys and it
+            * is questionable whether this would make sense at all */
+           check_all_keysigs( keyblock, count_selected_uids(keyblock) );
+           break;
+
+         case cmdSIGN: /* sign (only the public key) */
+           if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) {
+               if( !tty_get_answer_is_yes(_("Really sign all user ids? ")) ) {
+                   tty_printf(_("Hint: Select the user ids to sign\n"));
+                   break;
+               }
+           }
+           sign_uids( keyblock, locusr, &modified );
+           break;
+
+         case cmdDEBUG:
+           dump_kbnode( cur_keyblock );
+           break;
+
+         case cmdTOGGLE:
+           toggle = !toggle;
+           cur_keyblock = toggle? sec_keyblock : keyblock;
+           redisplay = 1;
+           break;
+
+         case cmdADDUID:
+           if( menu_adduid( keyblock, sec_keyblock ) ) {
+               redisplay = 1;
+               sec_modified = modified = 1;
+           }
+           break;
+
+         case cmdDELUID: {
+               int n1;
+
+               if( !(n1=count_selected_uids(keyblock)) )
+                   tty_printf(_("You must select at least one user id.\n"));
+               else if( count_uids(keyblock) - n1 < 1 )
+                   tty_printf(_("You can't delete the last user id!\n"));
+               else if( tty_get_answer_is_yes(
+                       n1 > 1? _("Really remove all selected user ids? ")
+                             : _("Really remove this user id? ")
+                      ) ) {
+                   menu_deluid( keyblock, sec_keyblock );
+                   redisplay = 1;
+                   modified = 1;
+                   if( sec_keyblock )
+                      sec_modified = 1;
+               }
+           }
+           break;
+
+         case cmdADDKEY:
+           if( generate_subkeypair( keyblock, sec_keyblock ) ) {
+               redisplay = 1;
+               sec_modified = modified = 1;
+           }
+           break;
+
+
+         case cmdDELKEY: {
+               int n1;
+
+               if( !(n1=count_selected_keys( keyblock )) )
+                   tty_printf(_("You must select at least one key.\n"));
+               else if( sec_keyblock && !tty_get_answer_is_yes(
+                      n1 > 1?
+                       _("Do you really want to delete the selected keys? "):
+                       _("Do you really want to delete this key? ")
+                      ))
+                   ;
+               else {
+                   menu_delkey( keyblock, sec_keyblock );
+                   redisplay = 1;
+                   modified = 1;
+                   if( sec_keyblock )
+                      sec_modified = 1;
+               }
+           }
+           break;
+
+         case cmdPASSWD:
+           if( change_passphrase( sec_keyblock ) )
+               sec_modified = 1;
+           break;
+
+         case cmdNOP:
+           break;
+
+         default:
+           tty_printf("\n");
+           tty_printf(_("Invalid command  (try \"help\")\n"));
+           break;
+       }
+    } /* end main loop */
+
   leave:
-    m_free( passphrase );
     release_kbnode( keyblock );
-    set_next_passphrase( NULL );
-    return rc;
+    release_kbnode( sec_keyblock );
+    m_free(answer);
 }
 
 
+
 /****************
- * Create a signature packet for the given public key certificate
- * and the user id and return it in ret_sig. User signature class SIGCLASS
- * user-id is not used (and may be NULL if sigclass is 0x20)
- * If digest_algo is 0 the function selects an appropriate one.
+ * Display the key a the user ids, if only_marked is true, do only
+ * so for user ids with mark A flag set and dont display the index number
  */
-int
-make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
-                   PKT_user_id *uid, PKT_public_key *subpk,
-                   PKT_secret_key *sk,
-                   int sigclass, int digest_algo,
-                   int (*mksubpkt)(PKT_signature *, void *), void *opaque
-                  )
+static void
+show_key_with_all_names( KBNODE keyblock, int only_marked,
+                        int with_fpr, int with_subkeys )
 {
-    PKT_signature *sig;
-    int rc=0;
-    MD_HANDLE md;
-
-    assert( (sigclass >= 0x10 && sigclass <= 0x13)
-           || sigclass == 0x20 || sigclass == 0x18 );
-    if( !digest_algo ) {
-       switch( sk->pubkey_algo ) {
-         case PUBKEY_ALGO_DSA: digest_algo = DIGEST_ALGO_SHA1; break;
-         case PUBKEY_ALGO_RSA_S:
-         case PUBKEY_ALGO_RSA: digest_algo = DIGEST_ALGO_MD5; break;
-         default:              digest_algo = DIGEST_ALGO_RMD160; break;
-       }
-    }
-    md = md_open( digest_algo, 0 );
-
-    /* hash the public key certificate and the user id */
-    hash_public_key( md, pk );
-    if( sigclass == 0x18 ) { /* subkey binding */
-       hash_public_key( md, subpk );
-    }
-    else if( sigclass != 0x20 ) {
-       if( sk->version >=4 ) {
-           byte buf[5];
-           buf[0] = 0xb4;            /* indicates a userid packet */
-           buf[1] = uid->len >> 24;  /* always use 4 length bytes */
-           buf[2] = uid->len >> 16;
-           buf[3] = uid->len >>  8;
-           buf[4] = uid->len;
-           md_write( md, buf, 5 );
-       }
-       md_write( md, uid->name, uid->len );
-    }
-    /* and make the signature packet */
-    sig = m_alloc_clear( sizeof *sig );
-    sig->version = sk->version;
-    keyid_from_sk( sk, sig->keyid );
-    sig->pubkey_algo = sk->pubkey_algo;
-    sig->digest_algo = digest_algo;
-    sig->timestamp = make_timestamp();
-    sig->sig_class = sigclass;
-    if( sig->version >= 4 )
-       build_sig_subpkt_from_sig( sig );
-
-    if( sig->version >= 4 && mksubpkt )
-       rc = (*mksubpkt)( sig, opaque );
-
-    if( !rc ) {
-       if( sig->version >= 4 )
-           md_putc( md, sig->version );
-       md_putc( md, sig->sig_class );
-       if( sig->version < 4 ) {
-           u32 a = sig->timestamp;
-           md_putc( md, (a >> 24) & 0xff );
-           md_putc( md, (a >> 16) & 0xff );
-           md_putc( md, (a >>  8) & 0xff );
-           md_putc( md,  a        & 0xff );
+    KBNODE node;
+    int i;
+
+    /* the keys */
+    for( node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_KEY
+           || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) {
+           PKT_public_key *pk = node->pkt->pkt.public_key;
+           tty_printf("%s%c %4u%c/%08lX  created: %s expires: %s\n",
+                         node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
+                         (node->flag & NODFLG_SELKEY)? '*':' ',
+                         nbits_from_pk( pk ),
+                         pubkey_letter( pk->pubkey_algo ),
+                         (ulong)keyid_from_pk(pk,NULL),
+                         datestr_from_pk(pk),
+                         expirestr_from_pk(pk) );
+           if( with_fpr && node->pkt->pkttype == PKT_PUBLIC_KEY )
+               show_fingerprint( pk );
        }
-       else {
-           byte buf[6];
-           size_t n;
-
-           md_putc( md, sig->pubkey_algo );
-           md_putc( md, sig->digest_algo );
-           if( sig->hashed_data ) {
-               n = (sig->hashed_data[0] << 8) | sig->hashed_data[1];
-               md_write( md, sig->hashed_data, n+2 );
-               n += 6;
+       else if( node->pkt->pkttype == PKT_SECRET_KEY
+           || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) {
+           PKT_secret_key *sk = node->pkt->pkt.secret_key;
+           tty_printf("%s%c %4u%c/%08lX  created: %s expires: %s\n",
+                         node->pkt->pkttype == PKT_SECRET_KEY? "sec":"sbb",
+                         (node->flag & NODFLG_SELKEY)? '*':' ',
+                         nbits_from_sk( sk ),
+                         pubkey_letter( sk->pubkey_algo ),
+                         (ulong)keyid_from_sk(sk,NULL),
+                         datestr_from_sk(sk),
+                         expirestr_from_sk(sk) );
+       }
+    }
+    /* the user ids */
+    i = 0;
+    for( node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_USER_ID ) {
+           PKT_user_id *uid = node->pkt->pkt.user_id;
+           ++i;
+           if( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A))){
+               if( only_marked )
+                  tty_printf("     ");
+               else if( node->flag & NODFLG_SELUID )
+                  tty_printf("(%d)* ", i);
+               else
+                  tty_printf("(%d)  ", i);
+               tty_print_string( uid->name, uid->len );
+               tty_printf("\n");
            }
-           else
-               n = 6;
-           /* add some magic */
-           buf[0] = sig->version;
-           buf[1] = 0xff;
-           buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */
-           buf[3] = n >> 16;
-           buf[4] = n >>  8;
-           buf[5] = n;
-           md_write( md, buf, 6 );
+       }
+    }
+}
+
+static void
+show_key_and_fingerprint( KBNODE keyblock )
+{
+    KBNODE node;
+    PKT_public_key *pk = NULL;
 
+    for( node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+           pk = node->pkt->pkt.public_key;
+           tty_printf("pub  %4u%c/%08lX %s ",
+                         nbits_from_pk( pk ),
+                         pubkey_letter( pk->pubkey_algo ),
+                         (ulong)keyid_from_pk(pk,NULL),
+                         datestr_from_pk(pk) );
        }
-       md_final(md);
+       else if( node->pkt->pkttype == PKT_USER_ID ) {
+           PKT_user_id *uid = node->pkt->pkt.user_id;
+           tty_print_string( uid->name, uid->len );
+           break;
+       }
+    }
+    tty_printf("\n");
+    if( pk )
+       show_fingerprint( pk );
+}
+
 
-       rc = complete_sig( sig, sk, md );
+static void
+show_fingerprint( PKT_public_key *pk )
+{
+    byte *array, *p;
+    size_t i, n;
+
+    p = array = fingerprint_from_pk( pk, NULL, &n );
+    tty_printf("             Fingerprint:");
+    if( n == 20 ) {
+       for(i=0; i < n ; i++, i++, p += 2 ) {
+           if( i == 10 )
+               tty_printf(" ");
+           tty_printf(" %02X%02X", *p, p[1] );
+       }
     }
+    else {
+       for(i=0; i < n ; i++, p++ ) {
+           if( i && !(i%8) )
+               tty_printf(" ");
+           tty_printf(" %02X", *p );
+       }
+    }
+    tty_printf("\n");
+    m_free(array);
+}
 
-    md_close( md );
-    if( rc )
-       free_seckey_enc( sig );
+
+/****************
+ * Ask for a new user id , do the selfsignature and put it into
+ * both keyblocks.
+ * Return true if there is a new user id
+ */
+static int
+menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+    PKT_user_id *uid;
+    PKT_public_key *pk=NULL;
+    PKT_secret_key *sk=NULL;
+    PKT_signature *sig=NULL;
+    PACKET *pkt;
+    KBNODE node;
+    KBNODE pub_where=NULL, sec_where=NULL;
+    int rc;
+
+    uid = generate_user_id();
+    if( !uid )
+       return 0;
+
+    for( node = pub_keyblock; node; pub_where = node, node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+           pk = node->pkt->pkt.public_key;
+       else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+           break;
+    }
+    if( !node ) /* no subkey */
+       pub_where = NULL;
+    for( node = sec_keyblock; node; sec_where = node, node = node->next ) {
+       if( node->pkt->pkttype == PKT_SECRET_KEY )
+           sk = node->pkt->pkt.secret_key;
+       else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
+           break;
+    }
+    if( !node ) /* no subkey */
+       sec_where = NULL;
+    assert(pk && sk );
+
+    rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0,
+                            keygen_add_std_prefs, sk );
+    if( rc ) {
+       log_error("signing failed: %s\n", g10_errstr(rc) );
+       free_user_id(uid);
+       return 0;
+    }
+
+    /* insert/append to secret keyblock */
+    pkt = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_USER_ID;
+    pkt->pkt.user_id = copy_user_id(NULL, uid);
+    node = new_kbnode(pkt);
+    if( sec_where )
+       insert_kbnode( sec_where, node, 0 );
     else
-       *ret_sig = sig;
-    return rc;
+       add_kbnode( sec_keyblock, node );
+    pkt = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_SIGNATURE;
+    pkt->pkt.signature = copy_signature(NULL, sig);
+    if( sec_where )
+       insert_kbnode( node, new_kbnode(pkt), 0 );
+    else
+       add_kbnode( sec_keyblock, new_kbnode(pkt) );
+    /* insert/append to public keyblock */
+    pkt = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_USER_ID;
+    pkt->pkt.user_id = uid;
+    node = new_kbnode(pkt);
+    if( pub_where )
+       insert_kbnode( pub_where, node, 0 );
+    else
+       add_kbnode( pub_keyblock, node );
+    pkt = m_alloc_clear( sizeof *pkt );
+    pkt->pkttype = PKT_SIGNATURE;
+    pkt->pkt.signature = copy_signature(NULL, sig);
+    if( pub_where )
+       insert_kbnode( node, new_kbnode(pkt), 0 );
+    else
+       add_kbnode( pub_keyblock, new_kbnode(pkt) );
+    return 1;
+}
+
+
+/****************
+ * Remove all selceted userids from the keyrings
+ */
+static void
+menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+    KBNODE node;
+    int selected=0;
+
+    for( node = pub_keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_USER_ID ) {
+           selected = node->flag & NODFLG_SELUID;
+           if( selected ) {
+               delete_kbnode( node );
+               if( sec_keyblock ) {
+                   KBNODE snode;
+                   int s_selected = 0;
+                   PKT_user_id *uid = node->pkt->pkt.user_id;
+                   for( snode = sec_keyblock; snode; snode = snode->next ) {
+                       if( snode->pkt->pkttype == PKT_USER_ID ) {
+                           PKT_user_id *suid = snode->pkt->pkt.user_id;
+
+                           s_selected =
+                               (uid->len == suid->len
+                                && !memcmp( uid->name, suid->name, uid->len));
+                           if( s_selected )
+                               delete_kbnode( snode );
+                       }
+                       else if( s_selected
+                                && snode->pkt->pkttype == PKT_SIGNATURE )
+                           delete_kbnode( snode );
+                       else if( snode->pkt->pkttype == PKT_SECRET_SUBKEY )
+                           s_selected = 0;
+                   }
+               }
+           }
+       }
+       else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+           delete_kbnode( node );
+       else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+           selected = 0;
+    }
+    commit_kbnode( &pub_keyblock );
+    if( sec_keyblock )
+       commit_kbnode( &sec_keyblock );
+}
+
+
+/****************
+ * Remove some of the secondary keys
+ */
+static void
+menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+    KBNODE node;
+    int selected=0;
+
+    for( node = pub_keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+           selected = node->flag & NODFLG_SELKEY;
+           if( selected ) {
+               delete_kbnode( node );
+               if( sec_keyblock ) {
+                   KBNODE snode;
+                   int s_selected = 0;
+                   u32 ki[2];
+
+                   keyid_from_pk( node->pkt->pkt.public_key, ki );
+                   for( snode = sec_keyblock; snode; snode = snode->next ) {
+                       if( snode->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+                           u32 ki2[2];
+
+                           keyid_from_sk( snode->pkt->pkt.secret_key, ki2 );
+                           s_selected = (ki[0] == ki2[0] && ki[1] == ki2[1]);
+                           if( s_selected )
+                               delete_kbnode( snode );
+                       }
+                       else if( s_selected
+                                && snode->pkt->pkttype == PKT_SIGNATURE )
+                           delete_kbnode( snode );
+                       else
+                           s_selected = 0;
+                   }
+               }
+           }
+       }
+       else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+           delete_kbnode( node );
+       else
+           selected = 0;
+    }
+    commit_kbnode( &pub_keyblock );
+    if( sec_keyblock )
+       commit_kbnode( &sec_keyblock );
+}
+
+
+/****************
+ * Select one user id or remove all selection if index is 0.
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_uid( KBNODE keyblock, int index )
+{
+    KBNODE node;
+    int i;
+
+    /* first check that the index is valid */
+    if( index ) {
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_USER_ID ) {
+               if( ++i == index )
+                   break;
+           }
+       }
+       if( !node ) {
+           tty_printf(_("No user id with index %d\n"), index );
+           return 0;
+       }
+    }
+    else { /* reset all */
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_USER_ID )
+               node->flag &= ~NODFLG_SELUID;
+       }
+       return 1;
+    }
+    /* and toggle the new index */
+    for( i=0, node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_USER_ID ) {
+           if( ++i == index )
+               if( (node->flag & NODFLG_SELUID) )
+                   node->flag &= ~NODFLG_SELUID;
+               else
+                   node->flag |= NODFLG_SELUID;
+       }
+    }
+
+    return 1;
+}
+
+/****************
+ * Select secondary keys
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_key( KBNODE keyblock, int index )
+{
+    KBNODE node;
+    int i;
+
+    /* first check that the index is valid */
+    if( index ) {
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+               || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+               if( ++i == index )
+                   break;
+           }
+       }
+       if( !node ) {
+           tty_printf(_("No secondary key with index %d\n"), index );
+           return 0;
+       }
+    }
+    else { /* reset all */
+       for( i=0, node = keyblock; node; node = node->next ) {
+           if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+               || node->pkt->pkttype == PKT_SECRET_SUBKEY )
+               node->flag &= ~NODFLG_SELKEY;
+       }
+       return 1;
+    }
+    /* and set the new index */
+    for( i=0, node = keyblock; node; node = node->next ) {
+       if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+           || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+           if( ++i == index )
+               if( (node->flag & NODFLG_SELKEY) )
+                   node->flag &= ~NODFLG_SELKEY;
+               else
+                   node->flag |= NODFLG_SELKEY;
+       }
+    }
+
+    return 1;
+}
+
+
+static int
+count_uids_with_flag( KBNODE keyblock, unsigned flag )
+{
+    KBNODE node;
+    int i=0;
+
+    for( node = keyblock; node; node = node->next )
+       if( node->pkt->pkttype == PKT_USER_ID && (node->flag & flag) )
+           i++;
+    return i;
+}
+
+static int
+count_keys_with_flag( KBNODE keyblock, unsigned flag )
+{
+    KBNODE node;
+    int i=0;
+
+    for( node = keyblock; node; node = node->next )
+       if( ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+             || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+           && (node->flag & flag) )
+           i++;
+    return i;
+}
+
+static int
+count_uids( KBNODE keyblock )
+{
+    KBNODE node;
+    int i=0;
+
+    for( node = keyblock; node; node = node->next )
+       if( node->pkt->pkttype == PKT_USER_ID )
+           i++;
+    return i;
+}
+
+
+/****************
+ * Returns true if there is at least one selected user id
+ */
+static int
+count_selected_uids( KBNODE keyblock )
+{
+    return count_uids_with_flag( keyblock, NODFLG_SELUID);
+}
+
+static int
+count_selected_keys( KBNODE keyblock )
+{
+    return count_keys_with_flag( keyblock, NODFLG_SELKEY);
 }
 
index 0eaeafe..4ff81b7 100644 (file)
@@ -74,8 +74,8 @@ add_key_expire( PKT_signature *sig, void *opaque )
  * Add preference to the self signature packet.
  * This is only called for packets with version > 3.
  */
-static int
-add_prefs( PKT_signature *sig, void *opaque )
+int
+keygen_add_std_prefs( PKT_signature *sig, void *opaque )
 {
     byte buf[8];
 
@@ -134,7 +134,7 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk )
 
     /* and make the signature */
     rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0,
-                            add_prefs, sk );
+                            keygen_add_std_prefs, sk );
     if( rc ) {
        log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
        return rc;
@@ -444,25 +444,18 @@ ask_keysize( int algo )
        else if( nbits > 2048 ) {
            tty_printf(_("Keysizes larger than 2048 are not suggested because "
                         "computations take REALLY long!\n"));
-           answer = tty_get(_("Are you sure that you want this keysize? "));
-           tty_kill_prompt();
-           if( answer_is_yes(answer) ) {
-               m_free(answer);
+           if( tty_get_answer_is_yes(_(
+                       "Are you sure that you want this keysize? ")) ) {
                tty_printf(_("Okay, but keep in mind that your monitor "
                             "and keyboard radiation is also very vulnerable "
                             "to attacks!\n"));
                break;
            }
-           m_free(answer);
        }
        else if( nbits > 1536 ) {
-           answer = tty_get(_("Do you really need such a large keysize? "));
-           tty_kill_prompt();
-           if( answer_is_yes(answer) ) {
-               m_free(answer);
+           if( tty_get_answer_is_yes(_(
+                   "Do you really need such a large keysize? ")) )
                break;
-           }
-           m_free(answer);
        }
        else
            break;
@@ -524,10 +517,7 @@ ask_valid_days()
                       add_days_to_timestamp( make_timestamp(), valid_days )));
        }
 
-       m_free(answer);
-       answer = tty_get(_("Is this correct (y/n)? "));
-       tty_kill_prompt();
-       if( answer_is_yes(answer) )
+       if( tty_get_answer_is_yes(_("Is this correct (y/n)? ")) )
            break;
     }
     m_free(answer);
@@ -549,12 +539,13 @@ has_invalid_email_chars( const char *s )
 
 
 static char *
-ask_user_id()
+ask_user_id( int mode )
 {
     char *answer;
     char *aname, *acomment, *amail, *uid;
 
-    tty_printf( _("\n"
+    if( !mode )
+       tty_printf( _("\n"
 "You need a User-ID to identify your key; the software constructs the user id\n"
 "from Real Name, Comment and Email Address in this form:\n"
 "    \"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>\"\n\n") );
@@ -630,28 +621,37 @@ ask_user_id()
        tty_printf(_("You selected this USER-ID:\n    \"%s\"\n\n"), uid);
        /* fixme: add a warning if this user-id already exists */
        for(;;) {
-           answer = tty_get(_("Edit (N)ame, (C)omment, (E)mail or (O)kay? "));
+           char *ansstr = N_("NnCcEeOoQq");
+           answer = tty_get(_(
+               "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "));
            tty_kill_prompt();
            if( strlen(answer) > 1 )
                ;
-           else if( *answer == 'N' || *answer == 'n' ) {
+           else if( *answer == ansstr[0] || *answer == ansstr[1] ) {
                m_free(aname); aname = NULL;
                break;
            }
-           else if( *answer == 'C' || *answer == 'c' ) {
+           else if( *answer == ansstr[2] || *answer == ansstr[3] ) {
                m_free(acomment); acomment = NULL;
                break;
            }
-           else if( *answer == 'E' || *answer == 'e' ) {
+           else if( *answer == ansstr[4] || *answer == ansstr[5] ) {
                m_free(amail); amail = NULL;
                break;
            }
-           else if( *answer == 'O' || *answer == 'o' ) {
+           else if( *answer == ansstr[6] || *answer == ansstr[7] ) {
                m_free(aname); aname = NULL;
                m_free(acomment); acomment = NULL;
                m_free(amail); amail = NULL;
                break;
            }
+           else if( *answer == ansstr[8] || *answer == ansstr[9] ) {
+               m_free(aname); aname = NULL;
+               m_free(acomment); acomment = NULL;
+               m_free(amail); amail = NULL;
+               m_free(uid); uid = NULL;
+               break;
+           }
            m_free(answer);
        }
        m_free(answer);
@@ -685,7 +685,7 @@ ask_passphrase( STRING2KEY **ret_s2k )
            tty_printf(_(
            "You don't want a passphrase - this is probably a *bad* idea!\n"
            "I will do it anyway.  You can change your passphrase at any time,\n"
-           "using this program with the option \"--change-passphrase\".\n\n"));
+           "using this program with the option \"--edit-key\".\n\n"));
            break;
        }
        else
@@ -729,6 +729,27 @@ do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root,
 
 
 /****************
+ * Generate a new user id packet, or return NULL if cancelled
+ */
+PKT_user_id *
+generate_user_id()
+{
+    PKT_user_id *uid;
+    char *p;
+    size_t n;
+
+    p = ask_user_id( 1 );
+    if( !p )
+       return NULL;
+    n = strlen(p);
+    uid = m_alloc( sizeof *uid + n - 1 );
+    uid->len = n;
+    strcpy(uid->name, p);
+    return uid;
+}
+
+
+/****************
  * Generate a keypair
  */
 void
@@ -762,7 +783,11 @@ generate_keypair()
     }
     nbits = ask_keysize( algo );
     ndays = ask_valid_days();
-    uid = ask_user_id();
+    uid = ask_user_id(0);
+    if( !uid ) {
+       log_error(_("Key generation cancelled.\n"));
+       return;
+    }
     dek = ask_passphrase( &s2k );
 
 
@@ -879,88 +904,29 @@ generate_keypair()
 
 /****************
  * add a new subkey to an existing key.
+ * Returns true if a new key has been generated and put into the keyblocks.
  */
-void
-generate_subkeypair( const char *username )
+int
+generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
 {
-    int rc=0;
-    KBPOS pub_kbpos, sec_kbpos;
-    KBNODE pub_keyblock = NULL;
-    KBNODE sec_keyblock = NULL;
+    int okay=0, rc=0;
     KBNODE node;
     PKT_secret_key *sk = NULL; /* this is the primary sk */
-    u32 keyid[2];
     int v4, algo, ndays;
     unsigned nbits;
     char *passphrase = NULL;
     DEK *dek = NULL;
     STRING2KEY *s2k = NULL;
 
-    if( opt.batch || opt.answer_yes || opt.answer_no ) {
-       log_error(_("Key generation can only be used in interactive mode\n"));
-       return;
-    }
-
-    /* search the userid */
-    rc = find_secret_keyblock_byname( &sec_kbpos, username );
-    if( rc ) {
-       log_error("user '%s' not found\n", username );
-       goto leave;
-    }
-    rc = read_keyblock( &sec_kbpos, &sec_keyblock );
-    if( rc ) {
-       log_error("error reading the secret key: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
-    /* and the public key */
-    rc = find_keyblock_byname( &pub_kbpos, username );
-    if( rc ) {
-       log_error("user '%s' not found in public ring\n", username );
-       goto leave;
-    }
-    rc = read_keyblock( &pub_kbpos, &pub_keyblock );
-    if( rc ) {
-       log_error("error reading the public key: %s\n", g10_errstr(rc) );
-       goto leave;
-    }
-
-    /* break out the primary key */
+    /* break out the primary secret key */
     node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
     if( !node ) {
        log_error("Oops; secret key not found anymore!\n");
-       rc = G10ERR_GENERAL;
        goto leave;
     }
 
     /* make a copy of the sk to keep the protected one in the keyblock */
     sk = copy_secret_key( NULL, node->pkt->pkt.secret_key );
-    keyid_from_sk( sk, keyid );
-    /* display primary and all secondary keys */
-    tty_printf("sec  %4u%c/%08lX %s   ",
-             nbits_from_sk( sk ),
-             pubkey_letter( sk->pubkey_algo ),
-             keyid[1], datestr_from_sk(sk) );
-    {
-       size_t n;
-       char *p = get_user_id( keyid, &n );
-       tty_print_string( p, n );
-       m_free(p);
-       tty_printf("\n");
-    }
-    for(node=sec_keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
-           PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-           keyid_from_sk( subsk, keyid );
-           tty_printf("sub  %4u%c/%08lX %s\n",
-                     nbits_from_sk( subsk ),
-                     pubkey_letter( subsk->pubkey_algo ),
-                     keyid[1], datestr_from_sk(subsk) );
-       }
-    }
-    tty_printf("\n");
-
-
-
     /* unprotect to get the passphrase */
     switch( is_secret_key_protected( sk ) ) {
       case -1:
@@ -984,6 +950,8 @@ generate_subkeypair( const char *username )
     assert(algo);
     nbits = ask_keysize( algo );
     ndays = ask_valid_days();
+    if( !tty_get_answer_is_yes( _("Really create? ") ) )
+       goto leave;
 
     if( passphrase ) {
        s2k = m_alloc_secure( sizeof *s2k );
@@ -999,31 +967,18 @@ generate_subkeypair( const char *username )
        rc = write_keybinding(pub_keyblock, pub_keyblock, sk);
     if( !rc )
        rc = write_keybinding(sec_keyblock, pub_keyblock, sk);
-    /* write back */
-    if( !rc ) {
-       rc = update_keyblock( &pub_kbpos, pub_keyblock );
-       if( rc )
-           log_error("update_public_keyblock failed\n" );
-    }
-    if( !rc ) {
-       rc = update_keyblock( &sec_kbpos, sec_keyblock );
-       if( rc )
-           log_error("update_secret_keyblock failed\n" );
-    }
     if( !rc )
-       tty_printf(_("public and secret subkey created.\n") );
-
+       okay = 1;
 
   leave:
     if( rc )
-       tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) );
+       log_error(_("Key generation failed: %s\n"), g10_errstr(rc) );
     m_free( passphrase );
     m_free( dek );
     m_free( s2k );
     if( sk ) /* release the copy of the (now unprotected) secret key */
        free_secret_key(sk);
-    release_kbnode( sec_keyblock );
-    release_kbnode( pub_keyblock );
     set_next_passphrase( NULL );
+    return okay;
 }
 
index de0319f..3493564 100644 (file)
@@ -296,6 +296,37 @@ datestr_from_sig( PKT_signature *sig )
 }
 
 
+const char *
+expirestr_from_pk( PKT_public_key *pk )
+{
+    static char buffer[11+5];
+    struct tm *tp;
+    time_t atime;
+
+    if( !pk->valid_days )
+       return "never     ";
+    atime = add_days_to_timestamp( pk->timestamp, pk->valid_days );
+    tp = gmtime( &atime );
+    sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
+    return buffer;
+}
+
+const char *
+expirestr_from_sk( PKT_secret_key *sk )
+{
+    static char buffer[11+5];
+    struct tm *tp;
+    time_t atime;
+
+    if( !sk->valid_days )
+       return "never     ";
+    atime = add_days_to_timestamp( sk->timestamp, sk->valid_days );
+    tp = gmtime( &atime );
+    sprintf(buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
+    return buffer;
+}
+
+
 /**************** .
  * Return a byte array with the fingerprint for the given PK/SK
  * The length of the array is returned in ret_len. Caller must free
index d23a98f..04d79b1 100644 (file)
@@ -202,6 +202,8 @@ list_one( const char *name, int secret )
                any = 1;
            }
 
+
+
            keyid_from_pk( pk2, keyid2 );
            if( opt.with_colons ) {
                printf("sub:%c:%u:%d:%08lX%08lX:%s:%u:",
@@ -224,6 +226,8 @@ list_one( const char *name, int secret )
                                           pubkey_letter( pk2->pubkey_algo ),
                                           (ulong)keyid2[1],
                                           datestr_from_pk( pk2 ) );
+           if( opt.fingerprint > 1 )
+               fingerprint( pk2, NULL );
        }
        else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
            u32 keyid2[2];
@@ -251,6 +255,9 @@ list_one( const char *name, int secret )
                                           pubkey_letter( sk2->pubkey_algo ),
                                           (ulong)keyid2[1],
                                           datestr_from_sk( sk2 ) );
+           if( opt.fingerprint > 1 )
+               fingerprint( NULL, sk2 );
+
        }
        else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) {
            PKT_signature *sig = node->pkt->pkt.signature;
index fdb0134..573158a 100644 (file)
@@ -72,14 +72,13 @@ int clearsign_file( const char *fname, STRLIST locusr, const char *outfile );
 int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig );
 
 /*-- keyedit.c --*/
-int sign_key( const char *username, STRLIST locusr );
-int edit_keysigs( const char *username );
 int delete_key( const char *username, int secure );
-int change_passphrase( const char *username );
+void keyedit_menu( const char *username, STRLIST locusr );
 
 /*-- keygen.c --*/
 void generate_keypair(void);
-void generate_subkeypair(const char *userid);
+int keygen_add_std_prefs( PKT_signature *sig, void *opaque );
+int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock );
 
 /*-- openfile.c --*/
 int overwrite_filep( const char *fname );
index cd3e85a..e7dabf5 100644 (file)
@@ -520,6 +520,8 @@ list_node( CTX c, KBNODE node )
        }
        if( !any )
            putchar('\n');
+       if( !mainkey && opt.fingerprint > 1 )
+           print_fingerprint( pk, NULL );
     }
     else if( (mainkey = (node->pkt->pkttype == PKT_SECRET_KEY) )
             || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
@@ -583,6 +585,8 @@ list_node( CTX c, KBNODE node )
        }
        if( !any )
            putchar('\n');
+       if( !mainkey && opt.fingerprint > 1 )
+           print_fingerprint( NULL, sk );
     }
     else if( node->pkt->pkttype == PKT_SIGNATURE  ) {
        PKT_signature *sig = node->pkt->pkt.signature;
index ec7c40a..6ae065a 100644 (file)
@@ -98,7 +98,7 @@ checksum_u16( unsigned n )
     u16 a;
 
     a  = (n >> 8) & 0xff;
-    if( opt.emulate_bugs & 1 ) {
+    if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) {
        a |= n & 0xff;
        log_debug("csum_u16 emulated for n=%u\n", n);
     }
@@ -142,7 +142,7 @@ checksum_mpi( MPI a )
      * this stored value if it is still available.
      */
 
-    if( opt.emulate_bugs & 1 )
+    if( opt.emulate_bugs & EMUBUG_GPGCHKSUM )
        nbits = 0;
     else
        nbits = mpi_get_nbit_info(a);
index 80a6539..0c9e17f 100644 (file)
@@ -50,10 +50,12 @@ struct {
     int compress_sigs;
     int always_trust;
     int rfc1991;
-    unsigned emulate_bugs; /* bug emulation flags */
+    unsigned emulate_bugs; /* bug emulation flags EMUBUG_xxxx */
 } opt;
 
 
+#define EMUBUG_GPGCHKSUM  1
+
 #define DBG_PACKET_VALUE  1    /* debug packet reading/writing */
 #define DBG_MPI_VALUE    2     /* debug mpi details */
 #define DBG_CIPHER_VALUE  4    /* debug cipher handling */
index cef9afe..6a35cd8 100644 (file)
@@ -258,7 +258,9 @@ void free_comment( PKT_comment *rem );
 void free_packet( PACKET *pkt );
 PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s );
 PKT_secret_key *copy_secret_key( PKT_secret_key *d, PKT_secret_key *s );
-int cmp_public_keys( PKT_public_key *a, PKT_public_key *b );
+PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s );
+PKT_user_id *copy_user_id( PKT_user_id *d, PKT_user_id *s );
+int cmp_public_keys( PKT_public_key *d, PKT_public_key *s );
 int cmp_public_secret_key( PKT_public_key *pk, PKT_secret_key *sk );
 int cmp_user_ids( PKT_user_id *a, PKT_user_id *b );
 
@@ -298,4 +300,7 @@ int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
                        int (*mksubpkt)(PKT_signature *, void *),
                        void *opaque  );
 
+/*-- keygen.c --*/
+PKT_user_id *generate_user_id(void);
+
 #endif /*G10_PACKET_H*/
index 310feae..60e0042 100644 (file)
@@ -349,7 +349,7 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos,
     }
 
   leave:
-    if( rc == -1 && iobuf_error(inp) )
+    if( !rc && iobuf_error(inp) )
        rc = G10ERR_INV_KEYRING;
     return rc;
 }
@@ -434,7 +434,8 @@ skip_rest( IOBUF inp, unsigned long pktlen )
     }
     else {
        for( ; pktlen; pktlen-- )
-           iobuf_get(inp);
+           if( iobuf_get(inp) == -1 )
+               break;
     }
 }
 
@@ -1048,6 +1049,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
                rc = G10ERR_INVALID_PACKET;
                goto leave;
            }
+           /* fixme: Add support for other blocksizes */
            for(i=0; i < 8 && pktlen; i++, pktlen-- )
                temp[i] = iobuf_get_noeof(inp);
            if( list_mode ) {
index 042b90f..6bffb78 100644 (file)
@@ -53,6 +53,8 @@ do_check( PKT_secret_key *sk )
            BUG();
        if( check_cipher_algo( sk->protect.algo ) )
            return G10ERR_CIPHER_ALGO; /* unsupported protection algorithm */
+       if( cipher_get_blocksize( sk->protect.algo ) != 8 )
+           return G10ERR_CIPHER_ALGO; /* unsupported protection algorithm */
        keyid_from_sk( sk, keyid );
        dek = passphrase_to_dek( keyid, sk->protect.algo,
                                 &sk->protect.s2k, 0 );
@@ -76,7 +78,7 @@ do_check( PKT_secret_key *sk )
            csum += checksum_mpi( sk->skey[i] );
            m_free( buffer );
        }
-       if( opt.emulate_bugs & 1 ) {
+       if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) {
           csum = sk->csum;
        }
        cipher_close( cipher_hd );
@@ -180,6 +182,8 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek )
 
        if( check_cipher_algo( sk->protect.algo ) )
            rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
+       else if( cipher_get_blocksize( sk->protect.algo ) != 8 )
+           rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
        else {
            cipher_hd = cipher_open( sk->protect.algo,
                                     CIPHER_MODE_AUTO_CFB, 1 );
index 6192d59..2aa9468 100644 (file)
@@ -650,4 +650,112 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
 }
 
 
+/****************
+ * Create a signature packet for the given public key certificate
+ * and the user id and return it in ret_sig. User signature class SIGCLASS
+ * user-id is not used (and may be NULL if sigclass is 0x20)
+ * If digest_algo is 0 the function selects an appropriate one.
+ */
+int
+make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
+                   PKT_user_id *uid, PKT_public_key *subpk,
+                   PKT_secret_key *sk,
+                   int sigclass, int digest_algo,
+                   int (*mksubpkt)(PKT_signature *, void *), void *opaque
+                  )
+{
+    PKT_signature *sig;
+    int rc=0;
+    MD_HANDLE md;
+
+    assert( (sigclass >= 0x10 && sigclass <= 0x13)
+           || sigclass == 0x20 || sigclass == 0x18 );
+    if( !digest_algo ) {
+       switch( sk->pubkey_algo ) {
+         case PUBKEY_ALGO_DSA: digest_algo = DIGEST_ALGO_SHA1; break;
+         case PUBKEY_ALGO_RSA_S:
+         case PUBKEY_ALGO_RSA: digest_algo = DIGEST_ALGO_MD5; break;
+         default:              digest_algo = DIGEST_ALGO_RMD160; break;
+       }
+    }
+    md = md_open( digest_algo, 0 );
+
+    /* hash the public key certificate and the user id */
+    hash_public_key( md, pk );
+    if( sigclass == 0x18 ) { /* subkey binding */
+       hash_public_key( md, subpk );
+    }
+    else if( sigclass != 0x20 ) {
+       if( sk->version >=4 ) {
+           byte buf[5];
+           buf[0] = 0xb4;            /* indicates a userid packet */
+           buf[1] = uid->len >> 24;  /* always use 4 length bytes */
+           buf[2] = uid->len >> 16;
+           buf[3] = uid->len >>  8;
+           buf[4] = uid->len;
+           md_write( md, buf, 5 );
+       }
+       md_write( md, uid->name, uid->len );
+    }
+    /* and make the signature packet */
+    sig = m_alloc_clear( sizeof *sig );
+    sig->version = sk->version;
+    keyid_from_sk( sk, sig->keyid );
+    sig->pubkey_algo = sk->pubkey_algo;
+    sig->digest_algo = digest_algo;
+    sig->timestamp = make_timestamp();
+    sig->sig_class = sigclass;
+    if( sig->version >= 4 )
+       build_sig_subpkt_from_sig( sig );
+
+    if( sig->version >= 4 && mksubpkt )
+       rc = (*mksubpkt)( sig, opaque );
+
+    if( !rc ) {
+       if( sig->version >= 4 )
+           md_putc( md, sig->version );
+       md_putc( md, sig->sig_class );
+       if( sig->version < 4 ) {
+           u32 a = sig->timestamp;
+           md_putc( md, (a >> 24) & 0xff );
+           md_putc( md, (a >> 16) & 0xff );
+           md_putc( md, (a >>  8) & 0xff );
+           md_putc( md,  a        & 0xff );
+       }
+       else {
+           byte buf[6];
+           size_t n;
+
+           md_putc( md, sig->pubkey_algo );
+           md_putc( md, sig->digest_algo );
+           if( sig->hashed_data ) {
+               n = (sig->hashed_data[0] << 8) | sig->hashed_data[1];
+               md_write( md, sig->hashed_data, n+2 );
+               n += 6;
+           }
+           else
+               n = 6;
+           /* add some magic */
+           buf[0] = sig->version;
+           buf[1] = 0xff;
+           buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */
+           buf[3] = n >> 16;
+           buf[4] = n >>  8;
+           buf[5] = n;
+           md_write( md, buf, 6 );
+
+       }
+       md_final(md);
+
+       rc = complete_sig( sig, sk, md );
+    }
+
+    md_close( md );
+    if( rc )
+       free_seckey_enc( sig );
+    else
+       *ret_sig = sig;
+    return rc;
+}
+
 
index ac12661..36f0ac5 100644 (file)
@@ -45,46 +45,8 @@ static char *db_name;
 static int  db_fd = -1;
 
 
-
-static void create_db( const char *fname );
 static void open_db(void);
 
-/**************************************************
- ************** read and write helpers ************
- **************************************************/
-
-static void
-fwrite_8(FILE *fp, byte a)
-{
-    if( putc( a & 0xff, fp ) == EOF )
-       log_fatal("error writing byte to trustdb: %s\n", strerror(errno) );
-}
-
-
-static void
-fwrite_32( FILE*fp, ulong a)
-{
-    putc( (a>>24) & 0xff, fp );
-    putc( (a>>16) & 0xff, fp );
-    putc( (a>> 8) & 0xff, fp );
-    if( putc( a & 0xff, fp ) == EOF )
-       log_fatal("error writing ulong to trustdb: %s\n", strerror(errno) );
-}
-
-static void
-fwrite_zeros( FILE *fp, size_t n)
-{
-    while( n-- )
-       if( putc( 0, fp ) == EOF )
-           log_fatal("error writing zeros to trustdb: %s\n", strerror(errno) );
-}
-
-
-
-
-/**************************************************
- ************** read and write stuff **************
- **************************************************/
 
 int
 tdbio_set_dbname( const char *new_dbname, int create )
@@ -101,7 +63,11 @@ tdbio_set_dbname( const char *new_dbname, int create )
            return G10ERR_TRUSTDB;
        }
        if( create ) {
+           FILE *fp;
+           TRUSTREC rec;
+           int rc;
            char *p = strrchr( fname, '/' );
+
            assert(p);
            *p = 0;
            if( access( fname, F_OK ) ) {
@@ -119,7 +85,30 @@ tdbio_set_dbname( const char *new_dbname, int create )
                    log_fatal_f(fname, _("directory does not exist!\n") );
            }
            *p = '/';
-           create_db( fname );
+
+           fp =fopen( fname, "w" );
+           if( !fp )
+               log_fatal_f( fname, _("can't create: %s\n"), strerror(errno) );
+           fclose(fp);
+           m_free(db_name);
+           db_name = fname;
+           db_fd = open( db_name, O_RDWR );
+           if( db_fd == -1 )
+               log_fatal_f( db_name, _("can't open: %s\n"), strerror(errno) );
+
+           memset( &rec, 0, sizeof rec );
+           rec.r.ver.version = 2;
+           rec.r.ver.created = make_timestamp();
+           rec.rectype = RECTYPE_VER;
+           rec.recnum = 0;
+           rc = tdbio_write_record( &rec );
+           if( rc )
+               log_fatal_f( fname, _("failed to create version record: %s"),
+                                                              g10_errstr(rc));
+           /* and read again to check that we are okay */
+           if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
+               log_fatal_f( db_name, "invalid trust-db created\n" );
+           return 0;
        }
     }
     m_free(db_name);
@@ -136,37 +125,6 @@ tdbio_get_dbname()
 
 
 
-/****************
- * Create a new trustdb
- */
-static void
-create_db( const char *fname )
-{
-    FILE *fp;
-
-    fp =fopen( fname, "w" );
-    if( !fp )
-       log_fatal_f( fname, _("can't create %s: %s\n"), strerror(errno) );
-    fwrite_8( fp, 1 ); /* record type */
-    fwrite_8( fp, 'g' );
-    fwrite_8( fp, 'p' );
-    fwrite_8( fp, 'g' );
-    fwrite_8( fp, 2 ); /* version */
-    fwrite_zeros( fp, 3 ); /* reserved */
-    fwrite_32( fp, 0 ); /* not locked */
-    fwrite_32( fp, make_timestamp() ); /* created */
-    fwrite_32( fp, 0 ); /* not yet modified */
-    fwrite_32( fp, 0 ); /* not yet validated*/
-    fwrite_32( fp, 0 ); /* reserved */
-    fwrite_8( fp, 3 ); /* marginals needed */
-    fwrite_8( fp, 1 ); /* completes needed */
-    fwrite_8( fp, 4 ); /* max_cet_depth */
-    fwrite_zeros( fp, 9 ); /* filler */
-    fclose(fp);
-}
-
-
-
 static void
 open_db()
 {
@@ -182,10 +140,221 @@ open_db()
 }
 
 
+/****************
+ * Return the record number of the keyhash tbl or create a new one.
+ */
+static ulong
+get_keyhashrec()
+{
+    static ulong keyhashtbl; /* record number of the key hashtable */
+    TRUSTREC vr;
+    int rc;
+
+    if( keyhashtbl )
+       return keyhashtbl;
+
+    rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+    if( rc )
+       log_fatal_f( db_name, _("error reading version record: %s\n"),
+                                                       g10_errstr(rc) );
+    if( vr.r.ver.keyhashtbl )
+       keyhashtbl = vr.r.ver.keyhashtbl;
+    else {
+       TRUSTREC rec;
+       off_t offset;
+       ulong recnum;
+       int i, n;
+
+       offset = lseek( db_fd, 0, SEEK_END );
+       if( offset == -1 )
+           log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
+       recnum = offset / TRUST_RECORD_LEN;
+       assert(recnum); /* this is will never be the first record */
+
+       keyhashtbl = recnum;
+       /* Now write the records */
+       n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD;
+       for(i=0; i < n; i++, recnum++ ) {
+            memset( &rec, 0, sizeof rec );
+            rec.rectype = RECTYPE_HTBL; /* free record */
+            rec.recnum = recnum;
+            rc = tdbio_write_record( &rec );
+            if( rc )
+                log_fatal_f(db_name,_("failed to create hashtable: %s\n"),
+                                                    g10_errstr(rc));
+       }
+       /* update the version record */
+       vr.r.ver.keyhashtbl = keyhashtbl;
+       rc = tdbio_write_record( &vr );
+       if( rc )
+           log_fatal_f( db_name, _("error updating version record: %s\n"),
+                                                            g10_errstr(rc));
+    }
+    return keyhashtbl;
+}
+
+
+/****************
+ * Update the key hashtbl or create the table if it does not exist
+ */
+static int
+update_keyhashtbl( TRUSTREC *kr )
+{
+    TRUSTREC lastrec, rec;
+    ulong hashrec, item;
+    int msb;
+    int level=0;
+    int rc, i;
+
+    hashrec = get_keyhashrec();
+  next_level:
+    msb = kr->r.key.fingerprint[level];
+    hashrec += msb / ITEMS_PER_HTBL_RECORD;
+    rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL );
+    if( rc ) {
+       log_error( db_name, "update_keyhashtbl read failed: %s\n",
+                                                       g10_errstr(rc) );
+       return rc;
+    }
+
+    item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
+    if( !item ) { /* insert new one */
+       rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = kr->recnum;
+       rc = tdbio_write_record( &rec );
+       if( rc ) {
+           log_error( db_name, "update_keyhashtbl write htbl failed: %s\n",
+                                                           g10_errstr(rc) );
+           return rc;
+       }
+    }
+    else if( item != kr->recnum ) {  /* must do an update */
+       lastrec = rec;
+       rc = tdbio_read_record( item, &rec, 0 );
+       if( rc ) {
+           log_error( db_name, "update_keyhashtbl read item failed: %s\n",
+                                                           g10_errstr(rc) );
+           return rc;
+       }
+       if( rec.rectype == RECTYPE_HTBL ) {
+           hashrec = item;
+           level++;
+           if( level >= kr->r.key.fingerprint_len ) {
+               log_error( db_name, "keyhashtbl has invalid indirections\n");
+               return G10ERR_TRUSTDB;
+           }
+           goto next_level;
+       }
+       else if( rec.rectype == RECTYPE_HLST ) { /* extend list */
+           /* see whether the key is already in this list */
+           for(;;) {
+               for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+                   if( rec.r.hlst.rnum[i] == kr->recnum ) {
+                       log_debug("HTBL: no update needed for keyrec %lu\n",
+                                   kr->recnum );
+                       return 0;
+                   }
+               }
+               if( rec.r.hlst.next ) {
+                   rc = tdbio_read_record( rec.r.hlst.next,
+                                                       &rec, RECTYPE_HLST);
+                   if( rc ) {
+                       log_error( db_name,
+                                  "scan keyhashtbl read hlst failed: %s\n",
+                                                            g10_errstr(rc) );
+                       return rc;
+                   }
+               }
+               else
+                   break; /* not there */
+           }
+           /* find the next free entry and put it in */
+           for(;;) {
+               for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+                   if( !rec.r.hlst.rnum[i] ) {
+                       rec.r.hlst.rnum[i] = kr->recnum;
+                       rc = tdbio_write_record( &rec );
+                       if( rc )
+                           log_error( db_name,
+                                  "update_keyhashtbl write hlst failed: %s\n",
+                                                             g10_errstr(rc) );
+                       return rc; /* ready */
+                   }
+               }
+               if( rec.r.hlst.next ) {
+                   rc = tdbio_read_record( rec.r.hlst.next,
+                                                     &rec, RECTYPE_HLST );
+                   if( rc ) {
+                       log_error( db_name,
+                                  "update_keyhashtbl read hlst failed: %s\n",
+                                                            g10_errstr(rc) );
+                       return rc;
+                   }
+               }
+               else { /* add a new list record */
+                   rec.r.hlst.next = item = tdbio_new_recnum();
+                   rc = tdbio_write_record( &rec );
+                   if( rc ) {
+                       log_error( db_name,
+                              "update_keyhashtbl write hlst failed: %s\n",
+                                                         g10_errstr(rc) );
+                       return rc;
+                   }
+                   memset( &rec, 0, sizeof rec );
+                   rec.rectype = RECTYPE_HLST;
+                   rec.recnum = item;
+                   rec.r.hlst.rnum[0] = kr->recnum;
+                   if( rc )
+                       log_error( db_name,
+                              "update_keyhashtbl write ext hlst failed: %s\n",
+                                                         g10_errstr(rc) );
+                   return rc; /* ready */
+               }
+           }
+       }
+       else if( rec.rectype == RECTYPE_KEY ) { /* insert a list record */
+           if( rec.recnum == kr->recnum ) {
+               log_debug("HTBL: no update needed for keyrec %lu\n",
+                                                        kr->recnum );
+               return 0;
+           }
+           item = rec.recnum; /* save number of key record */
+           memset( &rec, 0, sizeof rec );
+           rec.rectype = RECTYPE_HLST;
+           rec.recnum = tdbio_new_recnum();
+           rec.r.hlst.rnum[0] = item;       /* old keyrecord */
+           rec.r.hlst.rnum[1] = kr->recnum; /* and new one */
+           rc = tdbio_write_record( &rec );
+           if( rc ) {
+               log_error( db_name,
+                      "update_keyhashtbl write new hlst failed: %s\n",
+                                                 g10_errstr(rc) );
+               return rc;
+           }
+           /* update the hashtable record */
+           lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum;
+           rc = tdbio_write_record( &lastrec );
+           if( rc )
+               log_error( db_name,
+                      "update_keyhashtbl update htbl failed: %s\n",
+                                                 g10_errstr(rc) );
+           return rc; /* ready */
+       }
+       else {
+           log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
+                                                                   item);
+           return G10ERR_TRUSTDB;
+       }
+    }
+
+    return 0;
+}
+
+
+
 void
 tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
 {
-    int i, any;
+    int i;
     ulong rnum = rec->recnum;
 
     fprintf(fp, "rec %5lu, ", rnum );
@@ -193,7 +362,8 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
     switch( rec->rectype ) {
       case 0: fprintf(fp, "free\n");
        break;
-      case RECTYPE_VER: fprintf(fp, "version\n");
+      case RECTYPE_VER: fprintf(fp, "version, keyhashtbl=%lu\n",
+           rec->r.ver.keyhashtbl );
        break;
       case RECTYPE_DIR:
        fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x",
@@ -213,11 +383,12 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
        putc('\n', fp);
        break;
       case RECTYPE_KEY:
-       fprintf(fp, "key %lu, next=%lu, algo=%d, flen=%d",
+       fprintf(fp, "key %lu, next=%lu, algo=%d, ",
                   rec->r.key.lid,
                   rec->r.key.next,
-                  rec->r.key.pubkey_algo,
-                  rec->r.key.fingerprint_len );
+                  rec->r.key.pubkey_algo );
+       for(i=0; i < rec->r.key.fingerprint_len; i++ )
+           fprintf(fp, "%02X", rec->r.key.fingerprint[i] );
        if( rec->r.key.keyflags & KEYF_REVOKED )
            fputs(", revoked", fp );
        putc('\n', fp);
@@ -239,29 +410,29 @@ tdbio_dump_record( TRUSTREC *rec, FILE *fp  )
                    rec->r.uid.next);
        break;
       case RECTYPE_SIG:
-       fprintf(fp, "sig %lu, next=%lu\n",
+       fprintf(fp, "sig %lu, next=%lu,",
                         rec->r.sig.lid, rec->r.sig.next );
-       for(i=any=0; i < SIGS_PER_RECORD; i++ ) {
-           if( rec->r.sig.sig[i].lid ) {
-               if( !any ) {
-                   putc('\t', fp);
-                   any++;
-               }
-               fprintf(fp, "  %lu:%02x", rec->r.sig.sig[i].lid,
+       for(i=0; i < SIGS_PER_RECORD; i++ ) {
+           if( rec->r.sig.sig[i].lid )
+               fprintf(fp, " %lu:%02x", rec->r.sig.sig[i].lid,
                                          rec->r.sig.sig[i].flag );
-           }
        }
-       if( any )
-           putc('\n', fp);
+       putc('\n', fp);
        break;
       case RECTYPE_CACH:
        fprintf(fp, "cach\n");
        break;
       case RECTYPE_HTBL:
-       fprintf(fp, "htbl\n");
+       fprintf(fp, "htbl,");
+       for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
+           fprintf(fp, " %lu", rec->r.htbl.item[i] );
+       putc('\n', fp);
        break;
       case RECTYPE_HLST:
-       fprintf(fp, "hlst\n");
+       fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
+       for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
+           fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
+       putc('\n', fp);
        break;
       default:
        fprintf(fp, "unknown type %d\n", rec->rectype );
@@ -302,30 +473,29 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
                    recnum, expected, rec->rectype );
        return G10ERR_TRUSTDB;
     }
-    p++;
+    p++;    /* skip reserved byte */
     switch( rec->rectype ) {
-      case 0:  /* unused record */
+      case 0:  /* unused (free) record */
        break;
       case RECTYPE_VER: /* version record */
        if( memcmp(buf+1, "gpg", 3 ) ) {
            log_error_f( db_name, _("not a trustdb file\n") );
            rc = G10ERR_TRUSTDB;
        }
-       p += 2; /* skip magic */
+       p += 2; /* skip "pgp" */
        rec->r.ver.version  = *p++;
-       rec->r.ver.locked   = buftoulong(p); p += 4;
+       p += 3; /* reserved bytes */
+       p += 4; /* lock flags */
        rec->r.ver.created  = buftoulong(p); p += 4;
        rec->r.ver.modified = buftoulong(p); p += 4;
        rec->r.ver.validated= buftoulong(p); p += 4;
-       rec->r.ver.marginals_needed = *p++;
-       rec->r.ver.completes_needed = *p++;
-       rec->r.ver.max_cert_depth = *p++;
+       rec->r.ver.keyhashtbl=buftoulong(p); p += 4;
        if( recnum ) {
            log_error_f( db_name, "version record with recnum %lu\n",
                                                             (ulong)recnum );
            rc = G10ERR_TRUSTDB;
        }
-       if( rec->r.ver.version != 2 ) {
+       else if( rec->r.ver.version != 2 ) {
            log_error_f( db_name, "invalid file version %d\n",
                                                        rec->r.ver.version );
            rc = G10ERR_TRUSTDB;
@@ -381,6 +551,17 @@ tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
        memcpy(rec->r.cache.blockhash, p, 20); p += 20;
        rec->r.cache.trustlevel = *p++;
        break;
+      case RECTYPE_HTBL:
+       for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
+           rec->r.htbl.item[i] = buftoulong(p); p += 4;
+       }
+       break;
+      case RECTYPE_HLST:
+       rec->r.hlst.next = buftoulong(p); p += 4;
+       for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+           rec->r.hlst.rnum[i] = buftoulong(p); p += 4;
+       }
+       break;
       default:
        log_error_f( db_name, "invalid record type %d at recnum %lu\n",
                                              rec->rectype, (ulong)recnum );
@@ -412,8 +593,16 @@ tdbio_write_record( TRUSTREC *rec )
     switch( rec->rectype ) {
       case 0:  /* unused record */
        break;
-      case 1: /* version record */
-       BUG();
+      case RECTYPE_VER: /* version record */
+       if( recnum )
+           BUG();
+       memcpy(p-1, "gpg", 3 ); p += 2;
+       *p++ = rec->r.ver.version;
+       p += 7; /* skip reserved bytes and lock flags */
+       ulongtobuf(p, rec->r.ver.created); p += 4;
+       ulongtobuf(p, rec->r.ver.modified); p += 4;
+       ulongtobuf(p, rec->r.ver.validated); p += 4;
+       ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4;
        break;
 
       case RECTYPE_DIR:   /*directory record */
@@ -466,6 +655,19 @@ tdbio_write_record( TRUSTREC *rec )
        *p++ = rec->r.cache.trustlevel;
        break;
 
+      case RECTYPE_HTBL:
+       for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
+           ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
+       }
+       break;
+
+      case RECTYPE_HLST:
+       ulongtobuf( p, rec->r.hlst.next); p += 4;
+       for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+           ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
+       }
+       break;
+
       default:
        BUG();
     }
@@ -479,6 +681,8 @@ tdbio_write_record( TRUSTREC *rec )
        log_error(_("trustdb: write failed (n=%d): %s\n"), n, strerror(errno) );
        return G10ERR_WRITE_FILE;
     }
+    else if( rec->rectype == RECTYPE_KEY )
+       rc = update_keyhashtbl( rec );
 
     return rc;
 }
@@ -528,9 +732,6 @@ tdbio_new_recnum()
 /****************
  * Search the trustdb for a key which matches PK and return the dir record
  * The local_id of PK is set to the correct value
- *
- * Note: To increase performance, we could use a index search here.
- *      tdbio_write_record shoudl create this index automagically
  */
 int
 tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec )
@@ -540,32 +741,111 @@ tdbio_search_dir_record( PKT_public_key *pk, TRUSTREC *rec )
     byte *fingerprint;
     size_t fingerlen;
     int rc;
+    ulong hashrec, item;
+    int msb;
+    int level=0;
 
     keyid_from_pk( pk, keyid );
     fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen );
     assert( fingerlen == 20 || fingerlen == 16 );
 
-    for(recnum=1; !(rc=tdbio_read_record( recnum, rec, 0)); recnum++ ) {
-       if( rec->rectype != RECTYPE_KEY )
-           continue;
-       if( rec->r.key.pubkey_algo == pk->pubkey_algo
-           && !memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) ) {
-           /* found: read the dir record for this key */
-           recnum = rec->r.key.lid;
-           rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
-           if( rc )
-               break;
-           if( pk->local_id && pk->local_id != recnum )
-               log_error_f(db_name,
-                          "found record, but LID from memory does "
-                          "not match recnum (%lu,%lu)\n",
-                                               pk->local_id, recnum );
-           pk->local_id = recnum;
-           return 0;
+    /* locate the key using the hash table */
+    hashrec = get_keyhashrec();
+  next_level:
+    msb = fingerprint[level];
+    hashrec += msb / ITEMS_PER_HTBL_RECORD;
+    rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL );
+    if( rc ) {
+       log_error( db_name, "scan keyhashtbl failed: %s\n", g10_errstr(rc) );
+       return rc;
+    }
+
+    item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
+    if( !item )
+       return -1; /* not found */
+
+    rc = tdbio_read_record( item, rec, 0 );
+    if( rc ) {
+       log_error( db_name, "keyhashtbl read failed: %s\n", g10_errstr(rc) );
+       return rc;
+    }
+    if( rec->rectype == RECTYPE_HTBL ) {
+       hashrec = item;
+       level++;
+       if( level >= fingerlen ) {
+           log_error( db_name, "keyhashtbl has invalid indirections\n");
+           return G10ERR_TRUSTDB;
        }
+       goto next_level;
     }
-    if( rc != -1 )
-       log_error_f( db_name, _("search_db failed: %s\n"), g10_errstr(rc) );
+    else if( rec->rectype == RECTYPE_HLST ) {
+       for(;;) {
+           int i;
+
+           for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+               if( rec->r.hlst.rnum[i] ) {
+                   TRUSTREC tmp;
+
+                   rc = tdbio_read_record( rec->r.hlst.rnum[i],
+                                            &tmp, RECTYPE_KEY );
+                   if( rc ) {
+                       log_error( db_name,
+                                  "scan keyhashtbl read key failed: %s\n",
+                                                            g10_errstr(rc) );
+                       return rc;
+                   }
+                   if( tmp.r.key.pubkey_algo == pk->pubkey_algo
+                       && tmp.r.key.fingerprint_len == fingerlen
+                       && !memcmp(tmp.r.key.fingerprint,
+                                           fingerprint, fingerlen) ) {
+                       *rec = tmp;
+                       goto found;
+                   }
+               }
+           }
+           if( rec->r.hlst.next ) {
+               rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST );
+               if( rc ) {
+                   log_error( db_name,
+                              "scan keyhashtbl read hlst failed: %s\n",
+                                                        g10_errstr(rc) );
+                   return rc;
+               }
+           }
+           else
+               return -1; /* not found */
+       }
+      found:
+       ;
+    }
+    else if( rec->rectype == RECTYPE_KEY ) {
+       /* must check that it is the requested key */
+       if( rec->r.key.pubkey_algo != pk->pubkey_algo
+           || rec->r.key.fingerprint_len != fingerlen
+           || memcmp(rec->r.key.fingerprint, fingerprint, fingerlen) )
+           return -1; /* no: not found */
+    }
+    else {
+       log_error( db_name, "keyhashtbl %lu points to an invalid record\n",
+                                                               item);
+       return G10ERR_TRUSTDB;
+    }
+
+    recnum = rec->r.key.lid;
+
+    if( pk->local_id && pk->local_id != recnum )
+       log_error_f(db_name,
+                  "found record, but LID from memory does "
+                  "not match recnum (%lu,%lu)\n",
+                                       pk->local_id, recnum );
+    pk->local_id = recnum;
+
+    /* Now read the dir record */
+    rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
+    if( rc )
+       log_error_f(db_name, "can't read dirrec %lu: %s\n",
+                                               recnum, g10_errstr(rc) );
+
     return rc;
 }
 
index 8eec40e..c37c3ac 100644 (file)
@@ -60,15 +60,12 @@ struct trust_record {
     int  mark;
     ulong recnum;
     union {
-       struct {            /* version record: */
-           byte version;   /* should be 1 */
-           ulong locked;    /* pid of process which holds a lock */
+       struct {             /* version record: */
+           byte version;    /* should be 2 */
            ulong created;   /* timestamp of trustdb creation  */
            ulong modified;  /* timestamp of last modification */
            ulong validated; /* timestamp of last validation   */
-           byte marginals_needed;
-           byte completes_needed;
-           byte max_cert_depth;
+           ulong keyhashtbl;
        } ver;
        struct {            /* directory record */
            ulong lid;
@@ -117,10 +114,7 @@ struct trust_record {
        } htbl;
        struct {
            ulong next;
-           struct {
-               byte hash;
-               ulong rnum;
-           } item[ITEMS_PER_HLST_RECORD];
+           ulong rnum[ITEMS_PER_HLST_RECORD]; /* of a key record */
        } hlst;
     } r;
 };
index b82d26a..2bd986c 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <ctype.h>
 #include <assert.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -102,6 +103,9 @@ static ulong last_trust_web_key;
 static TRUST_SEG_LIST last_trust_web_tslist;
 
 
+#define HEXTOBIN(a) ( (a) >= '0' && (a) <= '9' ? ((a)-'0') : \
+                     (a) >= 'A' && (a) <= 'F' ? ((a)-'A'+10) : ((a)-'a'+10))
+
 /**********************************************
  ************* list helpers *******************
  **********************************************/
@@ -945,6 +949,7 @@ update_sigs( TRUSTREC *dir )
            }
            else {
                /* fixme: handle other sig classes here */
+               /* FIXME: Revocations!!! */
            }
        }
     }
@@ -1243,7 +1248,16 @@ list_trustdb( const char *username )
 {
     TRUSTREC rec;
 
-    if( username ) {
+    if( username && *username == '#' ) {
+       int rc;
+       ulong lid = atoi(username+1);
+
+       if( (rc = list_records( lid)) )
+           log_error("user '%s' read problem: %s\n", username, g10_errstr(rc));
+       else if( (rc = list_sigs( lid )) )
+           log_error("user '%s' list problem: %s\n", username, g10_errstr(rc));
+    }
+    else if( username ) {
        PKT_public_key *pk = m_alloc_clear( sizeof *pk );
        int rc;
 
@@ -1274,10 +1288,10 @@ list_trustdb( const char *username )
 }
 
 /****************
- * make a list of all defined owner trust value.
+ * Print a list of all defined owner trust value.
  */
 void
-list_ownertrust()
+export_ownertrust()
 {
     TRUSTREC rec;
     TRUSTREC rec2;
@@ -1307,6 +1321,67 @@ list_ownertrust()
     }
 }
 
+
+void
+import_ownertrust( const char *fname )
+{
+    FILE *fp;
+    int is_stdin=0;
+    char line[256];
+    char *p;
+    size_t n, fprlen;
+    unsigned otrust;
+
+    if( !fname || (*fname == '-' && !fname[1]) ) {
+       fp = stdin;
+       fname = "[stdin]";
+       is_stdin = 1;
+    }
+    else if( !(fp = fopen( fname, "r" )) ) {
+       log_error_f(fname, _("can't open file: %s\n"), strerror(errno) );
+       return;
+    }
+
+    while( fgets( line, DIM(line)-1, fp ) ) {
+       if( !*line || *line == '#' )
+           continue;
+       n = strlen(line);
+       if( line[n-1] != '\n' ) {
+           log_error_f(fname, "line to long\n" );
+           break; /* can't continue */
+       }
+       for(p = line; *p && *p != ':' ; p++ )
+           if( !isxdigit(*p) )
+               break;
+       if( *p != ':' ) {
+           log_error_f(fname, "error: missing colon\n" );
+           continue;
+       }
+       fprlen = p - line;
+       if( fprlen != 32 && fprlen != 40 ) {
+           log_error_f(fname, "error: invalid fingerprint\n" );
+           continue;
+       }
+       if( sscanf(p, ":%u:", &otrust ) != 1 ) {
+           log_error_f(fname, "error: no otrust value\n" );
+           continue;
+       }
+       if( !otrust )
+           continue; /* no otrust defined - no need to update or insert */
+       /* convert the ascii fingerprint to binary */
+       for(p=line, fprlen=0; *p != ':'; p += 2 )
+           line[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]);
+       line[fprlen] = 0;
+
+       log_hexdump("found: ", line, fprlen );
+    }
+    if( ferror(fp) )
+       log_error_f(fname, _("read error: %s\n"), strerror(errno) );
+    if( !is_stdin )
+       fclose(fp);
+}
+
+
 void
 list_trust_path( int max_depth, const char *username )
 {
@@ -1405,7 +1480,17 @@ check_trustdb( const char *username )
     TRUSTREC rec;
     int rc;
 
-    if( username ) {
+    if( username && *username == '#' ) {
+       int rc;
+       ulong lid = atoi(username+1);
+
+       if( (rc = update_sigs_by_lid( lid )) )
+           log_error("lid %lu: check failed: %s\n",
+                                       lid, g10_errstr(rc));
+       else
+           log_info("lid %lu: checked: %s\n", lid, g10_errstr(rc));
+    }
+    else if( username ) {
        PKT_public_key *pk = m_alloc_clear( sizeof *pk );
 
        if( (rc = get_pubkey_byname( pk, username )) )
@@ -1724,7 +1809,7 @@ insert_trust_record( PKT_public_key *orig_pk )
                    BUG();  /* more than one primary key */
                keyid_from_pk( pk, keyid );
            }
-           fingerprint = fingerprint_from_pk( orig_pk, NULL, &fingerlen );
+           fingerprint = fingerprint_from_pk( pk, NULL, &fingerlen );
            rec = m_alloc_clear( sizeof *rec );
            rec->rectype = RECTYPE_KEY;
            rec->r.key.pubkey_algo = pk->pubkey_algo;
index aaf0d4a..dba3818 100644 (file)
@@ -38,7 +38,8 @@
 /*-- trustdb.c --*/
 void list_trustdb(const char *username);
 void list_trust_path( int max_depth, const char *username );
-void list_ownertrust(void);
+void export_ownertrust(void);
+void import_ownertrust(const char *fname);
 void check_trustdb( const char *username );
 int init_trustdb( int level, const char *dbname );
 int check_trust( PKT_public_key *pk, unsigned *r_trustlevel );
index 0e2b95a..8ddd365 100644 (file)
@@ -35,6 +35,7 @@
 #define CIPHER_ALGO_DES_SK      6
 #define CIPHER_ALGO_BLOWFISH160 42  /* blowfish 160 bit key (not in OpenPGP)*/
 #define CIPHER_ALGO_SKIPJACK   101  /* experimental: skipjack */
+#define CIPHER_ALGO_TWOFISH    102  /* experimental: twofish 128 bit */
 #define CIPHER_ALGO_DUMMY      110  /* no encryption at all */
 
 #define PUBKEY_ALGO_RSA        1
@@ -130,6 +131,7 @@ int string_to_cipher_algo( const char *string );
 const char * cipher_algo_to_string( int algo );
 int check_cipher_algo( int algo );
 unsigned cipher_get_keylen( int algo );
+unsigned cipher_get_blocksize( int algo );
 CIPHER_HANDLE cipher_open( int algo, int mode, int secure );
 void cipher_close( CIPHER_HANDLE c );
 void cipher_setkey( CIPHER_HANDLE c, byte *key, unsigned keylen );
index 2949cfd..e4119db 100644 (file)
@@ -25,6 +25,7 @@ void tty_print_string( byte *p, size_t n );
 char *tty_get( const char *prompt );
 char *tty_get_hidden( const char *prompt );
 void tty_kill_prompt(void);
+int tty_get_answer_is_yes( const char *prompt );
 
 
 #endif /*G10_TTYIO_H*/
index 6cad795..28feaf0 100644 (file)
@@ -49,7 +49,7 @@ g10m_release( MPI a )
 void
 g10m_resize( MPI a, unsigned nbits )
 {
-    return mpi_resize( a, (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+    mpi_resize( a, (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
 }
 
 MPI  g10m_copy( MPI a )           { return mpi_copy( a );   }
index 25efbd9..150d87c 100644 (file)
@@ -120,6 +120,7 @@ mpi_invm( MPI x, MPI a, MPI n )
            mpi_rshift(t2, t2, 1);
            mpi_rshift(t3, t3, 1);
          Y4:
+           ;
        } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
 
        if( !t3->sign ) {
@@ -216,6 +217,7 @@ mpi_invm( MPI x, MPI a, MPI n )
                mpi_rshift(t3, t3, 1);
            }
          Y4:
+           ;
        } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
 
        if( !t3->sign ) {
index 54cc7ea..9b0de2d 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -400,11 +400,6 @@ msgstr "--clearsign [Dateiname]"
 msgid "--decrypt [filename]"
 msgstr "--decrypt [Dateiname]"
 
-#. sign the key given as argument
-#: g10/g10.c:802
-msgid "--sign-key username"
-msgstr "--sign-key Benutzername"
-
 #: g10/g10.c:810
 msgid "--edit-key username"
 msgstr "--edit-key Benutzername"
@@ -417,12 +412,6 @@ msgstr "--delete-secret-key Benutzername"
 msgid "--delete-key username"
 msgstr "--delete-key Benutzername"
 
-#. Change the passphrase
-#. no arg: use default, 1 arg use this one
-#: g10/g10.c:829
-msgid "--change-passphrase [username]"
-msgstr "--change-passphrase [Benutzername]"
-
 #. prepare iobufs
 #: g10/encode.c:200 g10/g10.c:853 g10/keylist.c:79
 msgid "can't open %s: %s\n"
@@ -810,12 +799,12 @@ msgstr "\"passphrase\" nicht richtig wiederholt; noch einmal.\n"
 msgid ""
 "You don't want a passphrase - this is probably a *bad* idea!\n"
 "I will do it anyway.  You can change your passphrase at any time,\n"
-"using this program with the option \"--change-passphrase\".\n"
+"using this program with the option \"--edit-key\".\n"
 "\n"
 msgstr ""
 "Sie m\366chten keine \"passphrase\" - Dies ist eine *schlechte* Idee!\n"
 "Es ist trotzdem m\366glich.  Sie k\366nnen Ihre \"phassphrase\" jederzeit\n"
-"\344ndern, indem sie dieses Programm mit dem Kommando \"--change-passphrase\"\n"
+"\344ndern, indem sie dieses Programm mit dem Kommando \"--edit-key\"\n"
 "aufrufen.\n"
 "\n"
 
index a52ea0a..aa9afe6 100644 (file)
@@ -1,3 +1,7 @@
+Wed Jul 29 14:53:34 1998  Werner Koch  (wk@(none))
+
+       * ttyio.c (tty_get_answer_is_yes): New.
+
 Tue Jul 21 10:35:48 1998  Werner Koch  (wk@(none))
 
        * argparse.c: New option flag to distinguish options and commands.
index 3bdc68a..e4c2cf1 100644 (file)
@@ -91,3 +91,4 @@ answer_is_yes( const char *s )
     return 0;
 }
 
+
index 4721bcd..d126213 100644 (file)
@@ -204,7 +204,7 @@ secmem_malloc( size_t size )
        }
     /* allocate a new block */
     if( (poollen + size <= poolsize) ) {
-       mb = pool + poollen;
+       mb = (void*)((char*)pool + poollen);
        poollen += size;
        mb->size = size;
     }
@@ -240,7 +240,7 @@ secmem_realloc( void *p, size_t newsize )
        return p; /* it is easier not to shrink the memory */
     a = secmem_malloc( newsize );
     memcpy(a, p, size);
-    memset(a+size, 0, newsize-size);
+    memset((char*)a+size, 0, newsize-size);
     secmem_free(p);
     return a;
 }
@@ -271,7 +271,7 @@ secmem_free( void *a )
 int
 m_is_secure( const void *p )
 {
-    return p >= pool && p < (pool+poolsize);
+    return p >= pool && p < ((char*)pool+poolsize);
 }
 
 void
index bb795c2..99927d6 100644 (file)
@@ -328,3 +328,15 @@ tty_kill_prompt()
     last_prompt_len = 0;
 }
 
+
+int
+tty_get_answer_is_yes( const char *prompt )
+{
+    int yes;
+    char *p = tty_get( prompt );
+    tty_kill_prompt();
+    yes = answer_is_yes(p);
+    m_free(p);
+    return yes;
+}
+