diff --git a/AUTHORS b/AUTHORS index 86dab482b..779b4edac 100644 --- 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 6197de5c3..0a375b1a3 100644 --- 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 c220c00c2..240b319f1 100644 --- a/README +++ b/README @@ -135,6 +135,9 @@ 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 b30466c18..d0c53d505 100644 --- 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 3c05ad040..9ceeb728f 100644 --- 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. diff --git a/checks/defs.inc b/checks/defs.inc index f3ff66f18..49cbcf292 100755 --- a/checks/defs.inc +++ b/checks/defs.inc @@ -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 <./options no-greeting diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 010952863..528de20cd 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -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. diff --git a/cipher/Makefile.am b/cipher/Makefile.am index f01bb7844..36dd0c537 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -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 \ diff --git a/cipher/cipher.c b/cipher/cipher.c index 25b1e8fcd..049207bf1 100644 --- a/cipher/cipher.c +++ b/cipher/cipher.c @@ -34,12 +34,13 @@ #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; + 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; } + nbytes -= 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; - } - #ifdef BIG_ENDIAN_HOST - } - #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; } } diff --git a/cipher/tiger.c b/cipher/tiger.c index 2560e8ab7..d02f5cb86 100644 --- a/cipher/tiger.c +++ b/cipher/tiger.c @@ -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");*/ diff --git a/doc/DETAILS b/doc/DETAILS index 661a4a1ef..04236b7e9 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -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 diff --git a/doc/gpg.1pod b/doc/gpg.1pod index 1fde885fa..5be04b6b3 100644 --- a/doc/gpg.1pod +++ b/doc/gpg.1pod @@ -5,6 +5,7 @@ gpg - GNU Privacy Guard =head1 SYNOPSIS B [--homedir name] [--options file] [options] command [args] + B [--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 - 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 - Make a signature on key of user I. - 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 + Present a menu which enables you to do all key + related tasks: + B + Make a signature on key of user I. + 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 + Create an alternate user id. + B + Delete an user id. + B + Add a subkey to this key. + B + Remove a subkey. + B + Change the passphrase of the secret key. + B + Check signatures + B I + Toggle selection of user id with index I. + Use 0 to deselect all. + B I + Toggle selection of subkey with index I. + Use 0 to deselect all. + B + Check all selected user ids. + B + Toggle between public and secret key listing. + B + Save all changes to the key rings and quit. + B + 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] B<--import> import/merge keys -B<--list-ownertrust> +B<--export-ownertrust> List the assigned ownertrust values in ascii format for backup purposes [B only]. +B<--import-ownertrust> [I] + Update the trustdb with the ownertrust values stored in + I (or stdin if not given); existing values will be + overwritten. [B only]. + =head1 OPTIONS Long options can be put in an options file (default F<~/.gnupg/options>); diff --git a/g10/ChangeLog b/g10/ChangeLog index badb5cd32..39e528de0 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -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. diff --git a/g10/Makefile.am b/g10/Makefile.am index e97fb8cf4..0097ace5f 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -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 diff --git a/g10/OPTIONS b/g10/OPTIONS index aa8b46d6e..4def8e60e 100644 --- a/g10/OPTIONS +++ b/g10/OPTIONS @@ -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. + + diff --git a/g10/build-packet.c b/g10/build-packet.c index ae5e08f15..c7ed8fa2a 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -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. diff --git a/g10/cipher.c b/g10/cipher.c index 0891d52fb..4c4e85f78 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -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; } diff --git a/g10/encr-data.c b/g10/encr-data.c index e7e56408a..b5eb0e0ee 100644 --- a/g10/encr-data.c +++ b/g10/encr-data.c @@ -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; } diff --git a/g10/free-packet.c b/g10/free-packet.c index 9d623ec30..14a466556 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -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 ) { diff --git a/g10/g10.c b/g10/g10.c index dbd277d63..c7d8dfcc7 100644 --- 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 */ diff --git a/g10/import.c b/g10/import.c index ec143dce2..c5eaf9ba8 100644 --- a/g10/import.c +++ b/g10/import.c @@ -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; diff --git a/g10/kbnode.c b/g10/kbnode.c index 4578f8271..428768105 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -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; diff --git a/g10/keydb.h b/g10/keydb.h index bbd803fbe..85ab88b45 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -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 ); diff --git a/g10/keyedit.c b/g10/keyedit.c index 4369e9a86..73772328b 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "options.h" #include "packet.h" @@ -37,73 +38,60 @@ #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,338 +99,218 @@ 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; + if( node->pkt->pkttype == PKT_USER_ID ) { + PKT_user_id *uid = node->pkt->pkt.user_id; - 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; + 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("%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); + } + 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; + + 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; + } + 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 */ } - tty_printf("\n"); - /* FIXME: update the trustdb */ } } - if( inv_sigs ) + 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 ) - tty_printf(_("No public key for %d signatures\n"), no_key ); - if( oth_err ) + 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 ); - return inv_sigs || no_key || 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); + + return inv_sigs || no_key || oth_err || mis_selfsig; } + /**************** - * Ask and remove invalid signatures that are to be removed. + * 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 */ static int -remove_keysigs( KBNODE keyblock, u32 *keyid, int all ) +sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified ) { - 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 ) { - PKT_signature *sig = node->pkt->pkt.signature; - - 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 */ - } - - 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++; - } - m_free(answer); - } - } - - 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; -} - - -/**************** - * 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. - */ -int -sign_key( const char *username, STRLIST locusr ) -{ - 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 - && (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; - } - } - } - } - 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 ) - continue; - for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) { + /* reset mark for uids which are already signed */ + uidnode = NULL; + for( node=keyblock; node; node = node->next ) { 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 ); + 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( 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 */ } } } - } - - rc = update_keyblock( &kbpos, keyblock ); - if( rc ) { - log_error("update_keyblock failed: %s\n", g10_errstr(rc) ); - goto leave; - } - - 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; + /* 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; } - } + /* 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; + } + } + } /* end loop over signators */ leave: - release_kbnode( keyblock ); + release_sk_list( sk_list ); 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; - KBNODE node; - KBPOS kbpos; - PKT_secret_key *sk; - u32 keyid[2]; - char *answer; int changed=0; + KBNODE node; + PKT_secret_key *sk; 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; +} - if( changed ) { - rc = update_keyblock( &kbpos, keyblock ); + + + +/**************** + * 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; + } + + /* 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); +} + + + +/**************** + * 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 + */ +static void +show_key_with_all_names( KBNODE keyblock, int only_marked, + int with_fpr, int with_subkeys ) +{ + 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 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"); + } + } + } +} + +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) ); + } + 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 ); +} + + +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); } /**************** - * 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. + * Ask for a new user id , do the selfsignature and put it into + * both keyblocks. + * Return true if there is a new user id */ -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 int +menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) { - PKT_signature *sig; - int rc=0; - MD_HANDLE md; + 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; - 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; - } + 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; } - 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 ); + 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; } - 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( !node ) /* no subkey */ + sec_where = NULL; + assert(pk && sk ); - 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 ); + 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; } - md_close( md ); - if( rc ) - free_seckey_enc( sig ); + /* 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); } diff --git a/g10/keygen.c b/g10/keygen.c index 0eaeafeae..4ff81b7f5 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -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) \"\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 @@ -728,6 +728,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 */ @@ -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; } diff --git a/g10/keyid.c b/g10/keyid.c index de0319fe3..349356406 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -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 diff --git a/g10/keylist.c b/g10/keylist.c index d23a98f1e..04d79b1ec 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -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; diff --git a/g10/main.h b/g10/main.h index fdb01343f..573158a73 100644 --- a/g10/main.h +++ b/g10/main.h @@ -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 ); diff --git a/g10/mainproc.c b/g10/mainproc.c index cd3e85a8b..e7dabf5e9 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -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; diff --git a/g10/misc.c b/g10/misc.c index ec7c40a1a..6ae065a98 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -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); diff --git a/g10/options.h b/g10/options.h index 80a6539ec..0c9e17fc1 100644 --- a/g10/options.h +++ b/g10/options.h @@ -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 */ diff --git a/g10/packet.h b/g10/packet.h index cef9afe5c..6a35cd89e 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -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*/ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 310feae29..60e0042da 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -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 ) { diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 042b90fe8..6bffb7843 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -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 ); diff --git a/g10/sign.c b/g10/sign.c index 6192d5987..2aa9468e9 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -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; +} + diff --git a/g10/tdbio.c b/g10/tdbio.c index ac1266147..36f0ac503 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -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; } - if( rc != -1 ) - log_error_f( db_name, _("search_db failed: %s\n"), g10_errstr(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; + } + 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; } diff --git a/g10/tdbio.h b/g10/tdbio.h index 8eec40e4a..c37c3ac1f 100644 --- a/g10/tdbio.h +++ b/g10/tdbio.h @@ -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; }; diff --git a/g10/trustdb.c b/g10/trustdb.c index b82d26a1e..2bd986c79 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/g10/trustdb.h b/g10/trustdb.h index aaf0d4a8d..dba38188b 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -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 ); diff --git a/include/cipher.h b/include/cipher.h index 0e2b95a79..8ddd3655a 100644 --- a/include/cipher.h +++ b/include/cipher.h @@ -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 ); diff --git a/include/ttyio.h b/include/ttyio.h index 2949cfd19..e4119db86 100644 --- a/include/ttyio.h +++ b/include/ttyio.h @@ -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*/ diff --git a/mpi/g10m.c b/mpi/g10m.c index 6cad79587..28feaf08c 100644 --- a/mpi/g10m.c +++ b/mpi/g10m.c @@ -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 ); } diff --git a/mpi/mpi-inv.c b/mpi/mpi-inv.c index 25efbd979..150d87ca1 100644 --- a/mpi/mpi-inv.c +++ b/mpi/mpi-inv.c @@ -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 ) { diff --git a/po/de.po b/po/de.po index 54cc7eab2..9b0de2d28 100644 --- 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" diff --git a/util/ChangeLog b/util/ChangeLog index a52ea0a69..aa9afe6e3 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -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. diff --git a/util/miscutil.c b/util/miscutil.c index 3bdc68a21..e4c2cf1ae 100644 --- a/util/miscutil.c +++ b/util/miscutil.c @@ -91,3 +91,4 @@ answer_is_yes( const char *s ) return 0; } + diff --git a/util/secmem.c b/util/secmem.c index 4721bcd91..d12621324 100644 --- a/util/secmem.c +++ b/util/secmem.c @@ -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 diff --git a/util/ttyio.c b/util/ttyio.c index bb795c282..99927d660 100644 --- a/util/ttyio.c +++ b/util/ttyio.c @@ -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; +} +