diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 77a3e4b82..2ce644a43 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,461 +1,469 @@ +Fri Jul 2 11:45:54 CEST 1999 Werner Koch + + + * dsa.c (gen_k): Changed algorithm to consume less random bytes + * elgamal.c (gen_k): Ditto. + + * random.c (random_dump_stats): New. + Thu Jul 1 12:47:31 CEST 1999 Werner Koch * primegen.c, elgamal.c, dsa.c (progess): New and replaced all fputc with a call to this function. Sat Jun 26 12:15:59 CEST 1999 Werner Koch * rndegd.c (do_write): s/ssize_t/int/ due to SunOS 4.1 probs. * cipher.c (do_cbc_encrypt, do_cbc_decrypt): New. * dynload.c (HAVE_DL_SHL_LOAD): Map hpux API to dlopen (Dave Dykstra). * Makefile.am (install-exec-hook): Removed. Sun May 23 14:20:22 CEST 1999 Werner Koch * cipher.c (setup_cipher_table): Enable Twofish * random.c (fast_random_poll): Disable use of times() for mingw32. Mon May 17 21:54:43 CEST 1999 Werner Koch * dynload.c (register_internal_cipher_extension): Minor init fix. Tue May 4 15:47:53 CEST 1999 Werner Koch * primegen.c (gen_prime): Readded the Fermat test. Fixed the bug that we didn't correct for step when passing the prime to the Rabin-Miller test which led to bad performance (Stefan Keller). (check_prime): Add a first Fermat test. Sun Apr 18 10:11:28 CEST 1999 Werner Koch * cipher.c (cipher_setiv): Add ivlen arg, changed all callers. * random.c (randomize_buffer): alway use secure memory because we can't use m_is_secure() on a statically allocated buffer. * twofish.c: Replaced some macros by a loop to reduce text size. * Makefile.am (twofish): No more need for sed editing. Fri Apr 9 12:26:25 CEST 1999 Werner Koch * cipher.c (cipher_open): Reversed the changes for AUTO_CFB. * blowfish.c: Dropped the Blowfish 160 mode. * cipher.c (cipher_open): Ditto. (setup_cipher_table): Ditto. And removed support of twofish128 Wed Apr 7 20:51:39 CEST 1999 Werner Koch * random.c (get_random_bits): Can now handle requests > POOLSIZE * cipher.c (cipher_open): Now uses standard CFB for automode if the blocksize is gt 8 (according to rfc2440). * twofish.c: Applied Matthew Skala's patches for 256 bit key. Tue Apr 6 19:58:12 CEST 1999 Werner Koch * random.c (get_random_bits): Can now handle requests > POOLSIZE * cipher.c (cipher_open): Now uses standard CFB for automode if the blocksize is gt 8 (according to rfc2440). Sat Mar 20 11:44:21 CET 1999 Werner Koch * rndlinux.c (tty_printf) [IS_MODULE]: Removed. * rndegd.c (gather_random): Some fixes. Wed Mar 17 13:09:03 CET 1999 Werner Koch * rndegd.c (do_read): New. (gather_random): Changed the implementation. Mon Mar 8 20:47:17 CET 1999 Werner Koch * dynload.c (DLSYM_NEEDS_UNDERSCORE): Renamed. Fri Feb 26 17:55:41 CET 1999 Werner Koch * md.c: Nearly a total rewrote. Wed Feb 24 11:07:27 CET 1999 Werner Koch * cipher.c (context): Fixed alignment * md.c: Ditto. * rndegd.c: New Mon Feb 22 20:04:00 CET 1999 Werner Koch * rndegd.c: New. Wed Feb 10 17:15:39 CET 1999 Werner Koch * Makefile.am: Modules are now figured out by configure * construct.c: New. Generated by configure. Changed all modules to work with that. * sha1.h: Removed. * md5.h: Removed. * twofish.c: Changed interface to allow Twofish/256 * rndunix.c (start_gatherer): Die on SIGPIPE. Wed Jan 20 18:59:49 CET 1999 Werner Koch * rndunix.c (gather_random): Fix to avoid infinite loop. Sun Jan 17 11:04:33 CET 1999 Werner Koch * des.c (is_weak_key): Replace system memcmp due to bugs in SunOS's memcmp. (des_get_info): Return error on failed selftest. * twofish.c (twofish_setkey): Return error on failed selftest or invalid keylength. * cast5.c (cast_setkey): Ditto. * blowfish.c (bf_setkey): Return error on failed selftest. Tue Jan 12 11:17:18 CET 1999 Werner Koch * random.c (random_is_faked): New. * tiger.c: Only compile if we have the u64 type Sat Jan 9 16:02:23 CET 1999 Werner Koch * rndunix.c (gather_random): check for setuid. * Makefile.am: Add a way to staically link random modules Thu Jan 7 18:00:58 CET 1999 Werner Koch * md.c (md_stop_debug): Do a flush first. (md_open): size of buffer now depends on the secure parameter Sun Jan 3 15:28:44 CET 1999 Werner Koch * rndunix.c (start_gatherer): Fixed stupid ==/= bug 1998-12-31 Geoff Keating * des.c (is_weak_key): Rewrite loop end condition. Tue Dec 29 14:41:47 CET 1998 Werner Koch * random.c: add unistd.h for getpid(). (RAND_MAX): Fallback value for Sun. Wed Dec 23 17:12:24 CET 1998 Werner Koch * md.c (md_copy): Reset debug. Mon Dec 14 21:18:49 CET 1998 Werner Koch * random.c (read_random_source): Changed the interface to the random gathering function. (gather_faked): Use new interface. * dynload.c (dynload_getfnc_fast_random_poll): Ditto. (dynload_getfnc_gather_random): Ditto. * rndlinux.c (gather_random): Ditto. * rndunix.c (gather_random): Ditto. Sat Dec 12 18:40:32 CET 1998 Werner Koch * dynload.c (SYMBOL_VERSION): New to cope with system which needs underscores. * rndunix.c: Rewrote large parts Thu Dec 10 20:15:36 CET 1998 Werner Koch * dynload.c (load_extension): increased needed verbosity level. * random.c (fast_random_poll): Fallback to a default fast random poll function. (read_random_source): Always use the faked entroy gatherer if no gather module is available. * rndlinux.c (fast_poll): Removed. * rndunix.c (fast_poll): Removed. Wed Nov 25 12:33:41 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-*.c: Removed. * rndlinux.c : New. * rndunix.c : New. * random.c : Restructured the interface to the gather modules. (intialize): Call constructor functions (read_radnom_source): Moved to here. * dynload.c (dynload_getfnc_gather_random): New. (dynload_getfnc_fast_random_poll): New. (register_internal_cipher_extension): New. (register_cipher_extension): Support of internal modules. Sun Nov 8 17:44:36 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-unix.c (read_random_source): Removed the assert. Mon Oct 19 18:34:30 1998 me,,, (wk@tobold) * pubkey.c: Hack to allow us to give some info about RSA keys back. Thu Oct 15 11:47:57 1998 Werner Koch (wk@isil.d.shuttle.de) * dynload.c: Support for DLD Wed Oct 14 12:13:07 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-unix.c: Now uses names from configure for /dev/random. 1998-10-10 SL Baur * Makefile.am: fix sed -O substitutions to catch -O6, etc. Tue Oct 6 10:06:32 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-unix.c (HAVE_GETTIMEOFDAY): Fixed (was ..GETTIMEOFTIME :-) * rand-dummy.c (HAVE_GETTIMEOFDAY): Ditto. Mon Sep 28 13:23:09 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_digest): New. (md_reset): New. Wed Sep 23 12:27:02 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c (TIGER_CONTEXT): moved "buf", so that it is 64 bit aligned. Mon Sep 21 06:22:53 1998 Werner Koch (wk@(none)) * des.c: Some patches from Michael. Thu Sep 17 19:00:06 1998 Werner Koch (wk@(none)) * des.c : New file from Michael Roth Mon Sep 14 11:10:55 1998 Werner Koch (wk@(none)) * blowfish.c (bf_setkey): Niklas Hernaeus patch to detect weak keys. Mon Sep 14 09:19:25 1998 Werner Koch (wk@(none)) * dynload.c (RTLD_NOW): Now defined to 1 if it is undefined. Mon Sep 7 17:04:33 1998 Werner Koch (wk@(none)) * Makefile.am: Fixes to allow a different build directory Thu Aug 6 17:25:38 1998 Werner Koch,mobil,,, (wk@tobold) * random.c (get_random_byte): Removed and changed all callers to use get_random_bits() 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. (get_random_bits): Ditto. Thu Jul 9 13:01:14 1998 Werner Koch (wk@isil.d.shuttle.de) * dynload.c (load_extension): Function now nbails out if the program is run setuid. Wed Jul 8 18:58:23 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (rmd160_hash_buffer): New. Thu Jul 2 10:50:30 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c (cipher_open): algos >=100 use standard CFB Thu Jun 25 11:18:25 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am: Support for extensions Thu Jun 18 12:09:38 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (mix_pool): simpler handling for level 0 Mon Jun 15 14:40:48 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c: Removed from dist, will reappear as dynload module Sat Jun 13 14:16:57 1998 Werner Koch (wk@isil.d.shuttle.de) * pubkey.c: Major changes to allow extensions. Changed the inteface of all public key ciphers and added the ability to load extensions on demand. * misc.c: Removed. Wed Jun 10 07:52:08 1998 Werner Koch,mobil,,, (wk@tobold) * dynload.c: New. * cipher.c: Major changes to allow extensions. Mon Jun 8 22:43:00 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c: Major internal chnages to support extensions. * blowfish.c (blowfish_get_info): New and made all internal functions static, changed heder. * cast5.c (cast5_get_info): Likewise. Mon Jun 8 12:27:52 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c (transform): Fix for big endian * cipher.c (do_cfb_decrypt): Big endian fix. Fri May 22 07:30:39 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_get_oid): Add a new one for TIGER. Thu May 21 13:24:52 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c: Add support for a dummy cipher Thu May 14 15:40:36 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (transform): fixed sigbus - I should better add Christian von Roques's new implemenation of rmd160_write. Fri May 8 18:07:44 1998 Werner Koch (wk@isil.d.shuttle.de) * rand-internal.h, rand-unix.c, rand-w32.c, rand_dummy.c: New * random.c: Moved system specific functions to rand-****.c Fri May 8 14:01:17 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (fast_random_poll): add call to gethrtime. Tue May 5 21:28:55 1998 Werner Koch (wk@isil.d.shuttle.de) * elgamal.c (elg_generate): choosing x was not correct, could yield 6 bytes which are not from the random pool, tsss, tsss.. Tue May 5 14:09:06 1998 Werner Koch (wk@isil.d.shuttle.de) * primegen.c (generate_elg_prime): Add arg mode, changed all callers and implemented mode 1. Mon Apr 27 14:41:58 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c (cipher_get_keylen): New. Sun Apr 26 14:44:52 1998 Werner Koch (wk@isil.d.shuttle.de) * tiger.c, tiger.h: New. Wed Apr 8 14:57:11 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (check_pubkey_algo2): New. Tue Apr 7 18:46:49 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c: New * misc.c (check_cipher_algo): Moved to cipher.c * cast5.c: Moved many functions to cipher.c * blowfish.c: Likewise. Sat Apr 4 19:52:08 1998 Werner Koch (wk@isil.d.shuttle.de) * cast5.c: Implemented and tested. Wed Apr 1 16:38:27 1998 Werner Koch (wk@isil.d.shuttle.de) * elgamal.c (elg_generate): Faster generation of x in some cases. Thu Mar 19 13:54:48 1998 Werner Koch (wk@isil.d.shuttle.de) * blowfish.c (blowfish_decode_cfb): changed XOR operation (blowfish_encode_cfb): Ditto. Thu Mar 12 14:04:05 1998 Werner Koch (wk@isil.d.shuttle.de) * sha1.c (transform): Rewrote * blowfish.c (encrypt): Unrolled for rounds == 16 (decrypt): Ditto. Tue Mar 10 16:32:08 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (transform): Unrolled the loop. Tue Mar 10 13:05:14 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (read_pool): Add pool_balance stuff. (get_random_bits): New. * elgamal.c (elg_generate): Now uses get_random_bits to generate x. Tue Mar 10 11:33:51 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_digest_length): New. Tue Mar 10 11:27:41 1998 Werner Koch (wk@isil.d.shuttle.de) * dsa.c (dsa_verify): Works. Mon Mar 9 12:59:08 1998 Werner Koch (wk@isil.d.shuttle.de) * dsa.c, dsa.h: Removed some unused code. Wed Mar 4 10:39:22 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_open): Add call to fast_random_poll. blowfish.c (blowfish_setkey): Ditto. Tue Mar 3 13:32:54 1998 Werner Koch (wk@isil.d.shuttle.de) * rmd160.c (rmd160_mixblock): New. * random.c: Restructured to start with a new RNG implementation. * random.h: New. Mon Mar 2 19:21:46 1998 Werner Koch (wk@isil.d.shuttle.de) * gost.c, gost.h: Removed because they did only conatin trash. Sun Mar 1 16:42:29 1998 Werner Koch (wk@isil.d.shuttle.de) * random.c (fill_buffer): removed error message if n == -1. Fri Feb 27 16:39:34 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c (md_enable): No init if called twice. Thu Feb 26 07:57:02 1998 Werner Koch (wk@isil.d.shuttle.de) * primegen.c (generate_elg_prime): Changed the progress printing. (gen_prime): Ditto. Tue Feb 24 12:28:42 1998 Werner Koch (wk@isil.d.shuttle.de) * md5.c, md.5 : Replaced by a modified version of md5.c from GNU textutils 1.22. Wed Feb 18 14:08:30 1998 Werner Koch (wk@isil.d.shuttle.de) * md.c, md.h : New debugging support Mon Feb 16 10:08:47 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (cipher_algo_to_string): New (pubkey_algo_to_string): New. (digest_algo_to_string): New. diff --git a/cipher/dsa.c b/cipher/dsa.c index cbd90ac5f..9154f49d6 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -1,434 +1,453 @@ /* dsa.c - DSA signature scheme * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include "util.h" #include "mpi.h" #include "cipher.h" #include "dsa.h" typedef struct { MPI p; /* prime */ MPI q; /* group order */ MPI g; /* group generator */ MPI y; /* g^x mod p */ } DSA_public_key; typedef struct { MPI p; /* prime */ MPI q; /* group order */ MPI g; /* group generator */ MPI y; /* g^x mod p */ MPI x; /* secret exponent */ } DSA_secret_key; static MPI gen_k( MPI q ); static void test_keys( DSA_secret_key *sk, unsigned qbits ); static int check_secret_key( DSA_secret_key *sk ); static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ); static void sign(MPI r, MPI s, MPI input, DSA_secret_key *skey); static int verify(MPI r, MPI s, MPI input, DSA_public_key *pkey); static void progress( int c ) { fputc( c, stderr ); } /**************** * Generate a random secret exponent k less than q */ static MPI gen_k( MPI q ) { MPI k = mpi_alloc_secure( mpi_get_nlimbs(q) ); - unsigned nbits = mpi_get_nbits(q); + unsigned int nbits = mpi_get_nbits(q); + unsigned int nbytes = (nbits+7)/8; + char *rndbuf = NULL; if( DBG_CIPHER ) log_debug("choosing a random k "); for(;;) { if( DBG_CIPHER ) progress('.'); - { char *p = get_random_bits( nbits, 1, 1 ); - mpi_set_buffer( k, p, (nbits+7)/8, 0 ); - m_free(p); - /* make sure that the number is of the exact lenght */ - if( mpi_test_bit( k, nbits-1 ) ) - mpi_set_highbit( k, nbits-1 ); - else { - mpi_set_highbit( k, nbits-1 ); - mpi_clear_bit( k, nbits-1 ); - } + + if( !rndbuf || nbits < 32 ) { + m_free(rndbuf); + rndbuf = get_random_bits( nbits, 1, 1 ); + } + else { /* change only some of the higher bits */ + /* we could imporove this by directly requesting more memory + * at the first call to get_random_bits() and use this the here + * maybe it is easier to do this directly in random.c */ + char *pp = get_random_bits( 32, 1, 1 ); + memcpy( rndbuf,pp, 4 ); + m_free(pp); } - if( !(mpi_cmp( k, q ) < 0) ) /* check: k < q */ + mpi_set_buffer( k, rndbuf, nbytes, 0 ); + if( mpi_test_bit( k, nbits-1 ) ) + mpi_set_highbit( k, nbits-1 ); + else { + mpi_set_highbit( k, nbits-1 ); + mpi_clear_bit( k, nbits-1 ); + } + + if( !(mpi_cmp( k, q ) < 0) ) { /* check: k < q */ + if( DBG_CIPHER ) + progress('+'); continue; /* no */ - if( !(mpi_cmp_ui( k, 0 ) > 0) ) /* check: k > 0 */ + } + if( !(mpi_cmp_ui( k, 0 ) > 0) ) { /* check: k > 0 */ + if( DBG_CIPHER ) + progress('-'); continue; /* no */ + } break; /* okay */ } + m_free(rndbuf); if( DBG_CIPHER ) progress('\n'); return k; } static void test_keys( DSA_secret_key *sk, unsigned qbits ) { DSA_public_key pk; MPI test = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); MPI out1_a = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); MPI out1_b = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); pk.p = sk->p; pk.q = sk->q; pk.g = sk->g; pk.y = sk->y; /*mpi_set_bytes( test, qbits, get_random_byte, 0 );*/ { char *p = get_random_bits( qbits, 0, 0 ); mpi_set_buffer( test, p, (qbits+7)/8, 0 ); m_free(p); } sign( out1_a, out1_b, test, sk ); if( !verify( out1_a, out1_b, test, &pk ) ) log_fatal("DSA:: sign, verify failed\n"); mpi_free( test ); mpi_free( out1_a ); mpi_free( out1_b ); } /**************** * Generate a DSA key pair with a key of size NBITS * Returns: 2 structures filled with all needed values * and an array with the n-1 factors of (p-1) */ static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ) { MPI p; /* the prime */ MPI q; /* the 160 bit prime factor */ MPI g; /* the generator */ MPI y; /* g^x mod p */ MPI x; /* the secret exponent */ MPI h, e; /* helper */ unsigned qbits; byte *rndbuf; assert( nbits >= 512 && nbits <= 1024 ); qbits = 160; p = generate_elg_prime( 1, nbits, qbits, NULL, ret_factors ); /* get q out of factors */ q = mpi_copy((*ret_factors)[0]); if( mpi_get_nbits(q) != qbits ) BUG(); /* find a generator g (h and e are helpers)*/ /* e = (p-1)/q */ e = mpi_alloc( mpi_get_nlimbs(p) ); mpi_sub_ui( e, p, 1 ); mpi_fdiv_q( e, e, q ); g = mpi_alloc( mpi_get_nlimbs(p) ); h = mpi_alloc_set_ui( 1 ); /* we start with 2 */ do { mpi_add_ui( h, h, 1 ); /* g = h^e mod p */ mpi_powm( g, h, e, p ); } while( !mpi_cmp_ui( g, 1 ) ); /* continue until g != 1 */ /* select a random number which has these properties: * 0 < x < q-1 * This must be a very good random number because this * is the secret part. */ if( DBG_CIPHER ) log_debug("choosing a random x "); assert( qbits >= 160 ); x = mpi_alloc_secure( mpi_get_nlimbs(q) ); mpi_sub_ui( h, q, 1 ); /* put q-1 into h */ rndbuf = NULL; do { if( DBG_CIPHER ) progress('.'); if( !rndbuf ) rndbuf = get_random_bits( qbits, 2, 1 ); else { /* change only some of the higher bits (= 2 bytes)*/ char *r = get_random_bits( 16, 2, 1 ); memcpy(rndbuf, r, 16/8 ); m_free(r); } mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 ); mpi_clear_highbit( x, qbits+1 ); } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) ); m_free(rndbuf); mpi_free( e ); mpi_free( h ); /* y = g^x mod p */ y = mpi_alloc( mpi_get_nlimbs(p) ); mpi_powm( y, g, x, p ); if( DBG_CIPHER ) { progress('\n'); log_mpidump("dsa p= ", p ); log_mpidump("dsa q= ", q ); log_mpidump("dsa g= ", g ); log_mpidump("dsa y= ", y ); log_mpidump("dsa x= ", x ); } /* copy the stuff to the key structures */ sk->p = p; sk->q = q; sk->g = g; sk->y = y; sk->x = x; /* now we can test our keys (this should never fail!) */ test_keys( sk, qbits ); } /**************** * Test whether the secret key is valid. * Returns: if this is a valid key. */ static int check_secret_key( DSA_secret_key *sk ) { int rc; MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); mpi_powm( y, sk->g, sk->x, sk->p ); rc = !mpi_cmp( y, sk->y ); mpi_free( y ); return rc; } /**************** * Make a DSA signature from HASH and put it into r and s. */ static void sign(MPI r, MPI s, MPI hash, DSA_secret_key *skey ) { MPI k; MPI kinv; MPI tmp; /* select a random k with 0 < k < q */ k = gen_k( skey->q ); /* r = (a^k mod p) mod q */ mpi_powm( r, skey->g, k, skey->p ); mpi_fdiv_r( r, r, skey->q ); /* kinv = k^(-1) mod q */ kinv = mpi_alloc( mpi_get_nlimbs(k) ); mpi_invm(kinv, k, skey->q ); /* s = (kinv * ( hash + x * r)) mod q */ tmp = mpi_alloc( mpi_get_nlimbs(skey->p) ); mpi_mul( tmp, skey->x, r ); mpi_add( tmp, tmp, hash ); mpi_mulm( s , kinv, tmp, skey->q ); mpi_free(k); mpi_free(kinv); mpi_free(tmp); } /**************** * Returns true if the signature composed from R and S is valid. */ static int verify(MPI r, MPI s, MPI hash, DSA_public_key *pkey ) { int rc; MPI w, u1, u2, v; MPI base[3]; MPI exp[3]; if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) ) return 0; /* assertion 0 < r < q failed */ if( !(mpi_cmp_ui( s, 0 ) > 0 && mpi_cmp( s, pkey->q ) < 0) ) return 0; /* assertion 0 < s < q failed */ w = mpi_alloc( mpi_get_nlimbs(pkey->q) ); u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); v = mpi_alloc( mpi_get_nlimbs(pkey->p) ); /* w = s^(-1) mod q */ mpi_invm( w, s, pkey->q ); /* u1 = (hash * w) mod q */ mpi_mulm( u1, hash, w, pkey->q ); /* u2 = r * w mod q */ mpi_mulm( u2, r, w, pkey->q ); /* v = g^u1 * y^u2 mod p mod q */ base[0] = pkey->g; exp[0] = u1; base[1] = pkey->y; exp[1] = u2; base[2] = NULL; exp[2] = NULL; mpi_mulpowm( v, base, exp, pkey->p ); mpi_fdiv_r( v, v, pkey->q ); rc = !mpi_cmp( v, r ); mpi_free(w); mpi_free(u1); mpi_free(u2); mpi_free(v); return rc; } /********************************************* ************** interface ****************** *********************************************/ int dsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) { DSA_secret_key sk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; generate( &sk, nbits, retfactors ); skey[0] = sk.p; skey[1] = sk.q; skey[2] = sk.g; skey[3] = sk.y; skey[4] = sk.x; return 0; } int dsa_check_secret_key( int algo, MPI *skey ) { DSA_secret_key sk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.q = skey[1]; sk.g = skey[2]; sk.y = skey[3]; sk.x = skey[4]; if( !check_secret_key( &sk ) ) return G10ERR_BAD_SECKEY; return 0; } int dsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) { DSA_secret_key sk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.q = skey[1]; sk.g = skey[2]; sk.y = skey[3]; sk.x = skey[4]; resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); sign( resarr[0], resarr[1], data, &sk ); return 0; } int dsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, int (*cmp)(void *, MPI), void *opaquev ) { DSA_public_key pk; if( algo != PUBKEY_ALGO_DSA ) return G10ERR_PUBKEY_ALGO; pk.p = pkey[0]; pk.q = pkey[1]; pk.g = pkey[2]; pk.y = pkey[3]; if( !verify( data[0], data[1], hash, &pk ) ) return G10ERR_BAD_SIGN; return 0; } unsigned dsa_get_nbits( int algo, MPI *pkey ) { if( algo != PUBKEY_ALGO_DSA ) return 0; return mpi_get_nbits( pkey[0] ); } /**************** * Return some information about the algorithm. We need algo here to * distinguish different flavors of the algorithm. * Returns: A pointer to string describing the algorithm or NULL if * the ALGO is invalid. * Usage: Bit 0 set : allows signing * 1 set : allows encryption */ const char * dsa_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, int *use ) { *npkey = 4; *nskey = 5; *nenc = 0; *nsig = 2; switch( algo ) { case PUBKEY_ALGO_DSA: *use = PUBKEY_USAGE_SIG; return "DSA"; default: *use = 0; return NULL; } } diff --git a/cipher/elgamal.c b/cipher/elgamal.c index 0e6b992c0..4b9758628 100644 --- a/cipher/elgamal.c +++ b/cipher/elgamal.c @@ -1,563 +1,586 @@ /* elgamal.c - ElGamal Public Key encryption * Copyright (C) 1998 Free Software Foundation, Inc. * * For a description of the algorithm, see: * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996. * ISBN 0-471-11709-9. Pages 476 ff. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include "util.h" #include "mpi.h" #include "cipher.h" #include "elgamal.h" typedef struct { MPI p; /* prime */ MPI g; /* group generator */ MPI y; /* g^x mod p */ } ELG_public_key; typedef struct { MPI p; /* prime */ MPI g; /* group generator */ MPI y; /* g^x mod p */ MPI x; /* secret exponent */ } ELG_secret_key; static void test_keys( ELG_secret_key *sk, unsigned nbits ); static MPI gen_k( MPI p ); static void generate( ELG_secret_key *sk, unsigned nbits, MPI **factors ); static int check_secret_key( ELG_secret_key *sk ); static void encrypt(MPI a, MPI b, MPI input, ELG_public_key *pkey ); static void decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ); static void sign(MPI a, MPI b, MPI input, ELG_secret_key *skey); static int verify(MPI a, MPI b, MPI input, ELG_public_key *pkey); static void progress( int c ) { fputc( c, stderr ); } static void test_keys( ELG_secret_key *sk, unsigned nbits ) { ELG_public_key pk; MPI test = mpi_alloc( 0 ); MPI out1_a = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); MPI out1_b = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); MPI out2 = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); pk.p = sk->p; pk.g = sk->g; pk.y = sk->y; /*mpi_set_bytes( test, nbits, get_random_byte, 0 );*/ { char *p = get_random_bits( nbits, 0, 0 ); mpi_set_buffer( test, p, (nbits+7)/8, 0 ); m_free(p); } encrypt( out1_a, out1_b, test, &pk ); decrypt( out2, out1_a, out1_b, sk ); if( mpi_cmp( test, out2 ) ) log_fatal("ElGamal operation: encrypt, decrypt failed\n"); sign( out1_a, out1_b, test, sk ); if( !verify( out1_a, out1_b, test, &pk ) ) log_fatal("ElGamal operation: sign, verify failed\n"); mpi_free( test ); mpi_free( out1_a ); mpi_free( out1_b ); mpi_free( out2 ); } /**************** * generate a random secret exponent k from prime p, so * that k is relatively prime to p-1 */ static MPI gen_k( MPI p ) { MPI k = mpi_alloc_secure( 0 ); MPI temp = mpi_alloc( mpi_get_nlimbs(p) ); MPI p_1 = mpi_copy(p); - unsigned nbits = mpi_get_nbits(p); + unsigned int nbits = mpi_get_nbits(p); + unsigned int nbytes = (nbits+7)/8; + char *rndbuf = NULL; if( DBG_CIPHER ) log_debug("choosing a random k "); mpi_sub_ui( p_1, p, 1); for(;;) { if( DBG_CIPHER ) progress('.'); - { char *pp = get_random_bits( nbits, 1, 1 ); - mpi_set_buffer( k, pp, (nbits+7)/8, 0 ); + if( !rndbuf || nbits < 32 ) { + m_free(rndbuf); + rndbuf = get_random_bits( nbits, 1, 1 ); + } + else { /* change only some of the higher bits */ + /* we could imporove this by directly requesting more memory + * at the first call to get_random_bits() and use this the here + * maybe it is easier to do this directly in random.c */ + char *pp = get_random_bits( 32, 1, 1 ); + memcpy( rndbuf,pp, 4 ); m_free(pp); + } + mpi_set_buffer( k, rndbuf, nbytes, 0 ); + + for(;;) { /* make sure that the number is of the exact lenght */ if( mpi_test_bit( k, nbits-1 ) ) mpi_set_highbit( k, nbits-1 ); else { mpi_set_highbit( k, nbits-1 ); mpi_clear_bit( k, nbits-1 ); } + if( !(mpi_cmp( k, p_1 ) < 0) ) { /* check: k < (p-1) */ + if( DBG_CIPHER ) + progress('+'); + break; /* no */ + } + if( !(mpi_cmp_ui( k, 0 ) > 0) ) { /* check: k > 0 */ + if( DBG_CIPHER ) + progress('-'); + break; /* no */ + } + if( mpi_gcd( temp, k, p_1 ) ) + goto found; /* okay, k is relatively prime to (p-1) */ + mpi_add_ui( k, k, 1 ); } - if( !(mpi_cmp( k, p_1 ) < 0) ) /* check: k < (p-1) */ - continue; /* no */ - if( !(mpi_cmp_ui( k, 0 ) > 0) ) /* check: k > 0 */ - continue; /* no */ - if( mpi_gcd( temp, k, p_1 ) ) - break; /* okay, k is relatively prime to (p-1) */ } + found: + m_free(rndbuf); if( DBG_CIPHER ) progress('\n'); mpi_free(p_1); mpi_free(temp); return k; } /**************** * Generate a key pair with a key of size NBITS * Returns: 2 structures filles with all needed values * and an array with n-1 factors of (p-1) */ static void generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) { MPI p; /* the prime */ MPI p_min1; MPI g; MPI x; /* the secret exponent */ MPI y; MPI temp; unsigned qbits; byte *rndbuf; p_min1 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); temp = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); if( nbits < 512 ) qbits = 120; else if( nbits <= 1024 ) qbits = 160; else if( nbits <= 2048 ) qbits = 200; else qbits = 240; g = mpi_alloc(1); p = generate_elg_prime( 0, nbits, qbits, g, ret_factors ); mpi_sub_ui(p_min1, p, 1); /* select a random number which has these properties: * 0 < x < p-1 * This must be a very good random number because this is the * secret part. The prime is public and may be shared anyway, * so a random generator level of 1 is used for the prime. */ x = mpi_alloc_secure( nbits/BITS_PER_MPI_LIMB ); if( DBG_CIPHER ) log_debug("choosing a random x "); rndbuf = NULL; do { if( DBG_CIPHER ) progress('.'); if( rndbuf ) { /* change only some of the higher bits */ if( nbits < 16 ) {/* should never happen ... */ m_free(rndbuf); rndbuf = get_random_bits( nbits, 2, 1 ); } else { char *r = get_random_bits( 16, 2, 1 ); memcpy(rndbuf, r, 16/8 ); m_free(r); } } else rndbuf = get_random_bits( nbits, 2, 1 ); mpi_set_buffer( x, rndbuf, (nbits+7)/8, 0 ); mpi_clear_highbit( x, nbits+1 ); } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, p_min1 )<0 ) ); m_free(rndbuf); y = mpi_alloc(nbits/BITS_PER_MPI_LIMB); mpi_powm( y, g, x, p ); if( DBG_CIPHER ) { progress('\n'); log_mpidump("elg p= ", p ); log_mpidump("elg g= ", g ); log_mpidump("elg y= ", y ); log_mpidump("elg x= ", x ); } /* copy the stuff to the key structures */ sk->p = p; sk->g = g; sk->y = y; sk->x = x; /* now we can test our keys (this should never fail!) */ test_keys( sk, nbits - 64 ); mpi_free( p_min1 ); mpi_free( temp ); } /**************** * Test whether the secret key is valid. * Returns: if this is a valid key. */ static int check_secret_key( ELG_secret_key *sk ) { int rc; MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); mpi_powm( y, sk->g, sk->x, sk->p ); rc = !mpi_cmp( y, sk->y ); mpi_free( y ); return rc; } static void encrypt(MPI a, MPI b, MPI input, ELG_public_key *pkey ) { MPI k; /* Note: maybe we should change the interface, so that it * is possible to check that input is < p and return an * error code. */ k = gen_k( pkey->p ); mpi_powm( a, pkey->g, k, pkey->p ); /* b = (y^k * input) mod p * = ((y^k mod p) * (input mod p)) mod p * and because input is < p * = ((y^k mod p) * input) mod p */ mpi_powm( b, pkey->y, k, pkey->p ); mpi_mulm( b, b, input, pkey->p ); #if 0 if( DBG_CIPHER ) { log_mpidump("elg encrypted y= ", pkey->y); log_mpidump("elg encrypted p= ", pkey->p); log_mpidump("elg encrypted k= ", k); log_mpidump("elg encrypted M= ", input); log_mpidump("elg encrypted a= ", a); log_mpidump("elg encrypted b= ", b); } #endif mpi_free(k); } static void decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ) { MPI t1 = mpi_alloc_secure( mpi_get_nlimbs( skey->p ) ); /* output = b/(a^x) mod p */ mpi_powm( t1, a, skey->x, skey->p ); mpi_invm( t1, t1, skey->p ); mpi_mulm( output, b, t1, skey->p ); #if 0 if( DBG_CIPHER ) { log_mpidump("elg decrypted x= ", skey->x); log_mpidump("elg decrypted p= ", skey->p); log_mpidump("elg decrypted a= ", a); log_mpidump("elg decrypted b= ", b); log_mpidump("elg decrypted M= ", output); } #endif mpi_free(t1); } /**************** * Make an Elgamal signature out of INPUT */ static void sign(MPI a, MPI b, MPI input, ELG_secret_key *skey ) { MPI k; MPI t = mpi_alloc( mpi_get_nlimbs(a) ); MPI inv = mpi_alloc( mpi_get_nlimbs(a) ); MPI p_1 = mpi_copy(skey->p); /* * b = (t * inv) mod (p-1) * b = (t * inv(k,(p-1),(p-1)) mod (p-1) * b = (((M-x*a) mod (p-1)) * inv(k,(p-1),(p-1))) mod (p-1) * */ mpi_sub_ui(p_1, p_1, 1); k = gen_k( skey->p ); mpi_powm( a, skey->g, k, skey->p ); mpi_mul(t, skey->x, a ); mpi_subm(t, input, t, p_1 ); while( mpi_is_neg(t) ) mpi_add(t, t, p_1); mpi_invm(inv, k, p_1 ); mpi_mulm(b, t, inv, p_1 ); #if 0 if( DBG_CIPHER ) { log_mpidump("elg sign p= ", skey->p); log_mpidump("elg sign g= ", skey->g); log_mpidump("elg sign y= ", skey->y); log_mpidump("elg sign x= ", skey->x); log_mpidump("elg sign k= ", k); log_mpidump("elg sign M= ", input); log_mpidump("elg sign a= ", a); log_mpidump("elg sign b= ", b); } #endif mpi_free(k); mpi_free(t); mpi_free(inv); mpi_free(p_1); } /**************** * Returns true if the signature composed of A and B is valid. */ static int verify(MPI a, MPI b, MPI input, ELG_public_key *pkey ) { int rc; MPI t1; MPI t2; MPI base[4]; MPI exp[4]; if( !(mpi_cmp_ui( a, 0 ) > 0 && mpi_cmp( a, pkey->p ) < 0) ) return 0; /* assertion 0 < a < p failed */ t1 = mpi_alloc( mpi_get_nlimbs(a) ); t2 = mpi_alloc( mpi_get_nlimbs(a) ); #if 0 /* t1 = (y^a mod p) * (a^b mod p) mod p */ mpi_powm( t1, pkey->y, a, pkey->p ); mpi_powm( t2, a, b, pkey->p ); mpi_mulm( t1, t1, t2, pkey->p ); /* t2 = g ^ input mod p */ mpi_powm( t2, pkey->g, input, pkey->p ); rc = !mpi_cmp( t1, t2 ); #elif 0 /* t1 = (y^a mod p) * (a^b mod p) mod p */ base[0] = pkey->y; exp[0] = a; base[1] = a; exp[1] = b; base[2] = NULL; exp[2] = NULL; mpi_mulpowm( t1, base, exp, pkey->p ); /* t2 = g ^ input mod p */ mpi_powm( t2, pkey->g, input, pkey->p ); rc = !mpi_cmp( t1, t2 ); #else /* t1 = g ^ - input * y ^ a * a ^ b mod p */ mpi_invm(t2, pkey->g, pkey->p ); base[0] = t2 ; exp[0] = input; base[1] = pkey->y; exp[1] = a; base[2] = a; exp[2] = b; base[3] = NULL; exp[3] = NULL; mpi_mulpowm( t1, base, exp, pkey->p ); rc = !mpi_cmp_ui( t1, 1 ); #endif mpi_free(t1); mpi_free(t2); return rc; } /********************************************* ************** interface ****************** *********************************************/ int elg_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; generate( &sk, nbits, retfactors ); skey[0] = sk.p; skey[1] = sk.g; skey[2] = sk.y; skey[3] = sk.x; return 0; } int elg_check_secret_key( int algo, MPI *skey ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; if( !check_secret_key( &sk ) ) return G10ERR_BAD_SECKEY; return 0; } int elg_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) { ELG_public_key pk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; pk.p = pkey[0]; pk.g = pkey[1]; pk.y = pkey[2]; resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.p ) ); resarr[1] = mpi_alloc( mpi_get_nlimbs( pk.p ) ); encrypt( resarr[0], resarr[1], data, &pk ); return 0; } int elg_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; *result = mpi_alloc_secure( mpi_get_nlimbs( sk.p ) ); decrypt( *result, data[0], data[1], &sk ); return 0; } int elg_sign( int algo, MPI *resarr, MPI data, MPI *skey ) { ELG_secret_key sk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; sk.p = skey[0]; sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); sign( resarr[0], resarr[1], data, &sk ); return 0; } int elg_verify( int algo, MPI hash, MPI *data, MPI *pkey, int (*cmp)(void *, MPI), void *opaquev ) { ELG_public_key pk; if( !is_ELGAMAL(algo) ) return G10ERR_PUBKEY_ALGO; pk.p = pkey[0]; pk.g = pkey[1]; pk.y = pkey[2]; if( !verify( data[0], data[1], hash, &pk ) ) return G10ERR_BAD_SIGN; return 0; } unsigned elg_get_nbits( int algo, MPI *pkey ) { if( !is_ELGAMAL(algo) ) return 0; return mpi_get_nbits( pkey[0] ); } /**************** * Return some information about the algorithm. We need algo here to * distinguish different flavors of the algorithm. * Returns: A pointer to string describing the algorithm or NULL if * the ALGO is invalid. * Usage: Bit 0 set : allows signing * 1 set : allows encryption * NOTE: This function allows signing also for ELG-E, which is not * okay but a bad hack to allow to work with old gpg keys. The real check * is done in the gnupg ocde depending on the packet version. */ const char * elg_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, int *use ) { *npkey = 3; *nskey = 4; *nenc = 2; *nsig = 2; switch( algo ) { case PUBKEY_ALGO_ELGAMAL: *use = PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC; return "ELG"; case PUBKEY_ALGO_ELGAMAL_E: *use = PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC; return "ELG-E"; default: *use = 0; return NULL; } } diff --git a/cipher/random.c b/cipher/random.c index 5fa45ea86..8ade26c11 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -1,437 +1,477 @@ /* random.c - random number generator * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /**************** * This random number generator is modelled after the one described * in Peter Gutmann's Paper: "Software Generation of Practically * Strong Random Numbers". */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GETHRTIME #include #endif #ifdef HAVE_GETTIMEOFDAY #include #endif #ifdef HAVE_GETRUSAGE #include #endif #include "util.h" #include "rmd.h" #include "ttyio.h" #include "i18n.h" #include "random.h" #include "rand-internal.h" #include "dynload.h" #ifndef RAND_MAX /* for SunOS */ #define RAND_MAX 32767 #endif #if SIZEOF_UNSIGNED_LONG == 8 #define ADD_VALUE 0xa5a5a5a5a5a5a5a5 #elif SIZEOF_UNSIGNED_LONG == 4 #define ADD_VALUE 0xa5a5a5a5 #else #error weird size for an unsigned long #endif #define BLOCKLEN 64 /* hash this amount of bytes */ #define DIGESTLEN 20 /* into a digest of this length (rmd160) */ /* poolblocks is the number of digests which make up the pool * and poolsize must be a multiple of the digest length * to make the AND operations faster, the size should also be * a multiple of ulong */ #define POOLBLOCKS 30 #define POOLSIZE (POOLBLOCKS*DIGESTLEN) #if (POOLSIZE % SIZEOF_UNSIGNED_LONG) #error Please make sure that poolsize is a multiple of ulong #endif #define POOLWORDS (POOLSIZE / SIZEOF_UNSIGNED_LONG) static int is_initialized; #define MASK_LEVEL(a) do {if( a > 2 ) a = 2; else if( a < 0 ) a = 0; } while(0) static char *rndpool; /* allocated size is POOLSIZE+BLOCKLEN */ static char *keypool; /* allocated size is POOLSIZE+BLOCKLEN */ static size_t pool_readpos; static size_t pool_writepos; static int pool_filled; static int pool_balance; static int just_mixed; static int secure_alloc; static int quick_test; static int faked_rng; static void read_pool( byte *buffer, size_t length, int level ); static void add_randomness( const void *buffer, size_t length, int source ); static void random_poll(void); static void read_random_source( int requester, size_t length, int level); static int gather_faked( void (*add)(const void*, size_t, int), int requester, size_t length, int level ); +static struct { + ulong mixrnd; + ulong mixkey; + ulong slowpolls; + ulong fastpolls; + ulong getbytes1; + ulong ngetbytes1; + ulong getbytes2; + ulong ngetbytes2; + ulong addbytes; + ulong naddbytes; +} rndstats; static void initialize(void) { /* The data buffer is allocated somewhat larger, so that * we can use this extra space (which is allocated in secure memory) * as a temporary hash buffer */ rndpool = secure_alloc ? m_alloc_secure_clear(POOLSIZE+BLOCKLEN) : m_alloc_clear(POOLSIZE+BLOCKLEN); keypool = secure_alloc ? m_alloc_secure_clear(POOLSIZE+BLOCKLEN) : m_alloc_clear(POOLSIZE+BLOCKLEN); is_initialized = 1; cipher_modules_constructor(); } + +void +random_dump_stats() +{ + fprintf(stderr, + "random usage: poolsize=%d mixed=%lu polls=%lu/%lu added=%lu/%lu\n" + " outmix=%lu getlvl1=%lu/%lu getlvl2=%lu/%lu\n", + POOLSIZE, rndstats.mixrnd, rndstats.slowpolls, rndstats.fastpolls, + rndstats.naddbytes, rndstats.addbytes, + rndstats.mixkey, rndstats.ngetbytes1, rndstats.getbytes1, + rndstats.ngetbytes2, rndstats.getbytes2 ); +} + void secure_random_alloc() { secure_alloc = 1; } int quick_random_gen( int onoff ) { int last; read_random_source(0,0,0); /* init */ last = quick_test; if( onoff != -1 ) quick_test = onoff; return faked_rng? 1 : last; } /**************** * Fill the buffer with LENGTH bytes of cryptographically strong * random bytes. level 0 is not very strong, 1 is strong enough * for most usage, 2 is good for key generation stuff but may be very slow. */ void randomize_buffer( byte *buffer, size_t length, int level ) { char *p = get_random_bits( length*8, level, 1 ); memcpy( buffer, p, length ); m_free(p); } int random_is_faked() { if( !is_initialized ) initialize(); return faked_rng || quick_test; } /**************** * Return a pointer to a randomized buffer of level 0 and LENGTH bits * caller must free the buffer. * Note: The returned value is rounded up to bytes. */ byte * get_random_bits( size_t nbits, int level, int secure ) { byte *buf, *p; size_t nbytes = (nbits+7)/8; if( quick_test && level > 1 ) level = 1; MASK_LEVEL(level); + if( level == 1 ) { + rndstats.getbytes1 += nbytes; + rndstats.ngetbytes1++; + } + else if( level >= 2 ) { + rndstats.getbytes2 += nbytes; + rndstats.ngetbytes2++; + } + buf = secure && secure_alloc ? m_alloc_secure( nbytes ) : m_alloc( nbytes ); for( p = buf; nbytes > 0; ) { size_t n = nbytes > POOLSIZE? POOLSIZE : nbytes; read_pool( p, n, level ); nbytes -= n; p += n; } return buf; } /**************** * Mix the pool */ static void mix_pool(byte *pool) { char *hashbuf = pool + POOLSIZE; char *p, *pend; int i, n; RMD160_CONTEXT md; rmd160_init( &md ); #if DIGESTLEN != 20 #error must have a digest length of 20 for ripe-md-160 #endif /* loop over the pool */ pend = pool + POOLSIZE; memcpy(hashbuf, pend - DIGESTLEN, DIGESTLEN ); memcpy(hashbuf+DIGESTLEN, pool, BLOCKLEN-DIGESTLEN); rmd160_mixblock( &md, hashbuf); memcpy(pool, hashbuf, 20 ); p = pool; for( n=1; n < POOLBLOCKS; n++ ) { memcpy(hashbuf, p, DIGESTLEN ); p += DIGESTLEN; if( p+DIGESTLEN+BLOCKLEN < pend ) memcpy(hashbuf+DIGESTLEN, p+DIGESTLEN, BLOCKLEN-DIGESTLEN); else { char *pp = p+DIGESTLEN; for(i=DIGESTLEN; i < BLOCKLEN; i++ ) { if( pp >= pend ) pp = pool; hashbuf[i] = *pp++; } } rmd160_mixblock( &md, hashbuf); memcpy(p, hashbuf, 20 ); } } static void read_pool( byte *buffer, size_t length, int level ) { int i; ulong *sp, *dp; if( length >= POOLSIZE ) BUG(); /* not allowed */ /* for level 2 make sure that there is enough random in the pool */ if( level == 2 && pool_balance < length ) { size_t needed; if( pool_balance < 0 ) pool_balance = 0; needed = length - pool_balance; if( needed > POOLSIZE ) BUG(); read_random_source( 3, needed, 2 ); pool_balance += needed; } /* make sure the pool is filled */ while( !pool_filled ) random_poll(); /* do always a fast random poll */ fast_random_poll(); if( !level ) { /* no need for cryptographic strong random */ /* create a new pool */ for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; i < POOLWORDS; i++, dp++, sp++ ) *dp = *sp + ADD_VALUE; /* must mix both pools */ - mix_pool(rndpool); - mix_pool(keypool); + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; memcpy( buffer, keypool, length ); } else { /* mix the pool (if add_randomness() didn't it) */ - if( !just_mixed ) + if( !just_mixed ) { mix_pool(rndpool); + rndstats.mixrnd++; + } /* create a new pool */ for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; i < POOLWORDS; i++, dp++, sp++ ) *dp = *sp + ADD_VALUE; /* and mix both pools */ - mix_pool(rndpool); - mix_pool(keypool); + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; /* read the required data * we use a readpoiter to read from a different postion each * time */ while( length-- ) { *buffer++ = keypool[pool_readpos++]; if( pool_readpos >= POOLSIZE ) pool_readpos = 0; pool_balance--; } if( pool_balance < 0 ) pool_balance = 0; /* and clear the keypool */ memset( keypool, 0, POOLSIZE ); } } /**************** * Add LENGTH bytes of randomness from buffer to the pool. * source may be used to specify the randomness source. */ static void add_randomness( const void *buffer, size_t length, int source ) { const byte *p = buffer; if( !is_initialized ) initialize(); + rndstats.addbytes += length; + rndstats.naddbytes++; while( length-- ) { rndpool[pool_writepos++] = *p++; if( pool_writepos >= POOLSIZE ) { if( source > 1 ) pool_filled = 1; pool_writepos = 0; - mix_pool(rndpool); + mix_pool(rndpool); rndstats.mixrnd++; just_mixed = !length; } } } static void random_poll() { + rndstats.slowpolls++; read_random_source( 2, POOLSIZE/5, 1 ); } void fast_random_poll() { static void (*fnc)( void (*)(const void*, size_t, int), int) = NULL; static int initialized = 0; + rndstats.fastpolls++; if( !initialized ) { if( !is_initialized ) initialize(); initialized = 1; fnc = dynload_getfnc_fast_random_poll(); } if( fnc ) { (*fnc)( add_randomness, 1 ); return; } /* fall back to the generic function */ #if HAVE_GETHRTIME { hrtime_t tv; tv = gethrtime(); add_randomness( &tv, sizeof(tv), 1 ); } #elif HAVE_GETTIMEOFDAY { struct timeval tv; if( gettimeofday( &tv, NULL ) ) BUG(); add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 ); } #else /* use times */ #ifndef HAVE_DOSISH_SYSTEM { struct tms buf; times( &buf ); add_randomness( &buf, sizeof buf, 1 ); } #endif #endif #ifdef HAVE_GETRUSAGE { struct rusage buf; if( getrusage( RUSAGE_SELF, &buf ) ) BUG(); add_randomness( &buf, sizeof buf, 1 ); memset( &buf, 0, sizeof buf ); } #endif } static void read_random_source( int requester, size_t length, int level ) { static int (*fnc)(void (*)(const void*, size_t, int), int, size_t, int) = NULL; if( !fnc ) { if( !is_initialized ) initialize(); fnc = dynload_getfnc_gather_random(); if( !fnc ) { faked_rng = 1; fnc = gather_faked; } if( !requester && !length && !level ) return; /* init only */ } if( (*fnc)( add_randomness, requester, length, level ) < 0 ) log_fatal("No way to gather entropy for the RNG\n"); } static int gather_faked( void (*add)(const void*, size_t, int), int requester, size_t length, int level ) { static int initialized=0; size_t n; char *buffer, *p; if( !initialized ) { log_info(_("WARNING: using insecure random number generator!!\n")); tty_printf(_("The random number generator is only a kludge to let\n" "it run - it is in no way a strong RNG!\n\n" "DON'T USE ANY DATA GENERATED BY THIS PROGRAM!!\n\n")); initialized=1; #ifdef HAVE_RAND srand(make_timestamp()*getpid()); #else srandom(make_timestamp()*getpid()); #endif } p = buffer = m_alloc( length ); n = length; #ifdef HAVE_RAND while( n-- ) *p++ = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1); #else while( n-- ) *p++ = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1); #endif add_randomness( buffer, length, requester ); m_free(buffer); return 0; /* okay */ } diff --git a/cipher/random.h b/cipher/random.h index 4b1d56d57..64ffdb5e6 100644 --- a/cipher/random.h +++ b/cipher/random.h @@ -1,33 +1,34 @@ /* random.h - random functions * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifndef G10_RANDOM_H #define G10_RANDOM_H #include "types.h" /*-- random.c --*/ +void random_dump_stats(void); void secure_random_alloc(void); int quick_random_gen( int onoff ); int random_is_faked(void); void randomize_buffer( byte *buffer, size_t length, int level ); byte *get_random_bits( size_t nbits, int level, int secure ); void fast_random_poll( void ); #endif /*G10_RANDOM_H*/ diff --git a/g10/ChangeLog b/g10/ChangeLog index ca68a142a..416016ef7 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,2116 +1,2125 @@ +Fri Jul 2 11:45:54 CEST 1999 Werner Koch + + + * g10.c (g10_exit): Dump random stats. + + * sig-check.c (check_key_signature,check_key_signature2): Enhanced + version and wrapper for old function. + (do_signature_check,signature_check): Ditto. + Thu Jul 1 12:47:31 CEST 1999 Werner Koch * keyedit.c (show_key_with_all_names): Print a notice for disabled keys. (enable_disable_keys): Add functionality * pkclist.c (edit_ownertrust): preserve disabled state. (build_pk_list): Skip disabled keys. * trustdb.c (upd_one_ownertrust): Ditto. (build_cert_tree): Mask the ownertrust. (trust_letter): Mask the value. (do_check): Take disabled flag into account. * passphrase.c (passphrase_to_dek): Add a pubkey_alfo arg and changed all callers. * g10.c (utf8_strings): 2 new options. * trustdb.c (insert_trust_record_by_pk): New, replaces the next one. (insert_trust_record): Now takes a keyblock as arg. Changed all callers to use the appropritae function. * openfile.c (ask_outfile_name): New. * plaintext.c (handle_plaintext): Ask for filename if there is no valid syntax. Don't use fname varbatim but filter it. Tue Jun 29 21:44:25 CEST 1999 Werner Koch * trustdb.h (TRUST_FLAG_DISABLED): New. * status.c (USE_CAPABILITIES): Capabilities support (Remi). * tdbio.c : Added new fields to the DIR record. (tdbio_write_record): Fixed the update of the hash tables. (tdbio_delete_record): Drop the record from the hash tables. (drop_from_hashtbl): New. * status.c (cpr_get): Special online help mode. * helptext.c ("keyedit.cmd"): Removed. * keyedit.c (keyedit_menu): Use only help system. (enable_disable_key): New bit doies not yet work. Sat Jun 26 12:15:59 CEST 1999 Werner Koch * dearmor.c (enarmor_file): Fixed comment string. * tdbdump.c (export_ownertrust): Text fix. * tbio.c (tdbio_invalid): Ditto. * parse-packet.c (parse_key): Made temp buffer larger. * Makefile.am (install-data-local): Add missing backslashes Tue Jun 15 12:21:08 CEST 1999 Werner Koch * g10.c (main): Made iterated+salted the default S2K method. * Makefile.am (install-data-local): Use DESTDIR. * passphrase.c (passphrase_to_dek): Emit missing-passphrase while in batchmode. * parse-packet.c (parse_pubkeyenc): Fixed a SEGV. Mon Jun 14 21:18:54 CEST 1999 Michael Roth * g10.c: New options --openpgp, --no-tty, --emit-version, --default-comment and --lock-multiple Thu Jun 10 14:18:23 CEST 1999 Werner Koch * free-packet.c (free_encrypted): Fixed EOF case (Remi). (free_plaintext): Ditto. * helptext.c (keyedit.delsig.unknown): New (Remi). * keyedit.c (print_and_check_one_sig): Add arg print_without_key and changed all callers to make use of it (Remi): Tue Jun 8 13:36:25 CEST 1999 Werner Koch * keylist.c (print_key_data): New and called elsewhere. * g10.c: New option --with-key-data Wed Jun 2 14:17:19 CEST 1999 Werner Koch * mainproc.c (proc_tree): Yet another bad hack to cope with broken pgp2 created detached messages in textmode. Tue Jun 1 16:01:46 CEST 1999 Werner Koch * openfile.c (make_outfile_name): New. * plaintext.c (handle_plaintext): Outputfile is now the inputfile without the suffix. * g10.c: New option --use-embedded-filename Mon May 31 19:41:10 CEST 1999 Werner Koch * g10.c (main): Fix for SHM init (Michael). * compress.c, encr-data.c, mdfilter.c, plaintext.c, free-packet.c: Speed patches (Rémi). Thu May 27 09:40:55 CEST 1999 Werner Koch * status.c (cpr_get_answer_yes_no_quit): New. * keyedit.c (menu_delsig): New. (check_all_keysigs): Splitted. (print_and_check_one_sig): New. Wed May 26 14:36:29 CEST 1999 Werner Koch * build-packet.c (build_sig_subpkt): Support large packets. * parse-packet.c (enum_sig_subpkt): Replaces parse_sig_subpkt. * mainproc.c (print_notation_data): Print all notation packets. * g10.c (add_notation_data): Add a way to specify the critical flag. (main): Add option --set-policy-url. (check_policy_url): Basic checks. * sign.c (mk_notation_and_policy): Replaces mk_notation. * parse-packet.c (can_handle_critical): Moved decision whether we can handle critical subpacket to an extra function. Tue May 25 19:50:32 CEST 1999 Werner Koch * sign.c (sign_file): Always use compression algo 1 for signed onyl file becuase we can´ be sure the the verifier supports other algorithms. * build-packet.c (build_sig_subpkt): Support for notation data. * sign.c (sign_file,clearsign_file,make_keysig_packet): Ditto. (mk_notation): New. * g10.c (add_notation_data): New and add option -N * mainproc.c (print_notation_data): New. (check_sig_and_print): Print any notation data of the signed text. Sun May 23 14:20:22 CEST 1999 Werner Koch * pkclist.c (check_signatures_trust): Print a warning and return immediateley if opt.always_trust is true. * g10.c (main): Corrected handling of no-default-keyring * pkclist.c (algo_available): Disable Twofish until we have settled how to do the MDC. * hkp.c: Disable everything for mingw32 Sat May 22 22:47:26 CEST 1999 Werner Koch * mainproc.c (check_sig_and_print): Add sig creation time to the VALIDSIG status output. Add more info to the ERRSIG output. * sig-check.c (signature_check): Add sig time after epoch to SIG_ID. * import.c (import_one): Merge duplicate user IDs. (collapse_uids): New. * kbnode.c (move_kbnode): New. (remove_kbnode): New. * keyedit.c (keyedit_menu): Call collapse_uids. * g10.c: new option --logger-fd. * import.c: s/log_*_f/log_*/ Thu May 20 14:04:08 CEST 1999 Werner Koch * misc.c (pull_in_libs): do the volatile only for gcc * sig-check (signature_check): Emit SIG_iD only for classes 0 and 1. * armor.c (armor_filter): Add detection of PGP2 created clearsigs. (fake_packet): A tab is not a WS for pgp2 - handle this. * textfilter.c (len_without_trailing_chars): New. (copy_clearsig_text): Add pgp2mode arg. * sign.c (clearsign_file): pass old_style to the above fnc. Wed May 19 16:04:30 CEST 1999 Werner Koch * g10.c: New option --interactive. * mainproc.c (proc_plaintext): Add workaround for pgp2 bug (do_check_sig): Ditto. (proc_tree): Ditto. * plaintext.c (do_hash): Ditto. (hash_datafiles): Ditto, add an arg, changed all callers. * mdfilter.c (md_filter): Add support for the alternate hash context. Mon May 17 21:54:43 CEST 1999 Werner Koch * parse-packet.c (parse_encrypted): Support for PKT_ENCRYPTED_MDC. * build-packet.c (do_encrypted_mdc): Ditto. * cipher.c (write_header): Add mdc hashing. (cipher_filter): write out the hash. * mainproc.c (do_proc_packets): Add PKT_ENCRYPTED_MDC. * encr-data.c (decrypt_data): Add mdc hashing. (mdc_decode_filter): New. * parse-packet.c (parse_sig_subpkt): Fixed stupid bug for subpkt length calculation (parse_signature): Fixed even more stupid bug. Sat May 8 19:28:08 CEST 1999 Werner Koch * build-packet.c (do_signature): Removed MDC hack. * encode.c (encode_crypt_mdc): Removed. * mainproc.c (do_check_sig): Removed MDC hack. (check_sig_and_print): Ditto. * parse-packet.c (parse_signature): Ditto. * sig-check.c (mdc_kludge_check): Ditto. * free-packte.c (copy_signature, free_seckey_enc): Ditto. * parse-packet.c (parse_signature,parse_key): Store data of unknown algorithms with mpi_set_opaque inseatd of the old faked data stuff. (read_rest): Removed. (read_rest2): Renamed to read_rest * build-packet.c (write_fake_data): Use mpi_get_opaque. * free-packet.c (cp_fake_data): Removed and cahnged all callers to use mpi_copy. (free_pubkey_enc,free_seckey_enc,release_public_key_parts, release_secret_key_parts): Use mpi_free for opaque data. Thu May 6 14:18:17 CEST 1999 Werner Koch * trustdb.c (check_trust): Check for revoked subkeys. * pkclist.c (do_we_trust): Handled revoked subkeys. (do_we_trust_pre): Ditto. (check_signatures_trust): Ditto. * build-packet.c (hash_public_key): Fix for ancient g10 keys. * mainproc.c (do_proc_packets): Return EOF if no data has been read. * g10.c (main): Catch errors for default operation. Thu Apr 29 12:29:22 CEST 1999 Werner Koch * sign.c (sign_file): Fixed hashing in case of no subpackets. (clearsign_file): Ditto. (make_keysig_packet): Ditto. Wed Apr 28 13:03:03 CEST 1999 Werner Koch * keyedit.c (keyedit_menu): Add new command revkey. * (menu_revkey): New. Mon Apr 26 17:48:15 CEST 1999 Werner Koch * parse-packet.c (parse_signature): Add the MDC hack. * build-packet.c (do_signature): Ditto. * free-packet.c (free_seckey_enc,copy_signature,cmp_signatures): Ditto. * mainproc.c (do_check_sig): Ditto. * sig-check.c (mdc_kludge_check): New. * encode.c (encrypt_mdc_file): New. * keyedit.c (check_all_keysigs): List revocations. * (menu_revsig): New. * sign (make_keysig_packet): Support for class 0x30. Sun Apr 18 20:48:15 CEST 1999 Werner Koch * pkclist.c (select_algo_from_prefs): Fixed the case that one key has no preferences (Remi Guyomarch). keylist.c (list_keyblock): ulti_hack to propagate trust to all uids. Sun Apr 18 10:11:28 CEST 1999 Werner Koch * seckey-cert.c (do_check): Use real IV instead of a 0 one, so that it works even if the length of the IV doesn't match the blocksize. Removed the save_iv stuff. (protect_secret_key): Likewise. Create the IV here. * packet.h (PKT_secret_key): Increased size of IV field and add a ivlen field. * parse-packet.c (parse_key): Use the len protect.ivlen. * build-packet.c (do_secret_key). Ditto. * getkey.c (key_byname): Close keyblocks. * Makefile.am (gpgm): Removed this * g10.c: Merged gpg and gpgm * import.c (import): Utilize option quiet. * tdbio.c (tdbio_set_dbname): Ditto. * ringedit.c (add_keyblock_resource,keyring_copy): Ditto. * keyedit.c (sign_uids): Add some batch support. * g10.c (main): add call to tty_batchmode. Fri Apr 9 12:26:25 CEST 1999 Werner Koch * status.c (write_status_text): Some more status codes. * passphrase_to_dek (passphrase_to_dek): add a status code. * seckey_cert.c (check_secret_key): Likewise. * encr-data.c (decrypt_data): Reverse the last changes * cipher.c (write_header): Ditto. * parse-packet.c (parse_key): Dropped kludge for ancient blowfish mode. Thu Apr 8 09:35:53 CEST 1999 Werner Koch * mainproc.c (proc_encrypted): Add a new status output * passphrase.c (passphrase_to_dek): Ditto. * status.h status.c: Add new status tokens. Wed Apr 7 20:51:39 CEST 1999 Werner Koch * encr-data.c (decrypt_data): Fixes for 128 bit blocksize * cipher.c (write_header): Ditto. * seckey-cert.c (do_check): Ditto. (protect_secret_key). Ditto. * misc.c (print_cipher_algo_note): Twofish is now a standard algo. * keygen.c (do_create): Fixed spelling (Gaël Quéri) (ask_keysize): Only allow keysizes up to 4096 * ringedit.c (add_keyblock_resource): chmod newly created secrings. * import.c (delete_inv_parts): Fixed accidently deleted subkeys. Tue Apr 6 19:58:12 CEST 1999 Werner Koch * armor.c: Removed duped include (John Bley) * mainproc.c: Ditto. * build-packet.c (hash_public_key): Fixed hashing of the header. * import.c (delete_inv_parts): Allow import of own non-exportable sigs. Sat Mar 20 13:59:47 CET 1999 Werner Koch * armor.c (fake_packet): Fix for not not-dash-escaped Sat Mar 20 11:44:21 CET 1999 Werner Koch * g10.c (main): Added command --recv-keys * hkp.c (hkp_import): New. Wed Mar 17 13:09:03 CET 1999 Werner Koch * trustdb.c (check_trust): add new arg add_fnc and changed all callers. (do_check): Ditto. (verify_key): Ditto. (propagate_validity): Use the new add_fnc arg. (print_user_id): Add the FILE arg. (propagate_ownertrust): New. * pkclist.c (add_ownertrust_cb): New and changed the add_ownertrust logic. * getkey.c (get_keyblock_bylid): New. * trustdb.c (print_uid_from_keyblock): New. (dump_tn_tree_with_colons): New. (list_trust_path): Add colon print mode. * trustdb.c (insert_trust_record): Always use the primary key. * encode.c (encode_simple): Added text_mode filter (Rémi Guyomarch) (encode_crypt): Ditto. * mainproc.c (proc_pubkey_enc): Added status ENC_TO. * armor.c (armor_filter): Added status NODATA. * passphrase.c (passphrase_to_dek): Always print NEED_PASSPHRASE * seckey_cert.c (check_secret_key): Added BAD_PASS status. * g10.c (main): Set g10_opt_homedir. Sun Mar 14 19:34:36 CET 1999 Werner Koch * keygen.c (do_create): Changed wording of the note (Hugh Daniel) Thu Mar 11 16:39:46 CET 1999 Werner Koch * tdbdump.c: New * trustdb.c (walk_sigrecs,do_list_sigs,list_sigs, list_records,list_trustdb,export_ownertrust,import_ownertrust): Moved to tdbdump.c (init_trustdb): renamed to setup_trustdb. Changed all callers. (do_init_trustdb): renamed to init_trustdb(). * trustdb.c (die_invalid_db): replaced by tdbio_invalid. * tdbio.c (tdbio_invalid): New. * import.c (delete_inv_parts): Skip non exportable signatures. * keyedit.c (sign_uid_mk_attrib): New. (sign_uids): Add the local argument. (keyedit_menu): New "lsign" command. * trustdb.c (register_trusted_key): Removed this and all related stuff. * g10.c (oTrustedKey): Removed option. * tdbio.h (dir.valcheck): New trustdb field. * tdbio.c: Add support for this field (tdbio_read_modify_stamp): New. (tdbio_write_modify_stamp): New. * trustdb.c (do_check): Check against this field. Removed cache update. (verify_key): Add cache update. (upd_uid_record): Some functional changes. (upd_cert_record): Ditto Wed Mar 10 11:26:18 CET 1999 Werner Koch * keylist.c (list_keyblock): Fixed segv in uid. Print 'u' as validity of sks. Mon Mar 8 20:47:17 CET 1999 Werner Koch * getkey.c (classify_user_id): Add new mode 12 (#). * seckey-cert.c (check_secret_key): replaced error by info. * trustdb.c (query_trust_info): Add another arg, changed all callers. (check_trust): Ditto. (do_check): Ditto. (verify_key): Handle namehash. * keylist.c (list_keyblock): print trust info for user ids. * sig-check.c (signature_check): Add sig-created to status output. Tue Mar 2 16:44:57 CET 1999 Werner Koch * textfilter.c (copy_clearsig_text): New. (clearsign): Removed. * sign.c (clearsign_file): does not use textfiler anymore. * keygen.c (ask_user_id): print a note about the used charset. Tue Mar 2 10:38:42 CET 1999 Werner Koch * sig-check.c (signature_check): sig-id now works for all algos. * armor.c (armor_filter): Fixed armor bypassing. Sun Feb 28 19:11:00 CET 1999 Werner Koch * keygen.c (ask_user_id): Don't change the case of email addresses. (has_invalid_email_chars): Adjusted. * keylist.c (list_one): Really list serect keys (Remi Guyomarch) * keyedit.c (menu_select_uid): Add some braces to make egcs happy. (menu_select_key): Ditto. * mainproc.c (do_proc_packets): List sym-enc packets (Remi Guyomarch) Fri Feb 26 17:55:41 CET 1999 Werner Koch * pkclist.c (build_pk_list): Return error if there are no recipients. * sig-check.c (signature_check): New signature id feature. * armor.c (make_radic64_string): New. * mainproc.c (proc_pubkey_enc): early check for seckey availability. * pkclist.c (do_we_trust_pre): print user id before asking. * ringedit.c (add_keyblock_resource,get_keyblock_handle): Cleaner handling of default resource. Thu Feb 25 18:47:39 CET 1999 Werner Koch * pkclist.c (algo_available): New. (select_algo_from_prefs): Check whether algo is available. * ringedit.c (keyring_copy): Take care of opt.dry_run. (do_gdbm_store): Ditto. * openfile.c (open_outfile). Ditto. (copy_options_file): Ditto. * trustdb.c (update_trustdb): Ditto. (clear_trust_checked_flag): Ditto. (update_trust_record): Ditto. (insert_trust_record): Ditto. Wed Feb 24 11:07:27 CET 1999 Werner Koch * keylist.c (secret_key_list): Now really list the secret key. * trustdb.c (do_init_trustdb): New. Init is now deferred. Mon Feb 22 20:04:00 CET 1999 Werner Koch * getkey.c (lookup_sk): Return G10ERR_NO_SECKEY and not x_PUBKEY. Fri Feb 19 15:49:15 CET 1999 Werner Koch * pkclist.c (select_algo_from_prefs): retrieve LID if not there. * armor.c (fake_packet): Replaced ugly lineending handling. * g10.c (oNoEncryptTo): New. * pkclist.c (build_pk_list): Implemented this option. * g10.c (main): Greeting is now printed to stderr and not to tty. Use add_to_strlist() instead of direct coding. * import.c (import): Use iobuf_push_filter2. * mainproc.c (check_sig_and_print): Print all user ids for good signatures. * getkey.c (get_pubkeyblock): New. * import.c (chk_self_sigs): Fixed SEGV for unbounded class 0x18 keys. (delete_inv_parts): Delete special marked packets. Tue Feb 16 14:10:02 CET 1999 Werner Koch * g10.c (main): New option --encrypt-to * pkclist.c (build_pk_list): Implemented encrypt-to. * parse-packet.c (parse_user_id): Removed the hack to work with utf-8 strings. * g10.c (main): Install lockfile cleanup handler. * tdbio.c (cleanup): Removed: this is now handled by dotlock. Sat Feb 13 14:13:04 CET 1999 Werner Koch * tdbio.c (tdbio_set_dbname): Init lockhandle for a new trustdb Wed Feb 10 17:15:39 CET 1999 Werner Koch * g10.c (main): check for development version now in configure * tdbio.c (tdbio_write_record): Add uid.validity (tdbio_read_record) : Ditto. (tdbio_dump_record) : Ditto. * keygen.c (keygen_add_std_prefs): Replaced Blowfish by Twofish, removed MD5 and Tiger. * pubkey-enc.c (get_it): Suppress warning about missing Blowfish in preferences in certain cases. * ringedit.c (lock_rentry,unlock_rentry): New. * getkey.c (key_byname): Pass ret_kb down to lookup_xx. * armor.c (armor_filter): No output of of empty comment lines. Add option --no-version to suppress the output of the version string. * getkey.c: Release the getkey context for auto context variables. Sun Jan 24 18:16:26 CET 1999 Werner Koch * getkey.c: Changed the internal design to allow simultaneous lookup of multible user ids (get_pubkey_bynames): New. (get_seckey_bynames): New. (get_seckey_next): New. (get_seckey_end): New. * keylist.c (list_one): Use the new functions. * keylist.c (list_keyblock): add a newline for normal listings. * g10.c (--recipient): New option name to replace --remote-user Wed Jan 20 18:59:49 CET 1999 Werner Koch * textfilter.c: Mostly rewritten * plaintext.c (handle_plaintext): Use now text_filter semantics. Tue Jan 19 19:34:58 CET 1999 Werner Koch * export.c (export_pubkeys_stream): New. (do_export_stream): New. * g10.c (aSendKeys): New command. * hkp.c (hkp_export): New. * compress.c (do_uncompress): Hack for algo 1 and 1.1.3 Sun Jan 17 11:04:33 CET 1999 Werner Koch * textfilter.c (text_filter): Now uses iobuf_read_line(). (read_line): Removed. * armor.c (trim_trailing_spaces): Removed and replaced by trim_trailing_ws from libutil Sat Jan 16 12:03:27 CET 1999 Werner Koch * hkp.c (hkp_ask_import): Use only the short keyid Sat Jan 16 09:27:30 CET 1999 Werner Koch * import.c (import_key_stream): New (import): New, moved most of import_keys here. * g10.c: New option --keyserver * mainproc.c (check_sig_and_print): Hook to import a pubkey. * pref.c pref.h : Removed * hkp.c hkp.h: New Wed Jan 13 14:10:15 CET 1999 Werner Koch * armor.c (radix64_read): Print an error if a bad armor was detected. Wed Jan 13 12:49:36 CET 1999 Werner Koch * armor.c (radix64_read): Now handles malformed armors produced by some buggy MUAs. Tue Jan 12 11:17:18 CET 1999 Werner Koch * ringedit.c (find_keyblock_bysk): New. * skc_list.c (is_insecure): New. (build_sk_list): usage check for insecure keys. * import.c (chk_self_sigs): Add handling for subkeys. (delete_inv_parts): Skip unsigned subkeys * sig-check.c (do_check): Print info if the signature is older than the key. * keygen.c (generate_subkeypair): Fail on time warp. * sign.c (do_sign): Ditto. Sun Jan 10 15:10:02 CET 1999 Werner Koch * armor.c (fake_packet): Fixed not-dash-escaped bug. Sat Jan 9 16:02:23 CET 1999 Werner Koch * sig-check.c (do_check): Output time diff on error * status.c (STATUS_VALIDSIG): New. (is_status_enabled): New. * mainproc.c (check_sig_and_print): Issue that status message. * plaintext.c (special_md_putc): Removed * armor.c (armor_filter): print error for truncated lines. * free-packet.c (free_encrypted): Revomed call to set_block_mode. (free_plaintext): Ditto. Thu Jan 7 18:00:58 CET 1999 Werner Koch * pkclist.c (add_ownertrust): Fixed return value. * encr-data.c (decrypt_data): Disabled iobuf_set_limit and iobuf_pop_filter stuff. * compress.c (handle_compressed): Disabled iobuf_pop_filter. * packet.h (PKT_secret_key): Add is_primary flag. * parse-packet.c (parse_key): Set this flag. * passphrase.c (passphrase_to_dek): Kludge to print the primary keyid - changed the API: keyid must now hold 2 keyids. * getkey.c (get_primary_seckey): New. * seckey-cert.c (do_check): pass primary keyid to passphrase query * tbdio.c (open_db): removed the atexit (tdbio_set_dbname): and moved it to here. * armor.c: Rewrote large parts. Tue Dec 29 19:55:38 CET 1998 Werner Koch * revoke.c (gen_revoke): Removed compression. * pkclist.c (do_we_trust_pre): special check for revoked keys * trustdb.c (update_trust_record): Fixed revoke flag. Tue Dec 29 14:41:47 CET 1998 Werner Koch * misc.c (disable_core_dumps): Check for EINVAL (Atari) * getkey (merge_one_pk_and_selfsig): Fixed search of expiredate. (merge_keys_and_selfsig): Ditto. * free-packet.c (cmp_public_keys): cmp expire only for v3 packets (cmp_secret_keys): Ditto. (cmp_public_secret_key): Ditto. Wed Dec 23 17:12:24 CET 1998 Werner Koch * armor.c (find_header): Reset not_dashed at every header Wed Dec 23 13:18:14 CET 1998 Werner Koch * pkclist.c (add_ownertrust): Refresh validity values. * trustdb.c (enum_cert_paths_print): New arg refresh. * ringedit.c: Fixed problems fix keyrings * parse-packet.c (dbg_parse_packet): New debug functions. * getkey.c (getkey_disable_caches): New. * import.c (import_keys): Disable caches. Thu Dec 17 18:31:15 CET 1998 Werner Koch * misc.c (trap_unaligned): Only for glibc 1 * sign.c (write_dash_escaped): Now escapes "From " lines * g10.c: New option --escape-from-lines * trustdb.c (sort_tsl_list): New (list_trust_path): Now prints sorted list. (enum_cert_paths): Likewise. (enum_cert_paths_print): New. (print_paths): New printing format. * pkclist.c (add_ownertrust): New arg quit. (edit_ownertrust): New quit selection and does not query the recipients ownertrust anymore. (add_ownertrust): Print the ceritficate path. Mon Dec 14 21:18:49 CET 1998 Werner Koch * parse-packet.c (parse_signature): Now checks for critical bit (parse_sig_subpkt): Splitted. (parse_one_sig_subpkt): New. * sig-check.c (do_check): handle critical bit. Sun Dec 13 14:10:56 CET 1998 Werner Koch * pcklist.c (select_algo_from_prefs): Preferences should now work (lost the != ? ) Thu Dec 10 20:15:36 CET 1998 Werner Koch * ringedit.c (gdbm_store): Fix for inserts * g10.c (main): New option --export-all * export.c (export_pubkeys): New arg. (do_export): Now may skip old keys. * status.c: Minor patches for Sun's cc * keygen.c (ask_algo): Disabled v3 ElGamal choice, rearranged the numbers. Add a warning question when a sign+encrypt key is selected. * g10.c (do_not_use_RSA): Removed. * misc.c (print_pubkey_algo_note): New as replacement for the do_not_use_RSA() and chnaged all callers. (print_cipher_algo_note): New. (print_hash_algo_note): New. * cipher.c (write_header): Add a call to print_cipher_algo_note. * seckey-cert.c (protect_secret_key): Ditto * sign.c (do_sign): Add a call to print_digest_algo_note. * getkey.c (get_long_user_id_string): New. * mainproc.c (check_sig_and_print): Changed the format of the status output. * encrypt.c (write_pubkey_enc_from_list): print used symmetric cipher. * pkclist.c (do_we_trust): Changed a message. Wed Dec 9 13:41:06 CET 1998 Werner Koch * misc.c (trap_unaligned) [ALPHA]: Only if UAC_SIGBUS is defined. * sign.c (write_dash_escaped): Add the forgotten patch by Brian Moore. * compress.c (do_uncompress): Fixed the inflating bug. Tue Dec 8 13:15:16 CET 1998 Werner Koch * trustdb.c (upd_uid_record): Now uses the newest self-signature (insert_trust_record): Now calls update with recheck set to true. (register_trusted_key): New. (verify_own_keys): Enhanced by list of trusted keys. * g10.c (main): Print a warning when a devel version is used. (main): New option --trusted-key * import.c (merge_blocks): Fixed merging of new user ids and added merging of subkeys. (append_uid): Ditto. (merge_keysig): New. (append_key): New. * getkey.c (merge_one_pk_and_selfsig): Get the expiration time from the newest self-signature. (merge_keys_and_selfsig): Ditto. * free-packet.c (cmp_secret_key): New. Fri Nov 27 21:37:41 CET 1998 Werner Koch * g10.c: New option --lock-once * tdbio.c (open_db): Add an atexit (cleanup): New. (tdbio_sync): Add locking. (tdbio_end_transaction): Ditto. (put_record_into_cache): Ditto. * ringedit.c (keyring_copy): Ditto. (cleanup): New. (add_keyblock_resource): Add an atexit. Fri Nov 27 15:30:24 CET 1998 Werner Koch * armor.c (find_header): Another fix for clearsigs. Fri Nov 27 12:39:29 CET 1998 Werner Koch * status.c (display_help): Removed. * helptext.c: New and removed the N_() from all cpr_gets. Fri Nov 20 16:54:52 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): New option --not-dash-escaped * sign.c (write_dashed_escaped): Ditto. * armor.c (find_header): Support for NotDashEscaped header. * getkey.c: print "disabled cache.." only if verbose is used. Thu Nov 19 07:17:31 1998 Werner Koch * parse-packet.c (dump_sig_subpkt): Fixed expire listing * getkey.c (merge_keys_and_selfsig): Fixed expire calculation. (merge_one_pk_and_selfsig): Ditto. * keyedit.c (menu_expire). Ditto. * keygen.c (keygen_add_key_expire): Ditto. (ask_expire_interval): New and changed all local function to use this instead. (keygen_add_key_expire): Opaque should now be a public key; changed all callers. * parse.packet.c (parse): use skip_rest to skip packets. * keyedit.c (keyedit_menu): New arg for cmdline cmds. Wed Nov 18 20:33:50 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (check_trustdb): Now rechecks all gived userids. (collect_paths): Some fixes. (upd_pref_records): Skips empty items, evaluate all items. * parse-packet.c (dump_sig_subpkt): Better listing of prefs. (skip_packet): Now knows about marker packet * g10.c: removed cmd "--edit-sig". * pubring.asc: Updated. Sat Nov 14 14:01:29 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Changed syntax of --list-trust-path * trustdb.c (list_trust_path): Replaced max_depth by opt.max_cert_depth Fri Nov 13 07:39:58 1998 Werner Koch * trustdb.c (collect_paths): Removed a warning message. (enum_trust_web): Removed. (enum_cert_paths): New. * pkclist.c (add_ownertrust): Changed to use enum_cert_paths. (edit_ownertrust): Now list ceritficates on request. (show_paths): New. Wed Nov 11 18:05:44 1998 Werner Koch * g10.c (main): New option --max-cert-depth * tdbio.h: add new fields to ver and dir record. * tdbio.c: read/write/dump of these fields. (tdbio_db_matches_options): New. * trustdb.c: replaced MAC_CERT_DEPTH by opt.max_cert_depth. (do_check): cache validity and changed other functions to reset the cached value. * keylist.c (list_one): Now lists the ownertrust. * mainproc.c (list_node): Ditto. Tue Nov 10 10:08:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (g10_exit): Now looks at the new g10_errors_seen. * mainproc.c (check_sig_and_print): Sets g10_errors_seen. * *.c : i18n many more strings. * ringedit.c (locate_keyblock_by_keyid): Add HAVE_LIBGDBM (locate_keyblock_by_fpr): Ditto. * g10.c (main): removed unsused "int errors". (main): Add new option --charset. * g10.c (main): special message for the unix newbie. Mon Nov 9 07:17:42 1998 Werner Koch * getkey.c (finish_lookup): Kludge to prefere algo 16. * trustdb.c (new_lid_table): Clear cached item. * status.c (cpr_get_utf8): New. * pkclist.c (build_pk_list): Uses this. Sun Nov 8 17:20:39 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (check_sig_and_print): Why did I use strlen()-1 in the printf? - This truncated the TZ. Sat Nov 7 15:57:28 1998 me,,, (wk@tobold) * getkey.c (lookup): Changes to support a read_next. (get_pubkey): Fixed a memory leak. * keylist.c (list_one): Now lists all matching user IDs. Tue Nov 3 16:19:21 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (ask_user_id): Now converted to UTF-8 * g10.c (main): Kludge for pgp clearsigs and textmode. Fri Oct 30 16:40:39 1998 me,,, (wk@tobold) * signal.c (block_all_signals): New. (unblock_all_signals): New * tdbio.c (tdbio_end_transaction): Now blocks all signals. * trustdb.c (new_lid_table): Changed the representation of the former local_lid_info stuff. * trustdb.c (update_trust_record): Reorganized the whole thing. * sig-check.c (check_key_signature): Now handles class 0x28 Wed Oct 28 18:56:33 1998 me,,, (wk@tobold) * export.c (do_export): Takes care of the exportable sig flag. Tue Oct 27 14:53:04 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (update_trust_record): New "fast" parameter. Sun Oct 25 19:32:05 1998 Werner Koch (wk@isil.d.shuttle.de) * openfile.c (copy_options_File): New. * ringedit.c (add_keyblock_resource): Creates options file * tdbio.c (tdbio_set_dbname): Ditto. Sat Oct 24 14:10:53 1998 brian moore * mainproc.c (proc_pubkey_enc): Don't release the DEK (do_proc_packets): Ditto. Fri Oct 23 06:49:38 1998 me,,, (wk@tobold) * keyedit.c (keyedit_menu): Comments are now allowed * trustdb.c: Rewrote large parts. Thu Oct 22 15:56:45 1998 Michael Roth (mroth@nessie.de) * encode.c: (encode_simple): Only the plain filename without a given directory is stored in generated packets. (encode_crypt): Ditto. * sign.c: (sign_file) Ditto. Thu Oct 22 10:53:41 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (update_trust_record): Add new optional arg. * import.c (import_keys): Add statistics output * trustdb.c (update_trustdb): Ditto. (insert_trustdb): Ditto. * tdbio.c (tdbio_begin_transaction): New. (tdbio_end_transaction): New. (tdbio_cancel_transaction): New. * g10.c (main): New option --quit. * trustdb.c (check_hint_sig): No tests for user-id w/o sig. This caused an assert while checking the sigs. * trustdb.c (upd_sig_record): Splitted into several functions. * import.c (import_keys): New arg "fast". * g10.c (main): New command --fast-import. Wed Oct 21 18:19:36 1998 Michael Roth * ringedit.c (add_keyblock_resource): Directory is now created. * tdbio.c (tdbio_set_dbname): New info message. Wed Oct 21 11:52:04 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (update_trustdb): released keyblock in loop. * keylist.c (list_block): New. (list_all): Changed to use list_block. * trustdb.c: Completed support for GDBM * sign.c (only_old_style): Changed the way force_v3 is handled (sign_file): Ditto. (clearsign_file): Ditto. * keygen.c (has_invalid_email_chars): Splitted into mailbox and host part. * keylist.c (list_one): Add a merge_keys_and_selfsig. * mainproc.c (proc_tree): Ditto. Sun Oct 18 11:49:03 1998 Werner Koch (wk@isil.d.shuttle.de) * sign.c (only_old_style): Add option force_v3_sigs (sign_file): Fixed a bug in sig->version (clearsign_file): Ditto. * parse-packet.c (dump_sig_subpkt): New * keyedit.c (menu_expire): New. * free-packet.c (cmp_signatures): New Sat Oct 17 10:22:39 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c: changed output line length from 72 to 64. * keyedit.c (fix_keyblock): New. Fri Oct 16 10:24:47 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c: Rewrote most. * tdbio.c: Add cache and generalized hash tables. * options.h (ENABLE_COMMENT_PACKETS): New but undef'ed. * encode.c, sign.c, keygen.c: Disabled comment packets. * export.c (do_export): Comment packets are never exported, except for those in the secret keyring. * g10.c (main): Removed option do-no-export-rsa; should be be replaced by a secpial tool. * export.c (do_export): Removed the code for the above option. * armor.c (find_header): Support for new only_keyblocks. * import.c (import_keys): Only looks for keyblock armors. * packet.h: replaced valid_days by expiredate and changed all users. * build-packet.c (do_public_key): calculates valid-days (do_secret_key): Ditto. * parse-packet.c (parse_key): expiredate is calucated from the valid_period in v3 packets. * keyid.c (do_fingerprint_md): calculates valid_dates. * keygen.c (add_key_expire): fixed key expiration time for v4 packets. * armor.c (find_header): A LF in the first 28 bytes was skipped for non-armored data. Thu Oct 8 11:35:51 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (is_armored): Add test on old comment packets. * tdbio.c (tdbio_search_dir_bypk): fixed memory leak. * getkey.c: Changed the caching algorithms. Wed Oct 7 19:33:28 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnodes.c (unused_nodes): New. Wed Oct 7 11:15:36 1998 Werner Koch (wk@isil.d.shuttle.de) * keyedit.c (sign_uids): Fixed a problem with SK which could caused a save of an unprotected key. (menu_adduid): Ditto. * keyedit.c (keyedit_menu): Prefs are now correctly listed for new user ids. * trustdb.c (update_trust_record): New. (insert_trust_record): Now makes use of update_trust_record. Tue Oct 6 16:18:03 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (read_record): replaces most of the tdbio_read_records. (write_record): Ditto. Sat Oct 3 11:01:21 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (ask_alogo): enable ElGamal enc-only only for addmode. Wed Sep 30 10:15:33 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (import_one): Fixed update of wrong keyblock. Tue Sep 29 08:32:08 1998 me,,, (wk@tobold) * mainproc.c (proc_plaintext): Display note for special filename. * plaintext.c (handle_plaintext): Suppress output of special file. Mon Sep 28 12:57:12 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (verify_own_keys): Add warning if a key is not protected. * passphrase (hash_passphrase): Fixed iterated+salted mode and setup for keysizes > hashsize. * g10.c (main): New options: --s2k-{cipher,digest,mode}. Fri Sep 25 09:34:23 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c: Chnaged some help texts. Tue Sep 22 19:34:39 1998 Werner Koch (wk@isil.d.shuttle.de) * passphrase.c (read_passphrase_from_fd): fixed bug for long passphrases. Mon Sep 21 11:28:05 1998 Werner Koch (wk@(none)) * getkey.c (lookup): Add code to use the sub key if the primary one does not match the usage. * armor.c (armor_filter): New error message: no valid data found. (radix64_read): Changes to support multiple messages. (i18n.h): New. * mainproc.c (add_onepass_sig): bug fix. Mon Sep 21 08:03:16 1998 Werner Koch (wk@isil.d.shuttle.de) * pkclist.c (do_we_trust): Add keyid to most messages. * passphrase.c (read_passphrase_from_fd): New. (have_static_passphrase): New (get_passphrase_fd): Removed. (set_passphrase_fd): Removed. * g10.c (main): passphrase is now read here. * keyedit.c (keyedit_menu): "help" texts should now translate fine. Mon Sep 21 06:40:02 1998 Werner Koch (wk@isil.d.shuttle.de) * encode.c (encode_simple): Now disables compression when --rfc1991 is used. (encode_crypt): Ditto. Fri Sep 18 16:50:32 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (merge_key_and_selfsig): New. Fri Sep 18 10:20:11 1998 Werner Koch (wk@isil.d.shuttle.de) * pkclist.c (select_algo_from_prefs): Removed 3DES kludge. * seskey.c (make_session_key): Fixed SERIOUS bug introduced by adding the weak key detection code. * sign.c (sign_file): Changed aremor header in certain cases. Tue Sep 15 17:52:55 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (check_sig_and_print): Replaced ascime by asctimestamp. Mon Sep 14 11:40:52 1998 Werner Koch (wk@isil.d.shuttle.de) * seskey.c (make_session_key): Now detects weak keys. * trustdb (clear_trust_checked_flag): New. * plaintext.c (handle_plaintext): Does no anymore suppress CR from cleartext signed messages. Sun Sep 13 12:54:29 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (insert_trust_record): Fixed a stupid bug in the free liunked list loops. Sat Sep 12 15:49:16 1998 Werner Koch (wk@isil.d.shuttle.de) * status.c (remove_shmid): New. (init_shm_comprocess): Now sets permission to the real uid. Wed Sep 9 11:15:03 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (PKT_pubkey_enc): New flah throw_keyid, and add logic to implement it. * g10.c (main): New Option --throw-keyid * getkey.c (enum_secret_keys): Add new ar and changed all callers. Tue Sep 8 20:04:09 1998 Werner Koch (wk@isil.d.shuttle.de) * delkey.c (delete_key): Moved from keyedit.c. Mon Sep 7 16:37:52 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (calc_length_header): New arg new_ctb to correctly calculate the length of new style packets. * armor.c (is_armored): Checks for symkey_enc packets. * pkclist.c (select_algo_from_prefs): 3DEs substitute is now CAST5. Tue Aug 11 17:54:50 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (do_secret_key): Fixed handling of old keys. * getkey.c (compare_name): Fixed exact and email matching * openfile.c (open_outfile): Changed arguments and all callers. Tue Aug 11 09:14:35 1998 Werner Koch (wk@isil.d.shuttle.de) * encode.c (encode_simple): Applied option set-filename and comment. (encode_crypt): Ditto. * sign.c (sign_file): Ditto. * armor.c (armor_filter): Applied option comment. * encode.c (encode_crypt): Moved init_packet to the begin. (encode_simple): add an init_packet(). * comment (write_comment): Now enforces a hash sign as the 1st byte. * import.c (import_one): Add explanation for "no user ids". * compress.c (do_uncompress): Applied Brian Warner's patch to support zlib 1.1.3 etc. * trustdb.c (check_trust): Fixed a problem after inserting new keys. * getkey (lookup): do not return the primary key if usage is given (lookup_sk): Ditto and take usage into account. * status.c (cpr_get_answer_is_yes): add display_help. Mon Aug 10 10:11:28 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (lookup_sk): Now always returns the primary if arg primary is true. (lookup): Likewise. (get_pubkey_byname): Now returns the primary key (get_seckey_byname): Ditto. Mon Aug 10 08:34:03 1998 Werner Koch (wk@isil.d.shuttle.de) * keyid.c (pubkey_letter): ELG_E is now a small g. Sat Aug 8 17:26:12 1998 Werner Koch (wk@isil.d.shuttle.de) * openfile (overwrite_filep): Changed semantics and all callers. Sat Aug 8 12:17:07 1998 Werner Koch (wk@isil.d.shuttle.de) * status.c (display_help): New. Thu Aug 6 16:30:41 1998 Werner Koch,mobil,,, (wk@tobold) * seskey.c (encode_session_key): Now uses get_random_bits(). Thu Aug 6 07:34:56 1998 Werner Koch,mobil,,, (wk@tobold) * ringedit.c (keyring_copy): No more backupfiles for secret keyrings and add additional warning in case of a failed secret keyring operation. Wed Aug 5 11:54:37 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (check_opts): Moved to main. Changed def_cipher_algo semantics and chnaged all users. * pubkey-enc.c (get_sssion_key): New informational output about preferences. * parse-packet.c (parse_symkeyenc): Fixed salted+iterated S2K (parse_key): Ditto. * build-packet.c (do_secret_key): Ditto. (do_symkey_enc): Ditto. Tue Aug 4 08:59:10 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (enum_secret_keys): Now returns only primary keys. * getkey (lookup): Now sets the new namehash field. * parse-packet.c (parse_sig_subpkt2): New. * sign.c (sign_file): one-pass sigs are now emiited reverse. Preference data is considered when selecting the compress algo. Wed Jul 29 12:53:03 1998 Werner Koch (wk@isil.d.shuttle.de) * 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@isil.d.shuttle.de) * 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@isil.d.shuttle.de) * 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@isil.d.shuttle.de) * build-packet.c (write_sign_packet_header): New. Tue Jul 21 14:37:09 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (import_one): Now creates a trustdb record. * g10.c (main): New command --check-trustdb Mon Jul 20 11:15:07 1998 Werner Koch (wk@isil.d.shuttle.de) * genkey.c (generate_keypair): Default key is now DSA with encryption only ElGamal subkey. Thu Jul 16 10:58:33 1998 Werner Koch (wk@isil.d.shuttle.de) * keyid.c (keyid_from_fingerprint): New. * getkey.c (get_pubkey_byfprint): New. Tue Jul 14 18:09:51 1998 Werner Koch (wk@isil.d.shuttle.de) * keyid.c (fingerprint_from_pk): Add argument and changed all callers. (fingerprint_from_sk): Ditto. Tue Jul 14 10:10:03 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (handle_plaintext): Now returns create error if the file could not be created or the user responded not to overwrite the file. * mainproc.c (proc_plaintext): Tries again if the file could not be created to check the signature without output. * misc.c (disable_core_dumps): New. * g10.c (main): disable coredumps for gpg * g10.c (MAINTAINER_OPTIONS): New to disable some options Mon Jul 13 16:47:54 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (hash_datafiles): New arg for better support of detached sigs. Changed all callers. * mainproc.c (proc_signature_packets): Ditto. * g10.c (main): New option "compress-sigs" * sig.c (sign_file): detached signatures are not anymore compressed unless the option --compress-sigs is used. Thu Jul 9 19:54:54 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c: Fixes to allow zero length cleartext signatures Thu Jul 9 14:52:47 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (build_list): Now drops setuid. (main): Changed the way keyrings and algorithms are registered . Wed Jul 8 14:17:30 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (PKT_public_key): Add field keyid. * parse-packet.c (parse_key): Reset the above field. * keyid.c (keyid_from_pk): Use above field as cache. * tdbio.c, tdbio.h: New * trustdb.c: Moved some functions to tdbio.c. (print_keyid): New. * pkclist.c (check_signatures_trust): New. Wed Jul 8 10:45:28 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (special_md_putc): New. (handle_plaintext): add clearsig argument * mainproc.c (proc_plaintext): detection of clearsig * sign.c (write_dased_escaped): Changed clearsig format Tue Jul 7 18:56:19 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (find_header): Now makes sure that there is only one empty line for clearsigs, as this is what OP now says. Mon Jul 6 13:09:07 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): New option default-secret-key * getkey.c (get_seckey_byname): support for this option. Mon Jul 6 09:03:49 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (add_keyring): Keyrings are now added to end of the list of keyrings. The first added keyringwill be created. (add_secret_keyring): Likewise. * ringedit.c (add_keyblock_resource): Files are created here. * g10.c (aNOP): Removed * getkey.c (lookup): Add checking of usage for name lookups * packet.h (pubkey_usage): Add a field which may be used to store usage capabilities. * pkclist.c (build_pk_list): getkey now called with usage arg. * skclist.c (build_sk_list): Ditto. * sign.c (clearsign_file): Fixed "Hash:" headers Sat Jul 4 13:33:31 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (list_ownertrust): New. * g10.c (aListOwnerTrust): New. * g10.c (def_pubkey_algo): Removed. * trustdb.c (verify_private_data): Removed and also the call to it. (sign_private_data): Removed. Fri Jul 3 13:26:10 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aEditKey): was aEditSig. Changed usage msg. * keyedit.c: Done some i18n stuff. * g10.c (do_not_use_RSA): New. * sign.c (do_sign): Add call to above function. * encode.c (write_pubkey_enc_from_list): Ditto. Thu Jul 2 21:01:25 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c: Now is able sto store data of unknown algorithms. * free-packet.c: Support for this. * build-packet.c: Can write data of packet with unknown algos. Thu Jul 2 11:46:36 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse): fixed 4 byte length header Wed Jul 1 12:36:55 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (new_ctb): New field for some packets * build-packet.c (build_packet): Support for new_ctb * parse-packet.c (parse): Ditto. Mon Jun 29 12:54:45 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h: changed all "_cert" to "_key", "subcert" to "subkey". * free-packet.c (free_packet): Removed memory leak for subkeys. Sun Jun 28 18:32:27 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (import_keys): Renamed from import_pubkeys. (import_secret_one): New. * g10.c (aExportSecret): New. * export.c (export_seckeys): New. * parse-packet.c (parse_certificate): Cleaned up. (parse_packet): Trust packets are now considered as unknown. (parse_pubkey_warning): New. Fri Jun 26 10:37:35 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (has_invalid_email_chars): New. Wed Jun 24 16:40:22 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (armor_filter): Now creates valid onepass_sig packets with all detected hash algorithms. * mainproc.c (proc_plaintext): Now uses the hash algos as specified in the onepass_sig packets (if there are any) Mon Jun 22 11:54:08 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (handle_plaintext): add arg to disable outout * mainproc.c (proc_plaintext): disable output when in sigs_only mode. Thu Jun 18 13:17:27 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c: Removed all rsa packet stuff, chnaged defaults for key generation. Sun Jun 14 21:28:31 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (checksum_u16): Fixed a stupid bug which caused a wrong checksum calculation for the secret key protection and add a backward compatibility option. * g10.c (main): Add option --emulate-checksum-bug. Thu Jun 11 13:26:44 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h: Major changes to the structure of public key material which is now stored in an array and not anaymore in a union of algorithm specific structures. These is needed to make the system more extendable and makes a lot of stuff much simpler. Changed all over the system. * dsa.c, rsa.c, elg.c: Removed. Wed Jun 10 07:22:02 1998 Werner Koch,mobil,,, (wk@tobold) * g10.c ("load-extension"): New option. Mon Jun 8 22:23:37 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (do_check): Removed cipher constants (protect_secret_key): Ditto. Fri May 29 10:00:28 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (query_trust_info): New. * keylist.c (list_one): Add output of trust info * mainproc (list_node): ditto. * g10.c (main): full trustdb init if -with-colons and any of the key list modes. Thu May 28 10:34:42 1998 Werner Koch (wk@isil.d.shuttle.de) * status.c (STATUS_RSA_OR_IDEA): New. * sig-check.c (check_signature): Output special status message. * pubkey-enc.c (get_session_key): Ditto. * mainproc.c (check_sig_and_print): Changed format of output. * passpharse.c (passphrase_to_dek): Likewise. Wed May 27 13:46:48 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aListSecretKeys): New option --list-secret-keys * keylist.c (std_key_list): Renamed to public_key_list. (secret_key_list): New (list_one, list_all): Add support for secret keys. * getkey.c (get_secret_keyring): New. * mainproc.c (list_node): Add option --with-colons for secret keys * sig-check.c (check_key_signature): detection of selfsigs * mainproc.c (list_node): fixed listing. * g10.c (aListSecretKeys): New option --always-trust * pkclist.c (do_we_trust): Override per option added * status.c (write_status_text): Add a prefix to every output line. Wed May 27 07:49:21 1998 Werner Koch (wk@isil.d.shuttle.de) * g10 (--compress-keys): New. * options.h (compress_keys): New. * export.c (export_pubkeys): Only compresses with the new option. Tue May 26 11:24:33 1998 Werner Koch (wk@isil.d.shuttle.de) * passphrase.c (get_last_passphrase): New (set_next_passphrase): New. (passphrase_to_dek): add support for the above functions. * keyedit.c (make_keysig_packet): Add sigclass 0x18, changed all callers due to a new argument. * keygen.c (write_keybinding): New (generate_subkeypair): Add functionality (ask_algo, ask_keysize, ask_valid_days): Broke out of generate_keypair (ask_user_id, ask_passphrase): Ditto. Thu May 21 11:26:13 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c,gpgd.c (main): Does now return an int, so that egcs does not complain. * armor.c (fake_packet): Removed erro message and add a noticed that this part should be fixed. * sign.c (sign_file): Compression now comes in front of encryption. * encode.c (encode_simple): Ditto. (encode_crypt): Ditto. Tue May 19 16:18:19 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (fake_packet): Changed assertion to log_error Sat May 16 16:02:06 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (build_packet): Add SUBKEY packets. Fri May 15 17:57:23 1998 Werner Koch (wk@isil.d.shuttle.de) * sign.c (hash_for): New and used in all places here. * main.h (DEFAULT_): new macros. * g10.c (opt.def_digest_algo): Now set to 0 * compress.c (init_compress): Add support for algo 1 * options.h (def_compress_algo): New * g10.c (main): New option --compress-algo Fri May 15 13:23:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (print_mds): New feature to print only one hash, chnaged formatting. Thu May 14 15:36:24 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c (trap_unaligned) [__alpha__]: New * g10.c (trap_unaligned): Add call to this to track down SIGBUS on Alphas (to avoid the slow emulation code). Wed May 13 11:48:27 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (do_signature): Support for v4 pakets. * keyedit.c (make_keysig_packet): Ditto. * build-packet.c (build_sig_subpkt_from_sig): New. (build_sig_subpkt): New. * elg.c (g10_elg_sign): removed keyid_from_skc. * dsa.c (g10_dsa_sign): Ditto. * rsa.c (g10_rsa_sign): Ditto. * keyedit.c (make_keysig_packet): Add call to keyid_from_skc * sign.c (clearsign_file): Support for v4 signatures. (sign_file): Ditto. Wed May 6 09:31:24 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (do_parse): add support for 5 byte length leader. (parse_subpkt): Ditto. * build-packet.c (write_new_header): Ditto. * packet.h (SIGSUBPKT_): New constants. * parse-packet.c (parse_sig_subpkt): Changed name, made global, and arg to return packet length, chnaged all callers Tue May 5 22:11:59 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (gen_dsa): New. * build_packet.c (do_secret_cert): Support for DSA Mon May 4 19:01:25 1998 Werner Koch (wk@isil.d.shuttle.de) * compress.c: doubled buffer sizes * parse-packet.c (do_plaintext): now uses iobuf_read/write. Mon May 4 09:35:53 1998 Werner Koch (wk@isil.d.shuttle.de) * seskey.c (encode_md_value): Add optional argument hash_algo, changed all callers. * passphrase.c (make_dek_from_passphrase): Removed * (get_passhrase_hash): Changed name to passphrase_to_dek, add arg, changed all callers. * all: Introduced the new ELG identifier and added support for the encryption only one (which is okay to use by GNUPG for signatures). Sun May 3 17:50:26 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h (PKT_OLD_COMMENT): New name for type 16. * parse-packet.c (parse_comment): Now uses type 61 Fri May 1 12:44:39 1998 Werner Koch,mobil,,, (wk@tobold) * packet.h (count): Chnaged s2k count from byte to u32. * seckey-cert.c (do_check): Changed s2k algo 3 to 4, changed reading of count. * build-packet.c (do_secret_cert): ditto. * parse-packet.c (parse_certificate): ditto. * parse-packet.c (parse_symkeyenc): New. * build-packet.c (do_symkey_enc): New. Thu Apr 30 16:33:34 1998 Werner Koch (wk@isil.d.shuttle.de) * sign.c (clearsign_file): Fixed "Hash: " armor line. Tue Apr 28 14:27:42 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_subpkt): Some new types. Mon Apr 27 12:53:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Add option --skip-verify. * mainproc.c (check_sig_and_print): Ditto. * g10.c (print_mds): Add output for Tiger. * sign.c (sign_file): Now uses partial length headers if used in canonical textmode (kludge to fix a bug). * parse-packet.c (parse_certificate): Changed BLOWFISH id. * pubkey-enc.c (get_session_key): Ditto. * seskey.c (make_session_key): Ditto. * seckey-cert.c (protect_secret_key,do_check): Add BLOWFISH160. Fri Apr 24 17:38:48 1998 Werner Koch,mobil,,, (wk@tobold) * sig-check.c (check_key_signature): Add sig-class 0x14..0x17 * keyedit.c (sign-key): Some changes to start with support of the above new sig-classes. Wed Apr 22 09:01:57 1998 Werner Koch,mobil,,, (wk@tobold) * getkey.c (compare_name): add email matching Tue Apr 21 16:17:12 1998 Werner Koch,mobil,,, (wk@tobold) * armor.c (armor_filter): fixed missing last LF before CSUM. Thu Apr 9 11:35:22 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (do_check): New; combines all the check functions into one. * sign.c: removed all key management functions * keyedit.c: New. Thu Apr 9 09:49:36 1998 Werner Koch (wk@isil.d.shuttle.de) * import.c (chk_self_sigs): Changed an error message. Wed Apr 8 16:19:39 1998 Werner Koch (wk@isil.d.shuttle.de) * packet.h: packet structs now uses structs from the pubkey, removed all copy operations from packet to pubkey structs. Wed Apr 8 13:40:33 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (verify_own_certs): Fixed "public key not found". * getkey.c (key_byname): New, combines public and secret key search. * pkclist.c (build_pkc_list): Add new arg usage, changed all callers. * skclist.c (build_skc_list): Likewise. * ringedit.c (find_keyblock, keyring_search2): Removed. Wed Apr 8 09:47:21 1998 Werner Koch (wk@isil.d.shuttle.de) * sig-check.c (do_check): Applied small fix from Ulf Möller. Tue Apr 7 19:28:07 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c, encr-data.c, seckey-cert.c: Now uses cipher_xxxx functions instead of blowfish_xxx or cast_xxx Tue Apr 7 11:04:02 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (g10maint.o): Changed the way it is created. Mon Apr 6 11:17:08 1998 Werner Koch (wk@isil.d.shuttle.de) * misc.c: New. * keygen.c (checksum,checksum_u16,checksum_mpi): Moved to misc.c * seckey-cert.c: Kludge for wrong ELG checksum implementation. Sat Apr 4 20:07:01 1998 Werner Koch (wk@isil.d.shuttle.de) * cipher.c (cipher_filter): Support for CAST5 * encr-data.c (decode_filter): Ditto. (decrypt_data): Ditto. * seskey.c (make_session_key): Ditto. * seckey-cert.c (check_elg, check_dsa): Ditto, (protect_secret_key): Ditto. * pubkey-enc.c (get_session_key): Ditto. * passphrase.c (hash_passphrase): Ditto. Thu Apr 2 20:22:35 1998 Werner Koch (wk@isil.d.shuttle.de) * gpgd.c: New Thu Apr 2 10:38:16 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (generate_keypair): Add valid_days stuff. * trustdb.c (check_trust): Add check for valid_days. Wed Apr 1 16:15:58 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (generate_keypair): Addional question whether the selected large keysize is really needed. Wed Apr 1 15:56:33 1998 Werner Koch (wk@isil.d.shuttle.de) * seckey-cert.c (protect_secret_key): merged protect_xxx to here. Wed Apr 1 10:34:46 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (g10maint.c): Changed creation rule, so that it works on FreeBSD (missing CFLAGS). * parse-packet.c (parse_subkey): Removed. Thu Mar 19 15:22:36 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (keyring_enum): Fixed problem with reading too many packets. Add support to read secret keyrings. * getkey.c (scan_keyring): Removed (lookup): New to replace scan_keyring. (scan_secret_keyring): Removed. (lookup_skc): New. Wed Mar 18 11:47:34 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (enum_keyblocks): New read mode 11. * keyid.c (elg_fingerprint_md): New and changed all other functions to call this if the packet version is 4 or above. Tue Mar 17 20:46:16 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_certificate): Add listing support for subkeys. Tue Mar 17 20:32:22 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (is_armored): Allow marker packet. Thu Mar 12 13:36:49 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (check_trust): Checks timestamp of pubkey. * sig-check. (do_check): Compares timestamps. Tue Mar 10 17:01:56 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Add call to init_signals. * signal.c: New. Mon Mar 9 12:43:42 1998 Werner Koch (wk@isil.d.shuttle.de) * dsa.c: New * packet.h, free-packet.c, parse-packet.c : Add support for DSA * sig-check.c, getkey.c, keyid.c, ringedit.c: Ditto. * seckey-cert.c: Ditto. * packet.h : Moved .digest_algo of signature packets to outer structure. Changed all references Sun Mar 8 13:06:42 1998 Werner Koch (wk@isil.d.shuttle.de) * openfile.c : Support for stdout filename "-". * mainproc.c (check_sig_and_print): Enhanced status output: * status.c (write_status_text): New. Fri Mar 6 16:10:54 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnode.c (clone_kbnode): Fixed private_flag. * mainproc.c (list_node): Output of string "Revoked" as user-id. Fri Mar 6 14:26:39 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Add userids to "-kv" and cleaned up this stuff. Fri Mar 6 12:45:58 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Changed semantics of the list-... commands and added a new one. Removed option "-d" * decrypt.c: New. * trustdb.c (init_trustdb): Autocreate directory only if it ends in "/.gnupg". Thu Mar 5 12:12:11 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (do_proc_packets): New. Common part of proc_packet. (proc_signature_packets): special version to handle signature data. * verify.c: New. * g10.c (aVerify): New. * plaintext.c (hash_datafiles): New. * compress.c (handle_compressed): Add callback arg, changed caller. Thu Mar 5 10:20:06 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c: Is nom the common source for gpg and gpgm * g10maint.c: Removed * Makefile.am: Add rule to build g10maint.c Thu Mar 5 08:43:59 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Changed the way clear text sigs are faked. Wed Mar 4 19:47:37 1998 Werner Koch (wk@isil.d.shuttle.de) * g10maint.c (aMuttKeyList): New * keylist.c: New. Wed Mar 4 17:20:33 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (get_pubkey_byname): Kludge to allow 0x prefix. Tue Mar 3 13:46:55 1998 Werner Koch (wk@isil.d.shuttle.de) * g10maint.c (main): New option --gen-random. Tue Mar 3 09:50:08 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aDeleteSecretKey): New. (aEditSig): Add option "--edit-key" as synonym for "--edit-sig". (aDeleteSecretKey): New. * getkey.c (seckey_available): New. * sign.c (delete_key): Enhanced to delete secret keys, changed all callers. Mon Mar 2 21:23:48 1998 Werner Koch (wk@isil.d.shuttle.de) * pkc_list.c (build_pkc_list): Add interactive input of user ID. Mon Mar 2 20:54:05 1998 Werner Koch (wk@isil.d.shuttle.de) * pkclist.c (do_we_trust_pre): New. (add_ownertrust): Add message. * trustdb.c (enum_trust_web): Quick fix. Mon Mar 2 13:50:53 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): New action aDeleteKey * sign.c (delete_key): New. Sun Mar 1 16:38:58 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (do_check): No returns TRUST_UNDEFINED instead of eof error. Fri Feb 27 18:14:03 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (find_header): Removed trailing CR on headers. Fri Feb 27 18:02:48 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (keyring_search) [MINGW32]: Open and close file here because rename does not work on open files. Chnaged callers. Fri Feb 27 16:43:11 1998 Werner Koch (wk@isil.d.shuttle.de) * sig-check.c (do_check): Add an md_enable. * mainproc.c (do_check_sig): Use md_open in case of detached sig (proc_tree): Take detached sigs into account. Fri Feb 27 15:22:46 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): Make use of GNUPGHOME envvar. * g10main.c (main): Ditto. Wed Feb 25 11:40:04 1998 Werner Koch (wk@isil.d.shuttle.de) * plaintext.c (ask_for_detached_datafile): add opt.verbose to info output. * openfile.c (open_sigfile): Try also name ending in ".asc" Wed Feb 25 08:41:00 1998 Werner Koch (wk@isil.d.shuttle.de) * keygen.c (generate_keypair): Fixed memory overflow. Tue Feb 24 15:51:55 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_certificate): Support for S2K. * build-packet.c (do_secret_cert): Ditto. * keygen.c (gen_elg): Ditto. * seckey-cert.c (check_elg): Ditto (protect_elg): Ditto. * sign.c (chnage_passphrase): Ditto. * passphrase.c (get_passphrase_hash): Support for a salt and changed all callers. (make_dek_from_passphrase): Ditto. Tue Feb 24 12:30:56 1998 Werner Koch (wk@isil.d.shuttle.de) * build-packet.c (hash_public_cert): Disabled debug output. Fri Feb 20 17:22:28 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (init_trustdb) [MINGW32]: Removed 2nd mkdir arg. (keyring_copy) [MINGW32]: Add a remove prior to the renames. Wed Feb 18 18:39:02 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (OMIT_DEPENDENCIES): New. * rsa.c: Replaced log_bug by BUG. Wed Feb 18 13:35:58 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (do_check_sig): Now uses hash_public_cert. * parse-packet.c (parse_certificate): Removed hashing. * packet.h (public_cert): Removed hash variable. * free-packet.c (copy_public_cert, free_public_cert): Likewise. * sig-check.c (check_key_signatures): Changed semantics. Wed Feb 18 12:11:28 1998 Werner Koch (wk@isil.d.shuttle.de) * trustdb.c (do_check): Add handling for revocation certificates. (build_sigrecs): Ditto. (check_sigs): Ditto. Wed Feb 18 09:31:04 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (armor_filter): Add afx->hdrlines. * revoke.c (gen_revoke): Add comment line. * dearmor.c (enarmor_file): Ditto. * sig-check.c (check_key_signature): Add handling for class 0x20. * mainproc.c : Ditto. Tue Feb 17 21:24:17 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c : Add header lines "...ARMORED FILE .." * dearmor.c (enarmor_file): New. * g10maint.c (main): New option "--enarmor" Tue Feb 17 19:03:33 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c : Changed a lot, because the packets are now stored a simple linlked list and not anymore in a complicatd tree structure. Tue Feb 17 10:14:48 1998 Werner Koch (wk@isil.d.shuttle.de) * free_packet.c (cmp_public_certs): New. (cmp_user_ids): New. * kbnode.c (clone_kbnode): New. (release_kbnode): Add clone support. * ringedit.c (find_keyblock_bypkc): New. * sign.c (remove_keysigs): Self signatures are now skipped, changed arguments and all callers. * import.c : Add functionality. Tue Feb 17 09:31:40 1998 Werner Koch (wk@isil.d.shuttle.de) * options.h (homedir): New option. * g10.c, g10maint.c, getkey.c, keygen.c, trustdb.c (opt.homedir): New. * trustdb.c (init_trustdb): mkdir for hoem directory (sign_private_data): Renamed "sig" to "g10.sig" Mon Feb 16 20:02:03 1998 Werner Koch (wk@isil.d.shuttle.de) * kbnode.c (commit_kbnode): New. (delete_kbnode): removed unused first arg. Changed all Callers. * ringedit.c (keyblock_resource_name): New. (get_keyblock_handle): NULL for filename returns default resource. Mon Feb 16 19:38:48 1998 Werner Koch (wk@isil.d.shuttle.de) * sig-check.s (check_key_signature): Now uses the supplied public key to check the signature and not any more the one from the getkey.c (do_check): New. (check_signature): Most work moved to do_check. Mon Feb 16 14:48:57 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (find_header): Fixed another bug. Mon Feb 16 12:18:34 1998 Werner Koch (wk@isil.d.shuttle.de) * getkey.c (scan_keyring): Add handling of compressed keyrings. Mon Feb 16 10:44:51 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c, g10maint.c (strusage): Rewrote. (build_list): New Mon Feb 16 08:58:41 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (use_armor): New. Sat Feb 14 14:30:57 1998 Werner Koch (wk@isil.d.shuttle.de) * mainproc.c (proc_tree): Sigclass fix. Sat Feb 14 14:16:33 1998 Werner Koch (wk@isil.d.shuttle.de) * armor.c (armor_filter): Changed version and comment string. * encode.c, sign.c, keygen.c: Changed all comment packet strings. Sat Feb 14 12:39:24 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (aGenRevoke): New command. * revoke.c: New. * sign.c (make_keysig_packet): Add support for sigclass 0x20. Fri Feb 13 20:18:14 1998 Werner Koch (wk@isil.d.shuttle.de) * ringedit.c (enum_keyblocks, keyring_enum): New. Fri Feb 13 19:33:40 1998 Werner Koch (wk@isil.d.shuttle.de) * export.c: Add functionality. * keygen.c (generate_keypair): Moved the leading comment behind the key packet. * kbnode.c (walk_kbnode): Fixed. * g10.c (main): listing armored keys now work. Fri Feb 13 16:17:43 1998 Werner Koch (wk@isil.d.shuttle.de) * parse-packet.c (parse_publickey, parse_signature): Fixed calls to mpi_read used for ELG b. Fri Feb 13 15:13:23 1998 Werner Koch (wk@isil.d.shuttle.de) * g10.c (main): changed formatting of help output. Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo) * pubkey-enc.c (get_session_key): rewritten diff --git a/g10/g10.c b/g10/g10.c index 66a91bf94..48774ebd1 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -1,1515 +1,1517 @@ /* g10.c - The GnuPG utility (main for gpg) * Copyright (C) 1998, 1999 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #define MAINTAINER_OPTIONS #include "packet.h" #include "iobuf.h" #include "memory.h" #include "util.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "mpi.h" #include "cipher.h" #include "filter.h" #include "ttyio.h" #include "i18n.h" #include "status.h" #include "g10defs.h" #include "hkp.h" enum cmd_and_opt_values { aNull = 0, oArmor = 'a', aDetachedSign = 'b', aSym = 'c', aDecrypt = 'd', aEncr = 'e', oInteractive = 'i', oKOption = 'k', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', oRecipient = 'r', aSign = 's', oTextmodeShort= 't', oUser = 'u', oVerbose = 'v', oCompress = 'z', oNotation = 'N', oBatch = 500, aClearsign, aStore, aKeygen, aSignEncr, aSignKey, aListPackets, aEditKey, aDeleteKey, aDeleteSecretKey, aKMode, aKModeC, aImport, aFastImport, aVerify, aListKeys, aListSigs, aListSecretKeys, aSendKeys, aRecvKeys, aExport, aExportAll, aExportSecret, aCheckKeys, aGenRevoke, aPrimegen, aPrintMD, aPrintMDs, aCheckTrustDB, aUpdateTrustDB, aFixTrustDB, aListTrustDB, aListTrustPath, aExportOwnerTrust, aImportOwnerTrust, aDeArmor, aEnArmor, aGenRandom, oTextmode, oFingerprint, oAnswerYes, oAnswerNo, oKeyring, oSecretKeyring, oDefaultKey, oOptions, oDebug, oDebugAll, oStatusFD, oNoComment, oNoVersion, oEmitVersion, oCompletesNeeded, oMarginalsNeeded, oMaxCertDepth, oLoadExtension, oRFC1991, oOpenPGP, oCipherAlgo, oDigestAlgo, oCompressAlgo, oPasswdFD, oQuickRandom, oNoVerbose, oTrustDBName, oNoSecmemWarn, oNoArmor, oNoDefKeyring, oNoGreeting, oNoTTY, oNoOptions, oNoBatch, oHomedir, oWithColons, oWithKeyData, oSkipVerify, oCompressKeys, oCompressSigs, oAlwaysTrust, oEmuChecksumBug, oRunAsShmCP, oSetFilename, oSetPolicyURL, oUseEmbeddedFilename, oComment, oDefaultComment, oThrowKeyid, oForceV3Sigs, oForceMDC, oS2KMode, oS2KDigest, oS2KCipher, oCharset, oNotDashEscaped, oEscapeFrom, oLockOnce, oLockMultiple, oKeyServer, oEncryptTo, oNoEncryptTo, oLoggerFD, oUtf8Strings, oNoUtf8Strings, aTest }; static ARGPARSE_OPTS opts[] = { { 300, NULL, 0, N_("@Commands:\n ") }, { aSign, "sign", 256, N_("|[file]|make a signature")}, { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature") }, { aDetachedSign, "detach-sign", 256, N_("make a detached signature")}, { aEncr, "encrypt", 256, N_("encrypt data")}, { aSym, "symmetric", 256, N_("encryption only with symmetric cipher")}, { aStore, "store", 256, N_("store only")}, { aDecrypt, "decrypt", 256, N_("decrypt data (default)")}, { aVerify, "verify" , 256, N_("verify a signature")}, { aListKeys, "list-keys", 256, N_("list keys")}, { aListKeys, "list-public-keys", 256, "@" }, { aListSigs, "list-sigs", 256, N_("list keys and signatures")}, { aCheckKeys, "check-sigs",256, N_("check key signatures")}, { oFingerprint, "fingerprint", 256, N_("list keys and fingerprints")}, { aListSecretKeys, "list-secret-keys", 256, N_("list secret keys")}, { aKeygen, "gen-key", 256, N_("generate a new key pair")}, { aDeleteKey, "delete-key",256, N_("remove key from the public keyring")}, { aEditKey, "edit-key" ,256, N_("sign or edit a key")}, { aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")}, { aExport, "export" , 256, N_("export keys") }, { aSendKeys, "send-keys" , 256, N_("export keys to a key server") }, { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") }, { aExportAll, "export-all" , 256, "@" }, { aExportSecret, "export-secret-keys" , 256, "@" }, { aImport, "import", 256 , N_("import/merge keys")}, { aFastImport, "fast-import", 256 , "@"}, { aListPackets, "list-packets",256,N_("list only the sequence of packets")}, { aExportOwnerTrust, "export-ownertrust", 256, N_("export the ownertrust values")}, { aImportOwnerTrust, "import-ownertrust", 256 , N_("import ownertrust values")}, { aUpdateTrustDB, "update-trustdb",0 , N_("|[NAMES]|update the trust database")}, { aCheckTrustDB, "check-trustdb",0 , N_("|[NAMES]|check the trust database")}, { aFixTrustDB, "fix-trustdb",0 , N_("fix a corrupted trust database")}, { aDeArmor, "dearmor", 256, N_("De-Armor a file or stdin") }, { aEnArmor, "enarmor", 256, N_("En-Armor a file or stdin") }, { aPrintMD, "print-md" , 256, N_("|algo [files]|print message digests")}, { aPrintMDs, "print-mds" , 256, N_("print all message digests")}, #ifdef MAINTAINER_OPTIONS { aPrimegen, "gen-prime" , 256, "@" }, { aGenRandom, "gen-random" , 256, "@" }, #endif { 301, NULL, 0, N_("@\nOptions:\n ") }, { oArmor, "armor", 0, N_("create ascii armored output")}, { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")}, { oRecipient, "remote-user", 2, "@"}, /* old option name */ { oEncryptTo, "encrypt-to", 2, "@" }, { oNoEncryptTo, "no-encrypt-to", 0, "@" }, { oUser, "local-user",2, N_("use this user-id to sign or decrypt")}, { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") }, { oTextmodeShort, NULL, 0, "@"}, { oTextmode, "textmode", 0, N_("use canonical text mode")}, { oOutput, "output", 2, N_("use as output file")}, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, { oNoTTY, "no-tty", 0, N_("don't use the terminal at all") }, { oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") }, { oForceMDC, "force-mdc", 0, N_("always use a MDC for encryption") }, { oDryRun, "dry-run", 0, N_("do not make any changes") }, /*{ oInteractive, "interactive", 0, N_("prompt before overwriting") }, */ { oBatch, "batch", 0, N_("batch mode: never ask")}, { oAnswerYes, "yes", 0, N_("assume yes on most questions")}, { oAnswerNo, "no", 0, N_("assume no on most questions")}, { oKeyring, "keyring" ,2, N_("add this keyring to the list of keyrings")}, { oSecretKeyring, "secret-keyring" ,2, N_("add this secret keyring to the list")}, { oDefaultKey, "default-key" ,2, N_("|NAME|use NAME as default secret key")}, { oKeyServer, "keyserver",2, N_("|HOST|use this keyserver to lookup keys")}, { oCharset, "charset" , 2, N_("|NAME|set terminal charset to NAME") }, { oOptions, "options" , 2, N_("read options from file")}, { oDebug, "debug" ,4|16, N_("set debugging flags")}, { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") }, { oNoComment, "no-comment", 0, N_("do not write comment packets")}, { oCompletesNeeded, "completes-needed", 1, N_("(default is 1)")}, { oMarginalsNeeded, "marginals-needed", 1, N_("(default is 3)")}, { oMaxCertDepth, "max-cert-depth", 1, "@" }, { oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")}, { oRFC1991, "rfc1991", 0, N_("emulate the mode described in RFC1991")}, { oOpenPGP, "openpgp", 0, N_("set all packet, cipher and digest options to OpenPGP behavior")}, { oS2KMode, "s2k-mode", 1, N_("|N|use passphrase mode N")}, { oS2KDigest, "s2k-digest-algo",2, N_("|NAME|use message digest algorithm NAME for passphrases")}, { oS2KCipher, "s2k-cipher-algo",2, N_("|NAME|use cipher algorithm NAME for passphrases")}, { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")}, { oDigestAlgo, "digest-algo", 2 , N_("|NAME|use message digest algorithm NAME")}, { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")}, { oThrowKeyid, "throw-keyid", 0, N_("throw keyid field of encrypted packets")}, { oNotation, "notation-data", 2, N_("|NAME=VALUE|use this notation data")}, { 302, NULL, 0, N_("@\nExamples:\n\n" " -se -r Bob [file] sign and encrypt for user Bob\n" " --clearsign [file] make a clear text signature\n" " --detach-sign [file] make a detached signature\n" " --list-keys [names] show keys\n" " --fingerprint [names] show fingerprints\n" ) }, /* hidden options */ { aExportOwnerTrust, "list-ownertrust",0 , "@"}, /* alias */ { aListTrustDB, "list-trustdb",0 , "@"}, { aListTrustPath, "list-trust-path",0, "@"}, { oKOption, NULL, 0, "@"}, { oPasswdFD, "passphrase-fd",1, "@" }, { aSignKey, "sign-key" ,256, "@" }, /* alias for edit-key */ { aDeleteSecretKey, "delete-secret-key",0, "@" }, { oQuickRandom, "quick-random", 0, "@"}, { oNoVerbose, "no-verbose", 0, "@"}, { oTrustDBName, "trustdb-name", 2, "@" }, { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */ { oNoArmor, "no-armor", 0, "@"}, { oNoDefKeyring, "no-default-keyring", 0, "@" }, { oNoGreeting, "no-greeting", 0, "@" }, { oNoOptions, "no-options", 0, "@" }, /* shortcut for --options /dev/null */ { oHomedir, "homedir", 2, "@" }, /* defaults to "~/.gnupg" */ { oNoBatch, "no-batch", 0, "@" }, { oWithColons, "with-colons", 0, "@"}, { oWithKeyData,"with-key-data", 0, "@"}, { aListKeys, "list-key", 0, "@" }, /* alias */ { aListSigs, "list-sig", 0, "@" }, /* alias */ { aCheckKeys, "check-sig",0, "@" }, /* alias */ { oSkipVerify, "skip-verify",0, "@" }, { oCompressKeys, "compress-keys",0, "@"}, { oCompressSigs, "compress-sigs",0, "@"}, { oAlwaysTrust, "always-trust", 0, "@"}, { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"}, { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" }, { oSetFilename, "set-filename", 2, "@" }, { oSetPolicyURL, "set-policy-url", 2, "@" }, { oComment, "comment", 2, "@" }, { oDefaultComment, "default-comment", 0, "@" }, { oNoVersion, "no-version", 0, "@"}, { oEmitVersion, "emit-version", 0, "@"}, { oNotDashEscaped, "not-dash-escaped", 0, "@" }, { oEscapeFrom, "escape-from-lines", 0, "@" }, { oLockOnce, "lock-once", 0, "@" }, { oLockMultiple, "lock-multiple", 0, "@" }, { oLoggerFD, "logger-fd",1, "@" }, { oUseEmbeddedFilename, "use-embedded-filename", 0, "@" }, { oUtf8Strings, "utf8-strings", 0, "@" }, { oNoUtf8Strings, "no-utf8-strings", 0, "@" }, {0} }; int g10_errors_seen = 0; static int utf8_strings = 0; static int maybe_setuid = 1; static char *build_list( const char *text, const char *(*mapf)(int), int (*chkf)(int) ); static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void print_hex( byte *p, size_t n ); static void print_mds( const char *fname, int algo ); static void add_notation_data( const char *string ); static int check_policy_url( const char *s ); const char * strusage( int level ) { static char *digests, *pubkeys, *ciphers; const char *p; switch( level ) { case 11: p = "gpg (GnuPG)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to .\n"); break; case 1: case 40: p = _("Usage: gpg [options] [files] (-h for help)"); break; case 41: p = _("Syntax: gpg [options] [files]\n" "sign, check, encrypt or decrypt\n" "default operation depends on the input data\n"); break; case 31: p = _("\nSupported algorithms:\n"); break; case 32: if( !ciphers ) ciphers = build_list("Cipher: ", cipher_algo_to_string, check_cipher_algo ); p = ciphers; break; case 33: if( !pubkeys ) pubkeys = build_list("Pubkey: ", pubkey_algo_to_string, check_pubkey_algo ); p = pubkeys; break; case 34: if( !digests ) digests = build_list("Hash: ", digest_algo_to_string, check_digest_algo ); p = digests; break; default: p = default_strusage(level); } return p; } static char * build_list( const char *text, const char * (*mapf)(int), int (*chkf)(int) ) { int i; const char *s; size_t n=strlen(text)+2; char *list, *p; if( maybe_setuid ) secmem_init( 0 ); /* drop setuid */ for(i=1; i < 110; i++ ) if( !chkf(i) && (s=mapf(i)) ) n += strlen(s) + 2; list = m_alloc( 21 + n ); *list = 0; for(p=NULL, i=1; i < 110; i++ ) { if( !chkf(i) && (s=mapf(i)) ) { if( !p ) p = stpcpy( list, text ); else p = stpcpy( p, ", "); p = stpcpy(p, s ); } } if( p ) p = stpcpy(p, "\n" ); return list; } static void i18n_init(void) { #ifdef ENABLE_NLS #ifdef HAVE_LC_MESSAGES setlocale( LC_TIME, "" ); setlocale( LC_MESSAGES, "" ); #else setlocale( LC_ALL, "" ); #endif bindtextdomain( PACKAGE, G10_LOCALEDIR ); textdomain( PACKAGE ); #endif } static void wrong_args( const char *text) { fputs(_("usage: gpg [options] "),stderr); fputs(text,stderr); putc('\n',stderr); g10_exit(2); } static void set_debug(void) { if( opt.debug & DBG_MEMORY_VALUE ) memory_debug_mode = 1; if( opt.debug & DBG_MEMSTAT_VALUE ) memory_stat_debug_mode = 1; if( opt.debug & DBG_MPI_VALUE ) mpi_debug_mode = 1; if( opt.debug & DBG_CIPHER_VALUE ) g10c_debug_mode = 1; if( opt.debug & DBG_IOBUF_VALUE ) iobuf_debug_mode = 1; } static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) { enum cmd_and_opt_values cmd = *ret_cmd; if( !cmd || cmd == new_cmd ) cmd = new_cmd; else if( cmd == aSign && new_cmd == aEncr ) cmd = aSignEncr; else if( cmd == aEncr && new_cmd == aSign ) cmd = aSignEncr; else if( cmd == aKMode && new_cmd == aSym ) cmd = aKModeC; else if( ( cmd == aSign && new_cmd == aClearsign ) || ( cmd == aClearsign && new_cmd == aSign ) ) cmd = aClearsign; else { log_error(_("conflicting commands\n")); g10_exit(2); } *ret_cmd = cmd; } int main( int argc, char **argv ) { ARGPARSE_ARGS pargs; IOBUF a; int rc=0; int orig_argc; char **orig_argv; const char *fname; STRLIST sl, remusr= NULL, locusr=NULL; STRLIST nrings=NULL, sec_nrings=NULL; armor_filter_context_t afx; int detached_sig = 0; FILE *configfp = NULL; char *configname = NULL; unsigned configlineno; int parse_debug = 0; int default_config =1; int default_keyring = 1; int greeting = 1; enum cmd_and_opt_values cmd = 0; const char *trustdb_name = NULL; char *def_cipher_string = NULL; char *def_digest_string = NULL; char *s2k_cipher_string = NULL; char *s2k_digest_string = NULL; int pwfd = -1; #ifdef USE_SHM_COPROCESSING ulong requested_shm_size=0; #endif trap_unaligned(); secmem_set_flags( secmem_get_flags() | 2 ); /* suspend warnings */ /* Please note that we may running SUID(ROOT), so be very CAREFUL * when adding any stuff between here and the call to * secmem_init() somewhere after the option parsing */ log_set_name("gpg"); secure_random_alloc(); /* put random number into secure memory */ disable_core_dumps(); init_signals(); create_dotlock(NULL); /* register locking cleanup */ i18n_init(); opt.compress = -1; /* defaults to standard compress level */ /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; opt.def_digest_algo = 0; opt.def_compress_algo = 2; opt.s2k_mode = 3; /* iterated+salted */ opt.s2k_digest_algo = DIGEST_ALGO_RMD160; opt.s2k_cipher_algo = CIPHER_ALGO_BLOWFISH; opt.completes_needed = 1; opt.marginals_needed = 3; opt.max_cert_depth = 5; opt.homedir = getenv("GNUPGHOME"); if( !opt.homedir || !*opt.homedir ) { #ifdef HAVE_DRIVE_LETTERS opt.homedir = "c:/gnupg"; #else opt.homedir = "~/.gnupg"; #endif } /* check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ while( arg_parse( &pargs, opts) ) { if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll ) parse_debug++; else if( pargs.r_opt == oOptions ) { /* yes there is one, so we do not try the default one, but * read the option file when it is encountered at the commandline */ default_config = 0; } else if( pargs.r_opt == oNoOptions ) default_config = 0; /* --no-options */ else if( pargs.r_opt == oHomedir ) opt.homedir = pargs.r.ret_str; #ifdef USE_SHM_COPROCESSING else if( pargs.r_opt == oRunAsShmCP ) { /* does not make sense in a options file, we do it here, * so that we are the able to drop setuid as soon as possible */ opt.shm_coprocess = 1; requested_shm_size = pargs.r.ret_ulong; } else if ( pargs.r_opt == oStatusFD ) { /* this is needed to ensure that the status-fd filedescriptor is * initialized when init_shm_coprocessing() is called */ set_status_fd( pargs.r.ret_int ); } #endif } #ifdef USE_SHM_COPROCESSING if( opt.shm_coprocess ) { init_shm_coprocessing(requested_shm_size, 1 ); } #endif /* initialize the secure memory. */ secmem_init( 16384 ); maybe_setuid = 0; /* Okay, we are now working under our real uid */ if( default_config ) configname = make_filename(opt.homedir, "options", NULL ); argc = orig_argc; argv = orig_argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* do not remove the args */ next_pass: if( configname ) { configlineno = 0; configfp = fopen( configname, "r" ); if( !configfp ) { if( default_config ) { if( parse_debug ) log_info(_("NOTE: no default option file `%s'\n"), configname ); } else { log_error(_("option file `%s': %s\n"), configname, strerror(errno) ); g10_exit(2); } m_free(configname); configname = NULL; } if( parse_debug && configname ) log_info(_("reading options from `%s'\n"), configname ); default_config = 0; } while( optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) { switch( pargs.r_opt ) { case aCheckKeys: set_cmd( &cmd, aCheckKeys); break; case aListPackets: set_cmd( &cmd, aListPackets); break; case aImport: set_cmd( &cmd, aImport); break; case aFastImport: set_cmd( &cmd, aFastImport); break; case aSendKeys: set_cmd( &cmd, aSendKeys); break; case aRecvKeys: set_cmd( &cmd, aRecvKeys); break; case aExport: set_cmd( &cmd, aExport); break; case aExportAll: set_cmd( &cmd, aExportAll); break; case aListKeys: set_cmd( &cmd, aListKeys); break; case aListSigs: set_cmd( &cmd, aListSigs); break; case aExportSecret: set_cmd( &cmd, aExportSecret); break; case aDeleteSecretKey: set_cmd( &cmd, aDeleteSecretKey); break; case aDeleteKey: set_cmd( &cmd, aDeleteKey); break; case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aSym: set_cmd( &cmd, aSym); break; case aDecrypt: set_cmd( &cmd, aDecrypt); break; case aEncr: set_cmd( &cmd, aEncr); break; case aSign: set_cmd( &cmd, aSign ); break; case aKeygen: set_cmd( &cmd, aKeygen); break; case aSignKey: set_cmd( &cmd, aSignKey); break; case aStore: set_cmd( &cmd, aStore); break; case aEditKey: set_cmd( &cmd, aEditKey); break; case aClearsign: set_cmd( &cmd, aClearsign); break; case aGenRevoke: set_cmd( &cmd, aGenRevoke); break; case aVerify: set_cmd( &cmd, aVerify); break; #ifdef MAINTAINER_OPTIONS case aPrimegen: set_cmd( &cmd, aPrimegen); break; case aGenRandom: set_cmd( &cmd, aGenRandom); break; #endif case aPrintMD: set_cmd( &cmd, aPrintMD); break; case aPrintMDs: set_cmd( &cmd, aPrintMDs); break; case aListTrustDB: set_cmd( &cmd, aListTrustDB); break; case aCheckTrustDB: set_cmd( &cmd, aCheckTrustDB); break; case aUpdateTrustDB: set_cmd( &cmd, aUpdateTrustDB); break; case aFixTrustDB: set_cmd( &cmd, aFixTrustDB); break; case aListTrustPath: set_cmd( &cmd, aListTrustPath); break; case aDeArmor: set_cmd( &cmd, aDeArmor); greeting = 0; break; case aEnArmor: set_cmd( &cmd, aEnArmor); greeting = 0; break; case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break; case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break; case oArmor: opt.armor = 1; opt.no_armor=0; break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oQuiet: opt.quiet = 1; break; case oNoTTY: opt.quiet = 1; tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; case oInteractive: opt.interactive = 1; break; case oVerbose: g10_opt_verbose++; opt.verbose++; opt.list_sigs=1; break; case oKOption: set_cmd( &cmd, aKMode ); break; case oBatch: opt.batch = 1; greeting = 0; break; case oAnswerYes: opt.answer_yes = 1; break; case oAnswerNo: opt.answer_no = 1; break; case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break; case oDebug: opt.debug |= pargs.r.ret_ulong; break; case oDebugAll: opt.debug = ~0; break; case oStatusFD: set_status_fd( pargs.r.ret_int ); break; case oLoggerFD: log_set_logfile( NULL, pargs.r.ret_int ); break; case oFingerprint: opt.fingerprint++; break; case oSecretKeyring: append_to_strlist( &sec_nrings, pargs.r.ret_str); break; case oOptions: /* config files may not be nested (silently ignore them) */ if( !configfp ) { m_free(configname); configname = m_strdup(pargs.r.ret_str); goto next_pass; } break; case oNoArmor: opt.no_armor=1; opt.armor=0; break; case oNoDefKeyring: default_keyring = 0; break; case oNoGreeting: greeting = 0; break; case oNoVerbose: g10_opt_verbose = 0; opt.verbose = 0; opt.list_sigs=0; break; case oQuickRandom: quick_random_gen(1); break; case oNoComment: opt.no_comment=1; break; case oNoVersion: opt.no_version=1; break; case oEmitVersion: opt.no_version=0; break; case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break; case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break; case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break; case oTrustDBName: trustdb_name = pargs.r.ret_str; break; case oDefaultKey: opt.def_secret_key = pargs.r.ret_str; break; case oNoOptions: break; /* no-options */ case oHomedir: opt.homedir = pargs.r.ret_str; break; case oNoBatch: opt.batch = 0; break; case oWithKeyData: opt.with_key_data=1; /* fall thru */ case oWithColons: opt.with_colons=':'; break; case oSkipVerify: opt.skip_verify=1; break; case oCompressAlgo: opt.def_compress_algo = pargs.r.ret_int; break; case oCompressKeys: opt.compress_keys = 1; break; case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break; case oAlwaysTrust: opt.always_trust = 1; break; case oLoadExtension: register_cipher_extension(orig_argc? *orig_argv:NULL, pargs.r.ret_str); break; case oRFC1991: opt.rfc1991 = 1; opt.no_comment = 1; opt.escape_from = 1; break; case oOpenPGP: opt.rfc1991 = 0; opt.escape_from = 0; opt.force_v3_sigs = 0; opt.compress_keys = 0; /* not mandated but we do it */ opt.compress_sigs = 0; /* ditto. */ opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; opt.def_digest_algo = 0; opt.def_compress_algo = 2; opt.s2k_mode = 3; /* iterated+salted */ opt.s2k_digest_algo = DIGEST_ALGO_RMD160; opt.s2k_cipher_algo = CIPHER_ALGO_BLOWFISH; break; case oEmuChecksumBug: opt.emulate_bugs |= EMUBUG_GPGCHKSUM; break; case oCompressSigs: opt.compress_sigs = 1; break; case oRunAsShmCP: #ifndef USE_SHM_COPROCESSING /* not possible in the option file, * but we print the warning here anyway */ log_error("shared memory coprocessing is not available\n"); #endif break; case oSetFilename: opt.set_filename = pargs.r.ret_str; break; case oSetPolicyURL: opt.set_policy_url = pargs.r.ret_str; break; case oUseEmbeddedFilename: opt.use_embedded_filename = 1; break; case oComment: opt.comment_string = pargs.r.ret_str; break; case oDefaultComment: opt.comment_string = NULL; break; case oThrowKeyid: opt.throw_keyid = 1; break; case oForceV3Sigs: opt.force_v3_sigs = 1; break; case oForceMDC: opt.force_mdc = 1; break; case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break; case oS2KDigest: s2k_digest_string = m_strdup(pargs.r.ret_str); break; case oS2KCipher: s2k_cipher_string = m_strdup(pargs.r.ret_str); break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptTo: /* store the recipient in the second list */ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); sl->flags = 1; break; case oRecipient: /* store the recipient */ add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); break; case oTextmodeShort: opt.textmode = 2; break; case oTextmode: opt.textmode=1; break; case oUser: /* store the local users */ add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings ); break; case oCompress: opt.compress = pargs.r.ret_int; break; case oPasswdFD: pwfd = pargs.r.ret_int; break; case oCipherAlgo: def_cipher_string = m_strdup(pargs.r.ret_str); break; case oDigestAlgo: def_digest_string = m_strdup(pargs.r.ret_str); break; case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break; case oCharset: if( set_native_charset( pargs.r.ret_str ) ) log_error(_("%s is not a valid character set\n"), pargs.r.ret_str); break; case oNotDashEscaped: opt.not_dash_escaped = 1; break; case oEscapeFrom: opt.escape_from = 1; break; case oLockOnce: opt.lock_once = 1; break; case oLockMultiple: opt.lock_once = 0; break; case oKeyServer: opt.keyserver_name = pargs.r.ret_str; break; case oNotation: add_notation_data( pargs.r.ret_str ); break; case oUtf8Strings: utf8_strings = 1; break; case oNoUtf8Strings: utf8_strings = 0; break; default : pargs.err = configfp? 1:2; break; } } if( configfp ) { fclose( configfp ); configfp = NULL; m_free(configname); configname = NULL; goto next_pass; } m_free( configname ); configname = NULL; if( log_get_errorcount(0) ) g10_exit(2); if( greeting ) { fprintf(stderr, "%s %s; %s\n", strusage(11), strusage(13), strusage(14) ); fprintf(stderr, "%s\n", strusage(15) ); #ifdef IS_DEVELOPMENT_VERSION log_info("NOTE: this is a development version!\n"); #endif } if( opt.batch ) tty_batchmode( 1 ); secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */ set_debug(); g10_opt_homedir = opt.homedir; /* must do this after dropping setuid, because string_to... * may try to load an module */ if( def_cipher_string ) { opt.def_cipher_algo = string_to_cipher_algo(def_cipher_string); m_free(def_cipher_string); def_cipher_string = NULL; if( check_cipher_algo(opt.def_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } if( def_digest_string ) { opt.def_digest_algo = string_to_digest_algo(def_digest_string); m_free(def_digest_string); def_digest_string = NULL; if( check_digest_algo(opt.def_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } if( s2k_cipher_string ) { opt.s2k_cipher_algo = string_to_cipher_algo(s2k_cipher_string); m_free(s2k_cipher_string); s2k_cipher_string = NULL; if( check_cipher_algo(opt.s2k_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } if( s2k_digest_string ) { opt.s2k_digest_algo = string_to_digest_algo(s2k_digest_string); m_free(s2k_digest_string); s2k_digest_string = NULL; if( check_digest_algo(opt.s2k_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } if( opt.set_policy_url ) { if( check_policy_url( opt.set_policy_url ) ) log_error(_("the given policy URL is invalid\n")); } if( opt.def_compress_algo < 1 || opt.def_compress_algo > 2 ) log_error(_("compress algorithm must be in range %d..%d\n"), 1, 2); if( opt.completes_needed < 1 ) log_error(_("completes-needed must be greater than 0\n")); if( opt.marginals_needed < 2 ) log_error(_("marginals-needed must be greater than 1\n")); if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 ) log_error(_("max-cert-depth must be in range 1 to 255\n")); switch( opt.s2k_mode ) { case 0: log_info(_("NOTE: simple S2K mode (0) is strongly discouraged\n")); break; case 1: case 3: break; default: log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } if( log_get_errorcount(0) ) g10_exit(2); if( !cmd && opt.fingerprint ) set_cmd( &cmd, aListKeys); if( cmd == aKMode || cmd == aKModeC ) { /* kludge to be compatible to pgp */ if( cmd == aKModeC ) { opt.fingerprint = 1; cmd = aKMode; } opt.list_sigs = 0; if( opt.verbose > 2 ) opt.check_sigs++; if( opt.verbose > 1 ) opt.list_sigs++; opt.verbose = opt.verbose > 1; g10_opt_verbose = opt.verbose; } /* kludge to let -sat generate a clear text signature */ if( opt.textmode == 2 && !detached_sig && opt.armor && cmd == aSign ) cmd = aClearsign; if( opt.verbose > 1 ) set_packet_list_mode(1); /* add the keyrings, but not for some special commands and * not in case of "-kvv userid keyring" */ if( cmd != aDeArmor && cmd != aEnArmor && !(cmd == aKMode && argc == 2 ) ) { if( !sec_nrings && default_keyring ) /* add default secret rings */ add_keyblock_resource("secring.gpg", 0, 1); for(sl = sec_nrings; sl; sl = sl->next ) add_keyblock_resource( sl->d, 0, 1 ); if( !nrings && default_keyring ) /* add default ring */ add_keyblock_resource("pubring.gpg", 0, 0); for(sl = nrings; sl; sl = sl->next ) add_keyblock_resource( sl->d, 0, 0 ); } FREE_STRLIST(nrings); FREE_STRLIST(sec_nrings); if( pwfd != -1 ) /* read the passphrase now. */ read_passphrase_from_fd( pwfd ); fname = argc? *argv : NULL; switch( cmd ) { case aPrimegen: case aPrintMD: case aPrintMDs: case aGenRandom: case aDeArmor: case aEnArmor: case aFixTrustDB: break; case aKMode: case aListKeys: case aListSecretKeys: case aCheckKeys: if( opt.with_colons ) /* need this to list the trust */ rc = setup_trustdb(1, trustdb_name ); break; case aExportOwnerTrust: rc = setup_trustdb( 0, trustdb_name ); break; case aListTrustDB: rc = setup_trustdb( argc? 1:0, trustdb_name ); break; default: rc = setup_trustdb(1, trustdb_name ); break; } if( rc ) log_error(_("failed to initialize the TrustDB: %s\n"), g10_errstr(rc)); switch( cmd ) { case aStore: /* only store the file */ if( argc > 1 ) wrong_args(_("--store [filename]")); if( (rc = encode_store(fname)) ) log_error_f( print_fname_stdin(fname), "store failed: %s\n", g10_errstr(rc) ); break; case aSym: /* encrypt the given file only with the symmetric cipher */ if( argc > 1 ) wrong_args(_("--symmetric [filename]")); if( (rc = encode_symmetric(fname)) ) log_error_f(print_fname_stdin(fname), "symmetric encryption failed: %s\n",g10_errstr(rc) ); break; case aEncr: /* encrypt the given file */ if( argc > 1 ) wrong_args(_("--encrypt [filename]")); if( (rc = encode_crypt(fname,remusr)) ) log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aSign: /* sign the given file */ sl = NULL; if( detached_sig ) { /* sign all files */ for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); } else { if( argc > 1 ) wrong_args(_("--sign [filename]")); if( argc ) { sl = m_alloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } } if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) ) log_error("signing failed: %s\n", g10_errstr(rc) ); free_strlist(sl); break; case aSignEncr: /* sign and encrypt the given file */ if( argc > 1 ) wrong_args(_("--sign --encrypt [filename]")); if( argc ) { sl = m_alloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } else sl = NULL; if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) ) log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); free_strlist(sl); break; case aClearsign: /* make a clearsig */ if( argc > 1 ) wrong_args(_("--clearsign [filename]")); if( (rc = clearsign_file(fname, locusr, NULL)) ) log_error("%s: clearsign failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aVerify: if( (rc = verify_signatures( argc, argv ) )) log_error("verify signatures failed: %s\n", g10_errstr(rc) ); break; case aDecrypt: if( argc > 1 ) wrong_args(_("--decrypt [filename]")); if( (rc = decrypt_message( fname ) )) log_error("decrypt_message failed: %s\n", g10_errstr(rc) ); break; case aSignKey: /* sign the key given as argument */ case aEditKey: /* Edit a key signature */ if( !argc ) wrong_args(_("--edit-key username [commands]")); if( argc > 1 ) { sl = NULL; for( argc--, argv++ ; argc; argc--, argv++ ) append_to_strlist2( &sl, *argv, utf8_strings ); keyedit_menu( fname, locusr, sl ); free_strlist(sl); } else keyedit_menu(fname, locusr, NULL ); break; case aDeleteSecretKey: if( argc != 1 ) wrong_args(_("--delete-secret-key username")); case aDeleteKey: if( argc != 1 ) wrong_args(_("--delete-key username")); /* note: fname is the user id! */ /* fixme: do utf8 conversion */ if( (rc = delete_key(fname, cmd==aDeleteSecretKey)) ) log_error("%s: delete key failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) ); break; case aCheckKeys: opt.check_sigs = 1; case aListSigs: opt.list_sigs = 1; case aListKeys: public_key_list( argc, argv ); break; case aListSecretKeys: secret_key_list( argc, argv ); break; case aKMode: /* list keyring */ if( argc < 2 ) /* -kv [userid] */ public_key_list( (argc && **argv)? 1:0, argv ); else if( argc == 2 ) { /* -kv userid keyring */ if( access( argv[1], R_OK ) ) { log_error(_("can't open %s: %s\n"), print_fname_stdin(argv[1]), strerror(errno)); } else { /* add keyring (default keyrings are not registered in this * special case */ add_keyblock_resource( argv[1], 0, 0 ); public_key_list( **argv?1:0, argv ); } } else wrong_args(_("-k[v][v][v][c] [userid] [keyring]") ); break; case aKeygen: /* generate a key (interactive) */ if( argc ) wrong_args("--gen-key"); generate_keypair(); break; case aFastImport: case aImport: if( !argc ) { rc = import_keys( NULL, (cmd == aFastImport) ); if( rc ) log_error("import failed: %s\n", g10_errstr(rc) ); } for( ; argc; argc--, argv++ ) { rc = import_keys( *argv, (cmd == aFastImport) ); if( rc ) log_error("import from `%s' failed: %s\n", *argv, g10_errstr(rc) ); } break; case aExport: case aExportAll: case aSendKeys: case aRecvKeys: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); if( cmd == aSendKeys ) hkp_export( sl ); else if( cmd == aRecvKeys ) hkp_import( sl ); else export_pubkeys( sl, (cmd == aExport) ); free_strlist(sl); break; case aExportSecret: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); export_seckeys( sl ); free_strlist(sl); break; case aGenRevoke: if( argc != 1 ) wrong_args("--gen-revoke user-id"); gen_revoke( *argv ); break; case aDeArmor: if( argc > 1 ) wrong_args("--dearmor [file]"); rc = dearmor_file( argc? *argv: NULL ); if( rc ) log_error(_("dearmoring failed: %s\n"), g10_errstr(rc)); break; case aEnArmor: if( argc > 1 ) wrong_args("--enarmor [file]"); rc = enarmor_file( argc? *argv: NULL ); if( rc ) log_error(_("enarmoring failed: %s\n"), g10_errstr(rc)); break; #ifdef MAINTAINER_OPTIONS case aPrimegen: if( argc == 1 ) { mpi_print( stdout, generate_public_prime( atoi(argv[0]) ), 1); putchar('\n'); } else if( argc == 2 ) { mpi_print( stdout, generate_elg_prime( 0, atoi(argv[0]), atoi(argv[1]), NULL,NULL ), 1); putchar('\n'); } else if( argc == 3 ) { MPI g = mpi_alloc(1); mpi_print( stdout, generate_elg_prime( 0, atoi(argv[0]), atoi(argv[1]), g, NULL ), 1); printf("\nGenerator: "); mpi_print( stdout, g, 1 ); putchar('\n'); mpi_free(g); } else if( argc == 4 ) { mpi_print( stdout, generate_elg_prime( 1, atoi(argv[0]), atoi(argv[1]), NULL,NULL ), 1); putchar('\n'); } else usage(1); break; #endif /* MAINTAINER OPTIONS */ #ifdef MAINTAINER_OPTIONS case aGenRandom: if( argc < 1 || argc > 2 ) wrong_args("--gen-random level [hex]"); { int c; int level = atoi(*argv); for(;;) { byte *p; if( argc == 2 ) { p = get_random_bits( 8, level, 0); printf("%02x", *p ); fflush(stdout); } else { p = get_random_bits( 800, level, 0); for(c=0; c < 100; c++ ) putchar( p[c] ); } m_free(p); } } break; #endif /* MAINTAINER OPTIONS */ case aPrintMD: if( argc < 1) wrong_args("--print-md algo [file]"); else { int algo = string_to_digest_algo(*argv); if( !algo ) log_error(_("invalid hash algorithm `%s'\n"), *argv ); else { argc--; argv++; if( !argc ) print_mds(NULL, algo); else { for(; argc; argc--, argv++ ) print_mds(*argv, algo); } } } break; case aPrintMDs: if( !argc ) print_mds(NULL,0); else { for(; argc; argc--, argv++ ) print_mds(*argv,0); } break; case aListTrustDB: if( !argc ) list_trustdb(NULL); else { for( ; argc; argc--, argv++ ) list_trustdb( *argv ); } break; case aUpdateTrustDB: if( argc ) wrong_args("--update-trustdb"); update_trustdb(); break; case aCheckTrustDB: if( !argc ) check_trustdb(NULL); else { for( ; argc; argc--, argv++ ) check_trustdb( *argv ); } break; case aFixTrustDB: log_error("this command ist not yet implemented.\"\n"); log_error("A workaround is to use \"--export-ownertrust\", remove\n"); log_error("the trustdb file and do an \"--import-ownertrust\".\n" ); break; case aListTrustPath: if( !argc ) wrong_args("--list-trust-path "); for( ; argc; argc--, argv++ ) list_trust_path( *argv ); break; case aExportOwnerTrust: if( argc ) wrong_args("--export-ownertrust"); export_ownertrust(); break; case aImportOwnerTrust: if( argc > 1 ) wrong_args("--import-ownertrust [file]"); import_ownertrust( argc? *argv:NULL ); break; case aListPackets: opt.list_packets=1; default: if( argc > 1 ) wrong_args(_("[filename]")); /* Issue some output for the unix newbie */ if( !fname && !opt.outfile && isatty( fileno(stdin) ) && isatty( fileno(stdout) ) && isatty( fileno(stderr) ) ) log_info(_("Go ahead and type your message ...\n")); if( !(a = iobuf_open(fname)) ) log_error(_("can't open `%s'\n"), print_fname_stdin(fname)); else { if( !opt.no_armor ) { if( use_armor_filter( a ) ) { memset( &afx, 0, sizeof afx); iobuf_push_filter( a, armor_filter, &afx ); } } if( cmd == aListPackets ) { set_packet_list_mode(1); opt.list_packets=1; } rc = proc_packets(NULL, a ); if( rc ) log_error("processing message failed: %s\n", g10_errstr(rc) ); iobuf_close(a); } break; } /* cleanup */ FREE_STRLIST(remusr); FREE_STRLIST(locusr); g10_exit(0); return 8; /*NEVER REACHED*/ } void g10_exit( int rc ) { - if( opt.debug & DBG_MEMSTAT_VALUE ) + if( opt.debug & DBG_MEMSTAT_VALUE ) { m_print_stats("on exit"); + random_dump_stats(); + } if( opt.debug ) secmem_dump_stats(); secmem_term(); rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; /*write_status( STATUS_LEAVE );*/ exit(rc ); } static void print_hex( byte *p, size_t n ) { int i; if( n == 20 ) { for(i=0; i < n ; i++, i++, p += 2 ) { if( i ) putchar(' '); if( i == 10 ) putchar(' '); printf("%02X%02X", *p, p[1] ); } } else if( n == 24 ) { for(i=0; i < n ; i += 4, p += 4 ) { if( i ) putchar(' '); if( i == 12 ) putchar(' '); printf("%02X%02X%02X%02X", *p, p[1], p[2], p[3] ); } } else { for(i=0; i < n ; i++, p++ ) { if( i ) putchar(' '); if( i && !(i%8) ) putchar(' '); printf("%02X", *p ); } } } static void print_mds( const char *fname, int algo ) { FILE *fp; char buf[1024]; size_t n; MD_HANDLE md; char *pname; if( !fname ) { fp = stdin; pname = m_strdup("[stdin]: "); } else { pname = m_alloc(strlen(fname)+3); strcpy(stpcpy(pname,fname),": "); fp = fopen( fname, "rb" ); } if( !fp ) { log_error("%s%s\n", pname, strerror(errno) ); m_free(pname); return; } md = md_open( 0, 0 ); if( algo ) md_enable( md, algo ); else { md_enable( md, DIGEST_ALGO_MD5 ); md_enable( md, DIGEST_ALGO_SHA1 ); md_enable( md, DIGEST_ALGO_RMD160 ); if( !check_digest_algo(DIGEST_ALGO_TIGER) ) md_enable( md, DIGEST_ALGO_TIGER ); } while( (n=fread( buf, 1, DIM(buf), fp )) ) md_write( md, buf, n ); if( ferror(fp) ) log_error("%s%s\n", pname, strerror(errno) ); else { md_final(md); if( algo ) { if( fname ) fputs( pname, stdout ); print_hex(md_read(md, algo), md_digest_length(algo) ); } else { printf( "%s MD5 = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_MD5), 16 ); printf("\n%s SHA1 = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_SHA1), 20 ); printf("\n%sRMD160 = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_RMD160), 20 ); if( !check_digest_algo(DIGEST_ALGO_TIGER) ) { printf("\n%s TIGER = ", fname?pname:"" ); print_hex(md_read(md, DIGEST_ALGO_TIGER), 24 ); } } putchar('\n'); } md_close(md); if( fp != stdin ) fclose(fp); } /**************** * Check the supplied name,value string and add it to the notation * data to be used for signatures. */ static void add_notation_data( const char *string ) { const char *s; const char *s2; STRLIST sl; int critical=0; int highbit=0; if( *string == '!' ) { critical = 1; string++; } s = string; if( !*s || (*s & 0x80) || (!isalpha(*s) && *s != '_') ) { log_error(_("the first character of a notation name " "must be a letter or an underscore\n") ); return; } for(s++; *s != '='; s++ ) { if( !*s || (*s & 0x80) || (!isalnum(*s) && *s != '_' && *s != '.' ) ) { log_error(_("a notation name must have only letters, " "digits, dots or underscores and end with an '='\n") ); return; } } if( s[-1] == '.' || ((s2=strstr(string, "..")) && s2 < s ) ) { log_error(_("dots in a notation name must be surrounded " "by other characters\n") ); return; } /* we do only support printabe text - therefore we enforce the use * of only printable characters (an empty value is valid) */ for( s++; *s ; s++ ) { if( iscntrl(*s) ) { log_error(_("a notation value must not use " "any control characters\n") ); return; } else if( *s & 0x80 ) highbit = 1; } if( highbit ) /* must use UTF8 encoding */ sl = add_to_strlist2( &opt.notation_data, string, utf8_strings ); else sl = add_to_strlist( &opt.notation_data, string ); if( critical ) sl->flags |= 1; } static int check_policy_url( const char *s ) { if( *s == '!' ) s++; if( !*s ) return -1; for(; *s ; s++ ) { if( (*s & 0x80) || iscntrl(*s) ) return -1; } return 0; } diff --git a/g10/main.h b/g10/main.h index f2c059b33..fec9ae04d 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,152 +1,154 @@ /* main.h * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "types.h" #include "iobuf.h" #include "mpi.h" #include "cipher.h" #include "keydb.h" #define DEFAULT_CIPHER_ALGO CIPHER_ALGO_BLOWFISH #define DEFAULT_PUBKEY_ALGO PUBKEY_ALGO_ELGAMAL #define DEFAULT_DIGEST_ALGO DIGEST_ALGO_RMD160 typedef struct { int header_okay; PK_LIST pk_list; cipher_filter_context_t cfx; } encrypt_filter_context_t; /*-- g10.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note( int algo ); void print_cipher_algo_note( int algo ); void print_digest_algo_note( int algo ); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); void disable_core_dumps(void); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( MPI a ); u16 checksum_mpi_counted_nbits( MPI a ); u32 buffer_to_u32( const byte *buffer ); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int encode_symmetric( const char *filename ); int encode_store( const char *filename ); int encode_crypt( const char *filename, STRLIST remusr ); int encrypt_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); /*-- sign.c --*/ int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ); int sign_file( STRLIST filenames, int detached, STRLIST locusr, int do_encrypt, STRLIST remusr, const char *outfile ); int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ); /*-- sig-check.c --*/ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); +int check_key_signature2( KBNODE root, KBNODE node, + int *is_selfsig, u32 *r_expire ); /*-- delkey.c --*/ int delete_key( const char *username, int secure ); /*-- keyedit.c --*/ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds ); /*-- keygen.c --*/ u32 ask_expiredate(void); void generate_keypair(void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); 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 ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile( const char *iname, int mode, IOBUF *a ); IOBUF open_sigfile( const char *iname ); void copy_options_file( const char *destdir ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); MPI encode_session_key( DEK *dek, unsigned nbits ); MPI encode_md_value( int pubkey_algo, MD_HANDLE md, int hash_algo, unsigned nbits ); /*-- comment.c --*/ KBNODE make_comment_node( const char *s ); KBNODE make_mpi_comment_node( const char *s, MPI a ); /*-- import.c --*/ int import_keys( const char *filename, int fast ); int import_keys_stream( IOBUF inp, int fast ); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ int export_pubkeys( STRLIST users, int onlyrfc ); int export_pubkeys_stream( IOBUF out, STRLIST users, int onlyrfc ); int export_seckeys( STRLIST users ); /* dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ int gen_revoke( const char *uname ); /*-- keylist.c --*/ void public_key_list( int nnames, char **names ); void secret_key_list( int nnames, char **names ); /*-- verify.c --*/ int verify_signatures( int nfiles, char **files ); /*-- decrypt.c --*/ int decrypt_message( const char *filename ); /*-- plaintext.c --*/ int hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files, const char *sigfilename, int textmode ); /*-- signal.c --*/ void init_signals(void); void pause_on_sigusr( int which ); void block_all_signals(void); void unblock_all_signals(void); #endif /*G10_MAIN_H*/ diff --git a/g10/sig-check.c b/g10/sig-check.c index e57ae8019..4731588c6 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -1,494 +1,514 @@ /* sig-check.c - Check a signature * Copyright (C) 1998 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include "util.h" #include "packet.h" #include "memory.h" #include "mpi.h" #include "keydb.h" #include "cipher.h" #include "main.h" #include "status.h" #include "i18n.h" struct cmp_help_context_s { PKT_signature *sig; MD_HANDLE md; }; +static int do_signature_check( PKT_signature *sig, MD_HANDLE digest, + u32 *r_expire ); static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest ); /**************** * Check the signature which is contained in SIG. * The MD_HANDLE should be currently open, so that this function * is able to append some data, before finalizing the digest. */ int signature_check( PKT_signature *sig, MD_HANDLE digest ) +{ + u32 dummy; + return do_signature_check( sig, digest, &dummy ); +} + +static int +do_signature_check( PKT_signature *sig, MD_HANDLE digest, u32 *r_expire ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int rc=0; if( is_RSA(sig->pubkey_algo) ) write_status(STATUS_RSA_OR_IDEA); + *r_expire = 0; if( get_pubkey( pk, sig->keyid ) ) rc = G10ERR_NO_PUBKEY; - else + else { + *r_expire = pk->expiredate; rc = do_check( pk, sig, digest ); + } free_public_key( pk ); if( !rc && sig->sig_class < 2 && is_status_enabled() ) { /* This signature id works best with DLP algorithms because * they use a random parameter for every signature. Instead of * this sig-id we could have also used the hash of the document * and the timestamp, but the drawback of this is, that it is * not possible to sign more than one identical document within * one second. Some remote bacth processing applications might * like this feature here */ MD_HANDLE md; u32 a = sig->timestamp; int i, nsig = pubkey_get_nsig( sig->pubkey_algo ); byte *p, *buffer; md = md_open( DIGEST_ALGO_RMD160, 0); md_putc( digest, sig->pubkey_algo ); md_putc( digest, sig->digest_algo ); md_putc( digest, (a >> 24) & 0xff ); md_putc( digest, (a >> 16) & 0xff ); md_putc( digest, (a >> 8) & 0xff ); md_putc( digest, a & 0xff ); for(i=0; i < nsig; i++ ) { unsigned n = mpi_get_nbits( sig->data[i]); md_putc( md, n>>8); md_putc( md, n ); p = mpi_get_buffer( sig->data[i], &n, NULL ); md_write( md, p, n ); m_free(p); } md_final( md ); p = make_radix64_string( md_read( md, 0 ), 20 ); buffer = m_alloc( strlen(p) + 60 ); sprintf( buffer, "%s %s %lu", p, strtimestamp( sig->timestamp ), (ulong)sig->timestamp ); write_status_text( STATUS_SIG_ID, buffer ); m_free(buffer); m_free(p); md_close(md); } return rc; } #if 0 /* not anymore used */ /**************** * Check the MDC which is contained in SIG. * The MD_HANDLE should be currently open, so that this function * is able to append some data, before finalizing the digest. */ int mdc_kludge_check( PKT_signature *sig, MD_HANDLE digest ) { int rc=0; if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; /* make sure the digest algo is enabled (in case of a detached mdc??) */ md_enable( digest, sig->digest_algo ); /* complete the digest */ if( sig->version >= 4 ) md_putc( digest, sig->version ); md_putc( digest, sig->sig_class ); if( sig->version < 4 ) { u32 a = sig->timestamp; md_putc( digest, (a >> 24) & 0xff ); md_putc( digest, (a >> 16) & 0xff ); md_putc( digest, (a >> 8) & 0xff ); md_putc( digest, a & 0xff ); } else { byte buf[6]; size_t n; md_putc( digest, sig->pubkey_algo ); md_putc( digest, sig->digest_algo ); if( sig->hashed_data ) { n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; md_write( digest, sig->hashed_data, n+2 ); n += 6; } else n = 6; /* add some magic */ buf[0] = sig->version; buf[1] = 0xff; buf[2] = n >> 24; buf[3] = n >> 16; buf[4] = n >> 8; buf[5] = n; md_write( digest, buf, 6 ); } md_final( digest ); rc = G10ERR_BAD_SIGN; { const byte *s1 = md_read( digest, sig->digest_algo ); int s1len = md_digest_length( sig->digest_algo ); log_hexdump( "MDC calculated", s1, s1len ); if( !sig->data[0] ) log_debug("sig_data[0] is NULL\n"); else { unsigned s2len; byte *s2; s2 = mpi_get_buffer( sig->data[0], &s2len, NULL ); log_hexdump( "MDC stored ", s2, s2len ); if( s2len != s1len ) log_debug("MDC check: len differ: %d/%d\n", s1len, s2len); else if( memcmp( s1, s2, s1len ) ) log_debug("MDC check: hashs differ\n"); else rc = 0; m_free(s2); } } if( !rc && sig->flags.unknown_critical ) { log_info(_("assuming bad MDC due to an unknown critical bit\n")); rc = G10ERR_BAD_SIGN; } sig->flags.checked = 1; sig->flags.valid = !rc; /* FIXME: check that we are actually in an encrypted packet */ return rc; } #endif /**************** * This function gets called by pubkey_verify() if the algorithm needs it. */ static int cmp_help( void *opaque, MPI result ) { #if 0 /* we do not use this anymore */ int rc=0, i, j, c, old_enc; byte *dp; const byte *asn; size_t mdlen, asnlen; struct cmp_help_context_s *ctx = opaque; PKT_signature *sig = ctx->sig; MD_HANDLE digest = ctx->md; old_enc = 0; for(i=j=0; (c=mpi_getbyte(result, i)) != -1; i++ ) { if( !j ) { if( !i && c != 1 ) break; else if( i && c == 0xff ) ; /* skip the padding */ else if( i && !c ) j++; else break; } else if( ++j == 18 && c != 1 ) break; else if( j == 19 && c == 0 ) { old_enc++; break; } } if( old_enc ) { log_error("old encoding scheme is not supported\n"); return G10ERR_GENERAL; } if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; /* unsupported algo */ asn = md_asn_oid( sig->digest_algo, &asnlen, &mdlen ); for(i=mdlen,j=asnlen-1; (c=mpi_getbyte(result, i)) != -1 && j >= 0; i++, j-- ) if( asn[j] != c ) break; if( j != -1 || mpi_getbyte(result, i) ) return G10ERR_BAD_PUBKEY; /* ASN is wrong */ for(i++; (c=mpi_getbyte(result, i)) != -1; i++ ) if( c != 0xff ) break; i++; if( c != sig->digest_algo || mpi_getbyte(result, i) ) { /* Padding or leading bytes in signature is wrong */ return G10ERR_BAD_PUBKEY; } if( mpi_getbyte(result, mdlen-1) != sig->digest_start[0] || mpi_getbyte(result, mdlen-2) != sig->digest_start[1] ) { /* Wrong key used to check the signature */ return G10ERR_BAD_PUBKEY; } dp = md_read( digest, sig->digest_algo ); for(i=mdlen-1; i >= 0; i--, dp++ ) { if( mpi_getbyte( result, i ) != *dp ) return G10ERR_BAD_SIGN; } return 0; #else return -1; #endif } static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest ) { MPI result = NULL; int rc=0; struct cmp_help_context_s ctx; u32 cur_time; if( pk->version == 4 && pk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E ) { log_info(_("this is a PGP generated " "ElGamal key which is NOT secure for signatures!\n")); return G10ERR_PUBKEY_ALGO; } if( pk->timestamp > sig->timestamp ) { ulong d = pk->timestamp - sig->timestamp; log_info( d==1 ? _("public key is %lu second newer than the signature\n") : _("public key is %lu seconds newer than the signature\n"), d ); return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */ } cur_time = make_timestamp(); if( pk->timestamp > cur_time ) { ulong d = pk->timestamp - cur_time; log_info( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); return G10ERR_TIME_CONFLICT; } if( pk->expiredate && pk->expiredate < cur_time ) { log_info(_("NOTE: signature key expired %s\n"), asctimestamp( pk->expiredate ) ); write_status(STATUS_SIGEXPIRED); } if( (rc=check_digest_algo(sig->digest_algo)) ) return rc; if( (rc=check_pubkey_algo(sig->pubkey_algo)) ) return rc; /* make sure the digest algo is enabled (in case of a detached signature)*/ md_enable( digest, sig->digest_algo ); /* complete the digest */ if( sig->version >= 4 ) md_putc( digest, sig->version ); md_putc( digest, sig->sig_class ); if( sig->version < 4 ) { u32 a = sig->timestamp; md_putc( digest, (a >> 24) & 0xff ); md_putc( digest, (a >> 16) & 0xff ); md_putc( digest, (a >> 8) & 0xff ); md_putc( digest, a & 0xff ); } else { byte buf[6]; size_t n; md_putc( digest, sig->pubkey_algo ); md_putc( digest, sig->digest_algo ); if( sig->hashed_data ) { n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; md_write( digest, sig->hashed_data, n+2 ); n += 6; } else n = 6; /* add some magic */ buf[0] = sig->version; buf[1] = 0xff; buf[2] = n >> 24; buf[3] = n >> 16; buf[4] = n >> 8; buf[5] = n; md_write( digest, buf, 6 ); } md_final( digest ); result = encode_md_value( pk->pubkey_algo, digest, sig->digest_algo, mpi_get_nbits(pk->pkey[0])); ctx.sig = sig; ctx.md = digest; rc = pubkey_verify( pk->pubkey_algo, result, sig->data, pk->pkey, cmp_help, &ctx ); mpi_free( result ); if( !rc && sig->flags.unknown_critical ) { log_info(_("assuming bad signature due to an unknown critical bit\n")); rc = G10ERR_BAD_SIGN; } sig->flags.checked = 1; sig->flags.valid = !rc; return rc; } static void hash_uid_node( KBNODE unode, MD_HANDLE md, PKT_signature *sig ) { PKT_user_id *uid = unode->pkt->pkt.user_id; assert( unode->pkt->pkttype == PKT_USER_ID ); if( sig->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 ); } /**************** * check the signature pointed to by NODE. This is a key signature. * If the function detects a self-signature, it uses the PK from * ROOT and does not read any public key. */ int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ) +{ + u32 dummy; + return check_key_signature2(root, node, is_selfsig, &dummy ); +} + +int +check_key_signature2( KBNODE root, KBNODE node, int *is_selfsig, u32 *r_expire) { MD_HANDLE md; PKT_public_key *pk; PKT_signature *sig; int algo; int rc; if( is_selfsig ) *is_selfsig = 0; + *r_expire = 0; assert( node->pkt->pkttype == PKT_SIGNATURE ); assert( root->pkt->pkttype == PKT_PUBLIC_KEY ); pk = root->pkt->pkt.public_key; sig = node->pkt->pkt.signature; algo = sig->digest_algo; if( sig->flags.checked ) log_debug("check_key_signature: already checked: %s\n", sig->flags.valid? "good":"bad" ); if( (rc=check_digest_algo(algo)) ) return rc; if( sig->sig_class == 0x20 ) { md = md_open( algo, 0 ); hash_public_key( md, pk ); rc = do_check( pk, sig, md ); md_close(md); } else if( sig->sig_class == 0x28 ) { /* subkey revocation */ KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); if( snode ) { md = md_open( algo, 0 ); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); rc = do_check( pk, sig, md ); md_close(md); } else { log_error("no subkey for subkey revocation packet\n"); rc = G10ERR_SIG_CLASS; } } else if( sig->sig_class == 0x18 ) { KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY ); if( snode ) { if( is_selfsig ) { /* does this make sense????? */ u32 keyid[2]; /* it should always be a selfsig */ keyid_from_pk( pk, keyid ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) *is_selfsig = 1; } md = md_open( algo, 0 ); hash_public_key( md, pk ); hash_public_key( md, snode->pkt->pkt.public_key ); rc = do_check( pk, sig, md ); md_close(md); } else { log_error("no subkey for key signature packet\n"); rc = G10ERR_SIG_CLASS; } } else { KBNODE unode = find_prev_kbnode( root, node, PKT_USER_ID ); if( unode ) { u32 keyid[2]; keyid_from_pk( pk, keyid ); md = md_open( algo, 0 ); hash_public_key( md, pk ); hash_uid_node( unode, md, sig ); if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) { if( is_selfsig ) *is_selfsig = 1; rc = do_check( pk, sig, md ); } else - rc = signature_check( sig, md ); + rc = do_signature_check( sig, md, r_expire ); md_close(md); } else { log_error("no user id for key signature packet\n"); rc = G10ERR_SIG_CLASS; } } return rc; } diff --git a/g10/trustdb.c b/g10/trustdb.c index fa1e43a56..b386d7049 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,2619 +1,2636 @@ /* trustdb.c * Copyright (C) 1998, 1999 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include #include #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "tdbio.h" #include "ttyio.h" #if MAX_FINGERPRINT_LEN > 20 #error Must change structure of trustdb #endif struct keyid_list { struct keyid_list *next; u32 keyid[2]; }; struct local_id_item { struct local_id_item *next; ulong lid; unsigned flag; }; struct local_id_table { struct local_id_table *next; /* only used to keep a list of unused tables */ struct local_id_item *items[16]; }; typedef struct local_id_table *LOCAL_ID_TABLE; struct enum_cert_paths_ctx { int init; int idx; }; struct recno_list_struct { struct recno_list_struct *next; ulong recno; int type; }; typedef struct recno_list_struct *RECNO_LIST; typedef struct trust_node *TN; struct trust_node { TN back; /* parent */ TN list; /* list of other node (should all be of the same type)*/ TN next; /* used to build the list */ int is_uid; /* set if this is an uid node */ ulong lid; /* key or uid recordnumber */ union { struct { int ownertrust; int validity; /* helper */ int buckstop; } k; struct { int marginal_count; int fully_count; int validity; } u; } n; }; static TN used_tns; static int alloced_tns; static int max_alloced_tns; static LOCAL_ID_TABLE new_lid_table(void); static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ); static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ); static int propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ); static void print_user_id( FILE *fp, const char *text, u32 *keyid ); static int do_check( TRUSTREC *drec, unsigned *trustlevel, const char *nhash, int (*add_fnc)(ulong), unsigned *retflgs); static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ); /* a table used to keep track of ultimately trusted keys * which are the ones from our secrings and the trusted keys */ static LOCAL_ID_TABLE ultikey_table; /* list of unused lid items and tables */ static LOCAL_ID_TABLE unused_lid_tables; static struct local_id_item *unused_lid_items; static struct { int init; int level; char *dbname; } trustdb_args; /********************************************** *********** record read write ************** **********************************************/ /**************** * Read a record but die if it does not exist */ static void read_record( ulong recno, TRUSTREC *rec, int rectype ) { int rc = tdbio_read_record( recno, rec, rectype ); if( !rc ) return; log_error(_("trust record %lu, req type %d: read failed: %s\n"), recno, rectype, g10_errstr(rc) ); tdbio_invalid(); } /**************** * Wirte a record but die on error */ static void write_record( TRUSTREC *rec ) { int rc = tdbio_write_record( rec ); if( !rc ) return; log_error(_("trust record %lu, type %d: write failed: %s\n"), rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } /**************** * Delete a record but die on error */ static void delete_record( ulong recno ) { int rc = tdbio_delete_record( recno ); if( !rc ) return; log_error(_("trust record %lu: delete failed: %s\n"), recno, g10_errstr(rc) ); tdbio_invalid(); } /**************** * sync the db */ static void do_sync(void) { int rc = tdbio_sync(); if( !rc ) return; log_error(_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); g10_exit(2); } /********************************************** ***************** helpers ****************** **********************************************/ static LOCAL_ID_TABLE new_lid_table(void) { LOCAL_ID_TABLE a; a = unused_lid_tables; if( a ) { unused_lid_tables = a->next; memset( a, 0, sizeof *a ); } else a = m_alloc_clear( sizeof *a ); return a; } #if 0 static void release_lid_table( LOCAL_ID_TABLE tbl ) { struct local_id_item *a, *a2; int i; for(i=0; i < 16; i++ ) { for(a=tbl->items[i]; a; a = a2 ) { a2 = a->next; a->next = unused_lid_items; unused_lid_items = a; } } tbl->next = unused_lid_tables; unused_lid_tables = tbl; } #endif /**************** * Add a new item to the table or return 1 if we already have this item */ static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag ) { struct local_id_item *a; for( a = tbl->items[lid & 0x0f]; a; a = a->next ) if( a->lid == lid ) return 1; a = unused_lid_items; if( a ) unused_lid_items = a->next; else a = m_alloc( sizeof *a ); a->lid = lid; a->flag = flag; a->next = tbl->items[lid & 0x0f]; tbl->items[lid & 0x0f] = a; return 0; } static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag ) { struct local_id_item *a; for( a = tbl->items[lid & 0x0f]; a; a = a->next ) if( a->lid == lid ) { if( flag ) *flag = a->flag; return 0; } return -1; } static TN new_tn(void) { TN t; if( used_tns ) { t = used_tns; used_tns = t->next; memset( t, 0, sizeof *t ); } else t = m_alloc_clear( sizeof *t ); if( ++alloced_tns > max_alloced_tns ) max_alloced_tns = alloced_tns; return t; } static void release_tn( TN t ) { if( t ) { t->next = used_tns; used_tns = t; alloced_tns--; } } static void release_tn_tree( TN kr ) { TN kr2; for( ; kr; kr = kr2 ) { release_tn_tree( kr->list ); kr2 = kr->next; release_tn( kr ); } } /********************************************** ****** access by LID and other helpers ******* **********************************************/ /**************** * Return the keyid from the primary key identified by LID. */ int keyid_from_lid( ulong lid, u32 *keyid ) { TRUSTREC rec; int rc; init_trustdb(); keyid[0] = keyid[1] = 0; rc = tdbio_read_record( lid, &rec, 0 ); if( rc ) { log_error(_("error reading dir record for LID %lu: %s\n"), lid, g10_errstr(rc)); return G10ERR_TRUSTDB; } if( rec.rectype == RECTYPE_SDIR ) return 0; if( rec.rectype != RECTYPE_DIR ) { log_error(_("lid %lu: expected dir record, got type %d\n"), lid, rec.rectype ); return G10ERR_TRUSTDB; } if( !rec.r.dir.keylist ) { log_error(_("no primary key for LID %lu\n"), lid ); return G10ERR_TRUSTDB; } rc = tdbio_read_record( rec.r.dir.keylist, &rec, RECTYPE_KEY ); if( rc ) { log_error(_("error reading primary key for LID %lu: %s\n"), lid, g10_errstr(rc)); return G10ERR_TRUSTDB; } keyid_from_fingerprint( rec.r.key.fingerprint, rec.r.key.fingerprint_len, keyid ); return 0; } ulong lid_from_keyblock( KBNODE keyblock ) { KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); PKT_public_key *pk; if( !node ) BUG(); pk = node->pkt->pkt.public_key; if( !pk->local_id ) { TRUSTREC rec; init_trustdb(); get_dir_record( pk, &rec ); } return pk->local_id; } static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec ) { int rc=0; if( pk->local_id ) { read_record( pk->local_id, rec, RECTYPE_DIR ); } else { /* no local_id: scan the trustdb */ if( (rc=tdbio_search_dir_bypk( pk, rec )) && rc != -1 ) log_error(_("get_dir_record: search_record failed: %s\n"), g10_errstr(rc)); } return rc; } /**************** * Get the LID of a public key. * Returns: The LID of the key (note, that this may be a shadow dir) * or 0 if not available. * fixme: make this ftser by putting entries into the sdir hash table */ #if 0 static ulong lid_from_keyid( u32 *keyid ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); TRUSTREC rec; ulong lid = 0; int rc; rc = get_pubkey( pk, keyid ); if( !rc ) { if( pk->local_id ) lid = pk->local_id; else { rc = tdbio_search_dir_bypk( pk, &rec ); if( !rc ) lid = rec.recnum; else if( rc == -1 ) { /* see whether there is a sdir instead */ u32 akid[2]; keyid_from_pk( pk, akid ); rc = tdbio_search_sdir( akid, pk->pubkey_algo, &rec ); if( !rc ) lid = rec.recnum; } } } free_public_key( pk ); return lid; } #endif static ulong lid_from_keyid_no_sdir( u32 *keyid ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); TRUSTREC rec; ulong lid = 0; int rc; rc = get_pubkey( pk, keyid ); if( !rc ) { if( pk->local_id ) lid = pk->local_id; else { rc = tdbio_search_dir_bypk( pk, &rec ); if( !rc ) lid = rec.recnum; } } free_public_key( pk ); return lid; } /*********************************************** ************* Initialization **************** ***********************************************/ /**************** * Verify that all our public keys are in the trustdb. */ static int verify_own_keys(void) { int rc; void *enum_context = NULL; PKT_secret_key *sk = m_alloc_clear( sizeof *sk ); PKT_public_key *pk = m_alloc_clear( sizeof *pk ); u32 keyid[2]; while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) { int have_pk = 0; keyid_from_sk( sk, keyid ); if( DBG_TRUST ) log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] ); if( is_secret_key_protected( sk ) < 1 ) log_info(_("NOTE: secret key %08lX is NOT protected.\n"), (ulong)keyid[1] ); /* see whether we can access the public key of this secret key */ memset( pk, 0, sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_info(_("key %08lX: secret key without public key - skipped\n"), (ulong)keyid[1] ); goto skip; } have_pk=1; if( cmp_public_secret_key( pk, sk ) ) { log_info(_("key %08lX: secret and public key don't match\n"), (ulong)keyid[1] ); goto skip; } /* make sure that the pubkey is in the trustdb */ rc = query_trust_record( pk ); if( rc == -1 ) { /* put it into the trustdb */ rc = insert_trust_record_by_pk( pk ); if( rc ) { log_error(_("key %08lX: can't put it into the trustdb\n"), (ulong)keyid[1] ); goto skip; } } else if( rc ) { log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] ); goto skip; } if( DBG_TRUST ) log_debug("key %08lX.%lu: stored into ultikey_table\n", (ulong)keyid[1], pk->local_id ); if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) ) log_error(_("key %08lX: already in trusted key table\n"), (ulong)keyid[1]); else if( opt.verbose > 1 ) log_info(_("key %08lX: accepted as trusted key.\n"), (ulong)keyid[1]); skip: release_secret_key_parts( sk ); if( have_pk ) release_public_key_parts( pk ); } if( rc != -1 ) log_error(_("enumerate secret keys failed: %s\n"), g10_errstr(rc) ); else rc = 0; enum_secret_keys( &enum_context, NULL, 0 ); /* free context */ free_secret_key( sk ); free_public_key( pk ); return rc; } /**************** * Perform some checks over the trustdb * level 0: only open the db * 1: used for initial program startup */ int setup_trustdb( int level, const char *dbname ) { /* just store the args */ if( trustdb_args.init ) return 0; trustdb_args.level = level; trustdb_args.dbname = dbname? m_strdup(dbname): NULL; return 0; } void init_trustdb() { int rc=0; int level = trustdb_args.level; const char* dbname = trustdb_args.dbname; if( trustdb_args.init ) return; trustdb_args.init = 1; if( !ultikey_table ) ultikey_table = new_lid_table(); if( !level || level==1 ) { rc = tdbio_set_dbname( dbname, !!level ); if( !rc ) { if( !level ) return; /* verify that our own keys are in the trustDB * or move them to the trustdb. */ rc = verify_own_keys(); /* should we check whether there is no other ultimately trusted * key in the database? */ } } else BUG(); if( rc ) log_fatal("can't init trustdb: %s\n", g10_errstr(rc) ); } /*********************************************** ************* Print helpers **************** ***********************************************/ static void print_user_id( FILE *fp, const char *text, u32 *keyid ) { char *p; size_t n; p = get_user_id( keyid, &n ); if( fp ) { fprintf( fp, "%s \"", text ); print_string( fp, p, n, 0 ); putc('\"', fp); putc('\n', fp); } else { tty_printf( "%s \"", text ); tty_print_string( p, n ); tty_printf( "\"\n" ); } m_free(p); } /**************** * This function returns a letter for a trustvalue Trust flags * are ignore. */ int trust_letter( unsigned value ) { switch( (value & TRUST_MASK) ) { case TRUST_UNKNOWN: return '-'; case TRUST_EXPIRED: return 'e'; case TRUST_UNDEFINED: return 'q'; case TRUST_NEVER: return 'n'; case TRUST_MARGINAL: return 'm'; case TRUST_FULLY: return 'f'; case TRUST_ULTIMATE: return 'u'; default: return 0 ; } } #if 0 static void print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight ) { int rc, c, i; u32 keyid[2]; char *p; size_t n; for( i = 0; i < pathlen; i++ ) { if( highlight ) fputs(highlight == path[i].lid? "* ":" ", fp ); rc = keyid_from_lid( path[i].lid, keyid ); if( rc ) fprintf(fp, "????????.%lu:", path[i].lid ); else fprintf(fp,"%08lX.%lu:", (ulong)keyid[1], path[i].lid ); c = trust_letter(path[i].otrust); if( c ) putc( c, fp ); else fprintf( fp, "%02x", path[i].otrust ); putc('/', fp); c = trust_letter(path[i].trust); if( c ) putc( c, fp ); else fprintf( fp, "%02x", path[i].trust ); putc(' ', fp); p = get_user_id( keyid, &n ); putc(' ', fp); putc('\"', fp); print_string( fp, p, n > 40? 40:n, 0 ); putc('\"', fp); m_free(p); putc('\n', fp ); } } #endif static void print_default_uid( FILE *fp, ulong lid ) { u32 keyid[2]; if( !keyid_from_lid( lid, keyid ) ) print_user_id( fp, "", keyid ); } static void print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno ) { TRUSTREC urec; KBNODE node; byte uhash[20]; read_record( urecno, &urec, RECTYPE_UID ); for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uidpkt = node->pkt->pkt.user_id; rmd160_hash_buffer( uhash, uidpkt->name, uidpkt->len ); if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) { print_string( fp, uidpkt->name, uidpkt->len, ':' ); return; } } } fputs("[?]", fp ); } static void dump_tn_tree( FILE *fp, int level, TN tree ) { TN kr, ur; for( kr=tree; kr; kr = kr->next ) { if( fp ) { fprintf( fp, "%*s", level*4, "" ); fprintf( fp, "K%lu(ot=%d,val=%d) ", kr->lid, kr->n.k.ownertrust, kr->n.k.validity ); } else { tty_printf("%*s", level*4, "" ); tty_printf("K%lu(ot=%d,val=%d) ", kr->lid, kr->n.k.ownertrust, kr->n.k.validity ); } print_default_uid( fp, kr->lid ); for( ur=kr->list; ur; ur = ur->next ) { if( fp ) { fprintf(fp, "%*s ", level*4, "" ); fprintf(fp, "U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, ur->n.u.marginal_count, ur->n.u.fully_count, ur->n.u.validity ); } else { tty_printf("%*s ", level*4, "" ); tty_printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid, ur->n.u.marginal_count, ur->n.u.fully_count, ur->n.u.validity ); } dump_tn_tree( fp, level+1, ur->list ); } } } /**************** * Special version of dump_tn_tree, which prints it colon delimited. * Format: * level:keyid:type:recno:ot:val:mc:cc:name: * With TYPE = U for a user ID * K for a key * The RECNO is either the one of the dir record or the one of the uid record. * OT is the the usual trust letter and only availabel on K lines. * VAL is the calcualted validity * MC is the marginal trust counter and only available on U lines * CC is the same for the complete count * NAME ist the username and only printed on U lines */ static void dump_tn_tree_with_colons( int level, TN tree ) { TN kr, ur; for( kr=tree; kr; kr = kr->next ) { KBNODE kb = NULL; u32 kid[2]; keyid_from_lid( kr->lid, kid ); get_keyblock_bylid( &kb, kr->lid ); printf( "%d:%08lX%08lX:K:%lu:%c:%c::::\n", level, (ulong)kid[0], (ulong)kid[1], kr->lid, trust_letter( kr->n.k.ownertrust ), trust_letter( kr->n.k.validity ) ); for( ur=kr->list; ur; ur = ur->next ) { printf( "%d:%08lX%08lX:U:%lu::%c:%d:%d:", level, (ulong)kid[0], (ulong)kid[1], ur->lid, trust_letter( kr->n.u.validity ), ur->n.u.marginal_count, ur->n.u.fully_count ); print_uid_from_keyblock( stdout, kb, ur->lid ); putchar(':'); putchar('\n'); dump_tn_tree_with_colons( level+1, ur->list ); } release_kbnode( kb ); } } /*********************************************** ************* trustdb maintenance *********** ***********************************************/ /**************** * Create or update shadow dir record and return the LID of the record */ static ulong create_shadow_dir( PKT_signature *sig ) { TRUSTREC sdir; int rc; /* first see whether we already have such a record */ rc = tdbio_search_sdir( sig->keyid, sig->pubkey_algo, &sdir ); if( rc && rc != -1 ) { log_error(_("tdbio_search_sdir failed: %s\n"), g10_errstr(rc)); tdbio_invalid(); } if( rc == -1 ) { /* not found: create */ memset( &sdir, 0, sizeof sdir ); sdir.recnum = tdbio_new_recnum(); sdir.rectype= RECTYPE_SDIR; sdir.r.sdir.lid = sdir.recnum; sdir.r.sdir.keyid[0] = sig->keyid[0]; sdir.r.sdir.keyid[1] = sig->keyid[1]; sdir.r.sdir.pubkey_algo = sig->pubkey_algo; write_record( &sdir ); } return sdir.recnum; } static ulong find_or_create_lid( PKT_signature *sig ) { ulong lid; lid = lid_from_keyid_no_sdir( sig->keyid ); if( !lid ) lid = create_shadow_dir( sig ); return lid; } #if 0 static void upd_pref_record( TRUSTREC *urec, u32 *keyid, PKT_signature *sig ) { static struct { sigsubpkttype_t subpkttype; int preftype; } ptable[] = { { SIGSUBPKT_PREF_SYM, PREFTYPE_SYM }, { SIGSUBPKT_PREF_HASH, PREFTYPE_HASH }, { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR }, { 0, 0 } }; TRUSTREC prec; ulong lid = urec->r.uid.lid ; const byte *uidhash = urec->r.uid.namehash; const byte *s; size_t n; int k, i; ulong recno; byte prefs_sig[200]; int n_prefs_sig = 0; byte prefs_rec[200]; int n_prefs_rec = 0; if( DBG_TRUST ) log_debug("upd_pref_record for %08lX.%lu/%02X%02X\n", (ulong)keyid[1], lid, uidhash[18], uidhash[19] ); /* check for changed preferences */ for(k=0; ptable[k].subpkttype; k++ ) { s = parse_sig_subpkt2( sig, ptable[k].subpkttype, &n ); if( s ) { for( ; n; n--, s++ ) { if( n_prefs_sig >= DIM(prefs_sig)-1 ) { log_info("uid %08lX.%lu/%02X%02X: %s\n", (ulong)keyid[1], lid, uidhash[18], uidhash[19], _("Too many preferences") ); break; } prefs_sig[n_prefs_sig++] = ptable[k].preftype; prefs_sig[n_prefs_sig++] = *s; } } } for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) { read_record( recno, &prec, RECTYPE_PREF ); for(i = 0; i < ITEMS_PER_PREF_RECORD; i +=2 ) { if( n_prefs_rec >= DIM(prefs_rec)-1 ) { log_info("uid %08lX.%lu/%02X%02X: %s\n", (ulong)keyid[1], lid, uidhash[18], uidhash[19], _("Too many preference items") ); break; } if( prec.r.pref.data[i] ) { prefs_rec[n_prefs_rec++] = prec.r.pref.data[i]; prefs_rec[n_prefs_rec++] = prec.r.pref.data[i+1]; } } } if( n_prefs_sig == n_prefs_rec && !memcmp( prefs_sig, prefs_rec, n_prefs_sig ) ) return; /* not changed */ /* Preferences have changed: Delete all pref records * This is much simpler than checking whether we have to * do update the record at all - the record cache may care about it */ for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) { read_record( recno, &prec, RECTYPE_PREF ); delete_record( recno ); } if( n_prefs_sig > ITEMS_PER_PREF_RECORD ) log_info(_("WARNING: can't yet handle long pref records\n")); memset( &prec, 0, sizeof prec ); prec.recnum = tdbio_new_recnum(); prec.rectype = RECTYPE_PREF; prec.r.pref.lid = lid; if( n_prefs_sig <= ITEMS_PER_PREF_RECORD ) memcpy( prec.r.pref.data, prefs_sig, n_prefs_sig ); else { /* need more than one pref record */ TRUSTREC tmp; ulong nextrn; byte *pp = prefs_sig; n = n_prefs_sig; memcpy( prec.r.pref.data, pp, ITEMS_PER_PREF_RECORD ); n -= ITEMS_PER_PREF_RECORD; pp += ITEMS_PER_PREF_RECORD; nextrn = prec.r.pref.next = tdbio_new_recnum(); do { memset( &tmp, 0, sizeof tmp ); tmp.recnum = nextrn; tmp.rectype = RECTYPE_PREF; tmp.r.pref.lid = lid; if( n <= ITEMS_PER_PREF_RECORD ) { memcpy( tmp.r.pref.data, pp, n ); n = 0; } else { memcpy( tmp.r.pref.data, pp, ITEMS_PER_PREF_RECORD ); n -= ITEMS_PER_PREF_RECORD; pp += ITEMS_PER_PREF_RECORD; nextrn = tmp.r.pref.next = tdbio_new_recnum(); } write_record( &tmp ); } while( n ); } write_record( &prec ); urec->r.uid.prefrec = prec.recnum; urec->dirty = 1; } #endif /**************** * Check the validity of a key and calculate the keyflags * keynode points to * a node with a [sub]key. mainkid has the key ID of the primary key * keyblock is the complete keyblock which is needed for signature * checking. LID and PK is only used in verbose mode. */ static unsigned int check_keybinding( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid, PKT_public_key *pk ) { KBNODE node; int keybind_seen = 0; int revoke_seen = 0; unsigned int keyflags=0; int is_main = (keynode->pkt->pkttype == PKT_PUBLIC_KEY); int rc; if( DBG_TRUST ) log_debug("check_keybinding: %08lX.%lu\n", (ulong)mainkid[1], lid ); if( is_main ) { /* a primary key is always valid (user IDs are handled elsewhere)*/ keyflags = KEYF_CHECKED | KEYF_VALID; } for( node=keynode->next; node; node = node->next ) { PKT_signature *sig; if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) continue; /* we only care about self-signatures */ if( sig->sig_class == 0x18 && !keybind_seen && !is_main ) { /* check until we find a valid keybinding */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info(_("key %08lX.%lu: Good subkey binding\n"), (ulong)keyid_from_pk(pk,NULL), lid ); keyflags |= KEYF_CHECKED | KEYF_VALID; } else { log_info(_( "key %08lX.%lu: Invalid subkey binding: %s\n"), (ulong)keyid_from_pk(pk,NULL), lid, g10_errstr(rc) ); keyflags |= KEYF_CHECKED; keyflags &= ~KEYF_VALID; } keybind_seen = 1; } else if( sig->sig_class == 0x20 && !revoke_seen ) { /* this is a key revocation certificate: check it */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info(_("key %08lX.%lu: Valid key revocation\n"), (ulong)keyid_from_pk(pk, NULL), lid ); keyflags |= KEYF_REVOKED; /* fixme: revoke the main key too*/ } else { log_info(_( "key %08lX.%lu: Invalid key revocation: %s\n"), (ulong)keyid_from_pk(pk,NULL), lid, g10_errstr(rc) ); } revoke_seen = 1; } else if( sig->sig_class == 0x28 && !revoke_seen && !is_main ) { /* this is a subkey revocation certificate: check it */ /* fixme: we should also check that the revocation * is newer than the key (OpenPGP) */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info(_( "key %08lX.%lu: Valid subkey revocation\n"), (ulong)keyid_from_pk(pk,NULL), lid ); keyflags |= KEYF_REVOKED; } else { log_info(_( "key %08lX.%lu: Invalid subkey binding: %s\n"), (ulong)keyid_from_pk(pk,NULL), lid, g10_errstr(rc) ); } revoke_seen = 1; } /* Hmmm: should we handle direct key signatures here? */ } return keyflags; } static ulong make_key_records( KBNODE keyblock, ulong lid, u32 *keyid ) { TRUSTREC *krecs, **kend, *k, *k2; KBNODE node; PKT_public_key *pk; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; ulong keyrecno; krecs = NULL; kend = &krecs; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype != PKT_PUBLIC_KEY && node->pkt->pkttype != PKT_PUBLIC_SUBKEY ) continue; pk = node->pkt->pkt.public_key; fingerprint_from_pk( pk, fpr, &fprlen ); /* create the key record */ k = m_alloc_clear( sizeof *k ); k->rectype = RECTYPE_KEY; k->r.key.lid = lid; k->r.key.pubkey_algo = pk->pubkey_algo; k->r.key.fingerprint_len = fprlen; memcpy(k->r.key.fingerprint, fpr, fprlen ); k->recnum = tdbio_new_recnum(); *kend = k; kend = &k->next; k->r.key.keyflags = check_keybinding( keyblock, node, keyid, lid, pk ); } keyrecno = krecs? krecs->recnum : 0; /* write the keylist and release the memory */ for( k = krecs; k ; k = k2 ) { if( k->next ) k->r.key.next = k->next->recnum; write_record( k ); k2 = k->next; m_free( k ); } return keyrecno; } /**************** * Check the validity of a user ID and calculate the uidflags * keynode points to * a node with a user ID. mainkid has the key ID of the primary key * keyblock is the complete keyblock which is needed for signature * checking. */ static unsigned int check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid ) { KBNODE node; unsigned int uidflags = 0; PKT_signature *sig; PKT_signature *selfsig = NULL; /* the latest valid self signature */ int rc; - if( DBG_TRUST ) - log_debug("check_uidsigs: %08lX.%lu\n", + if( DBG_TRUST ) { + PKT_user_id *uid; + log_debug("check_uidsigs: %08lX.%lu \"", (ulong)mainkid[1], lid ); + assert(keynode->pkt->pkttype == PKT_USER_ID ); + uid = keynode->pkt->pkt.user_id; + print_string( log_stream(), uid->name, uid->len, '\"' ); + fputs("\"\n", log_stream()); + } /* first we check only the selfsignatures */ for( node=keynode->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) continue; /* we only care about self-signatures for now */ if( (sig->sig_class&~3) == 0x10 ) { /* regular self signature */ rc = check_key_signature( keyblock, node, NULL ); if( !rc ) { if( opt.verbose ) log_info( "uid %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("Good self-signature") ); uidflags |= UIDF_CHECKED | UIDF_VALID; if( !selfsig ) selfsig = sig; /* use the first valid sig */ else if( sig->timestamp > selfsig->timestamp && sig->sig_class >= selfsig->sig_class ) selfsig = sig; /* but this one is newer */ } else { log_info( "uid %08lX: %s: %s\n", (ulong)mainkid[1], _("Invalid self-signature"), g10_errstr(rc) ); uidflags |= UIDF_CHECKED; } } } /* and now check for revocations - we must do this after the * self signature check because a self-signature which is newer * than a revocation makes the revocation invalid. * Fixme: Is this correct - check with rfc2440 */ for( node=keynode->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] ) continue; /* we only care about self-signatures for now */ if( sig->sig_class == 0x30 ) { /* cert revocation */ rc = check_key_signature( keyblock, node, NULL ); if( !rc && selfsig && selfsig->timestamp > sig->timestamp ) { log_info( "uid %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("Valid user ID revocation skipped " "due to a newer self signature") ); } else if( !rc ) { if( opt.verbose ) log_info( "uid %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("Valid user ID revocation") ); uidflags |= UIDF_CHECKED | UIDF_VALID | UIDF_REVOKED; } else { log_info("uid %08lX: %s: %s\n", (ulong)mainkid[1], _("Invalid user ID revocation"), g10_errstr(rc) ); } } } return uidflags; } static unsigned int check_sig_record( KBNODE keyblock, KBNODE signode, - ulong siglid, int sigidx, u32 *keyid, ulong lid ) + ulong siglid, int sigidx, u32 *keyid, ulong lid, + u32 *r_expire ) { PKT_signature *sig = signode->pkt->pkt.signature; unsigned int sigflag = 0; TRUSTREC tmp; int revocation=0, rc; if( DBG_TRUST ) log_debug("check_sig_record: %08lX.%lu %lu[%d]\n", (ulong)keyid[1], lid, siglid, sigidx ); - + *r_expire = 0; if( (sig->sig_class&~3) == 0x10 ) /* regular certification */ ; else if( sig->sig_class == 0x30 ) /* cert revocation */ revocation = 1; else return SIGF_CHECKED | SIGF_IGNORED; read_record( siglid, &tmp, 0 ); if( tmp.rectype == RECTYPE_DIR ) { /* the public key is in the trustdb: check sig */ - rc = check_key_signature( keyblock, signode, NULL ); + rc = check_key_signature2( keyblock, signode, NULL, r_expire ); if( !rc ) { /* valid signature */ if( opt.verbose ) log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s\n", (ulong)keyid[1], lid, siglid, sigidx, (ulong)sig->keyid[1], revocation? _("Valid certificate revocation") : _("Good certificate") ); sigflag |= SIGF_CHECKED | SIGF_VALID; if( revocation ) { sigflag |= SIGF_REVOKED; /**mod_down = 1;*/ } else /**mod_up = 1*/; } else if( rc == G10ERR_NO_PUBKEY ) { /* This may happen if the key is still in the trustdb * but not available in the keystorage */ sigflag |= SIGF_NOPUBKEY; /**mod_down = 1;*/ if( revocation ) sigflag |= SIGF_REVOKED; } else { log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s: %s\n", (ulong)keyid[1], lid, siglid, sigidx, (ulong)sig->keyid[1], revocation? _("Invalid certificate revocation") : _("Invalid certificate"), g10_errstr(rc)); sigflag |= SIGF_CHECKED; if( revocation ) { sigflag |= SIGF_REVOKED; /**mod_down = 1;*/ } } } else if( tmp.rectype == RECTYPE_SDIR ) { /* better check that it is the right one */ if( tmp.r.sdir.keyid[0] == sig->keyid[0] && tmp.r.sdir.keyid[1] == sig->keyid[1] && (!tmp.r.sdir.pubkey_algo || tmp.r.sdir.pubkey_algo == sig->pubkey_algo )) sigflag |= SIGF_NOPUBKEY; else log_error(_("sig record %lu[%d] points to wrong record.\n"), siglid, sigidx ); } else { log_error(_("sig record %lu[%d] points to wrong record.\n"), siglid, sigidx ); tdbio_invalid(); } return sigflag; } /**************** * Make the sig records for the given uid record * We don't set flags here or even check the signatures; this will * happen latter. */ static ulong -make_sig_records( KBNODE keyblock, KBNODE uidnode, ulong lid, u32 *mainkid ) +make_sig_records( KBNODE keyblock, KBNODE uidnode, + ulong lid, u32 *mainkid, u32 *min_expire ) { TRUSTREC *srecs, **s_end, *s=NULL, *s2; KBNODE node; PKT_signature *sig; ulong sigrecno, siglid; int i, sigidx = 0; + u32 expire; srecs = NULL; s_end = &srecs; for( node=uidnode->next; node; node = node->next ) { if( node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) break; /* ready */ if( node->pkt->pkttype != PKT_SIGNATURE ) continue; /* don't care about other packets */ sig = node->pkt->pkt.signature; if( mainkid[0] == sig->keyid[0] && mainkid[1] == sig->keyid[1] ) continue; /* we don't care about self-signatures here */ siglid = find_or_create_lid( sig ); /* smash dups */ for( s2 = s; s2 ; s2 = s2->next ) { for(i=0; i < sigidx; i++ ) { if( s2->r.sig.sig[i].lid == siglid ) goto leaveduptest; } } for( s2 = srecs; s2 ; s2 = s2->next ) { for(i=0; i < SIGS_PER_RECORD; i++ ) { if( s2->r.sig.sig[i].lid == siglid ) goto leaveduptest; } } leaveduptest: if( s2 ) { log_info( "sig %08lX.%lu: %s\n", (ulong)mainkid[1], lid, _("duplicated certificate - deleted") ); continue; } /* create the sig record */ if( !sigidx ) { s = m_alloc_clear( sizeof *s ); s->rectype = RECTYPE_SIG; s->r.sig.lid = lid; } s->r.sig.sig[sigidx].lid = siglid; s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node, siglid, sigidx, - mainkid, lid ); + mainkid, lid, &expire ); + sigidx++; if( sigidx == SIGS_PER_RECORD ) { s->recnum = tdbio_new_recnum(); *s_end = s; s_end = &s->next; sigidx = 0; } + /* keep track of signers pk expire time */ + if( expire && (!*min_expire || *min_expire > expire ) ) + *min_expire = expire; } if( sigidx ) { s->recnum = tdbio_new_recnum(); *s_end = s; s_end = &s->next; } sigrecno = srecs? srecs->recnum : 0; /* write the keylist and release the memory */ for( s = srecs; s ; s = s2 ) { if( s->next ) s->r.sig.next = s->next->recnum; write_record( s ); s2 = s->next; m_free( s ); } return sigrecno; } static ulong -make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid ) +make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire ) { TRUSTREC *urecs, **uend, *u, *u2; KBNODE node; PKT_user_id *uid; byte uidhash[20]; ulong uidrecno; urecs = NULL; uend = &urecs; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype != PKT_USER_ID ) continue; uid = node->pkt->pkt.user_id; rmd160_hash_buffer( uidhash, uid->name, uid->len ); /* create the uid record */ u = m_alloc_clear( sizeof *u ); u->rectype = RECTYPE_UID; u->r.uid.lid = lid; memcpy(u->r.uid.namehash, uidhash, 20 ); u->recnum = tdbio_new_recnum(); *uend = u; uend = &u->next; u->r.uid.uidflags = check_uidsigs( keyblock, node, keyid, lid ); if( (u->r.uid.uidflags & UIDF_CHECKED) && (u->r.uid.uidflags & UIDF_VALID) ) /*make_pref_record( &urec, keyid, selfsig )*/; /* create the list of signatures */ - u->r.uid.siglist = make_sig_records( keyblock, node, lid, keyid ); + u->r.uid.siglist = make_sig_records( keyblock, node, + lid, keyid, min_expire ); } uidrecno = urecs? urecs->recnum : 0; /* write the uidlist and release the memory */ for( u = urecs; u ; u = u2 ) { if( u->next ) u->r.uid.next = u->next->recnum; write_record( u ); u2 = u->next; m_free( u ); } return uidrecno; } /**************** * Update all the info from the public keyblock. * The key must already exist in the keydb. */ int update_trust_record( KBNODE keyblock, int recheck, int *modified ) { PKT_public_key *primary_pk; KBNODE node; TRUSTREC drec, krec, urec, prec, helprec; int rc = 0; u32 keyid[2]; /* keyid of primary key */ - int mod_up = 0; - int mod_down = 0; +/* int mod_up = 0; + int mod_down = 0; */ ulong recno, r2; + u32 expire; if( opt.dry_run ) return 0; init_trustdb(); if( modified ) *modified = 0; node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); primary_pk = node->pkt->pkt.public_key; rc = get_dir_record( primary_pk, &drec ); if( rc ) return rc; if( !primary_pk->local_id ) primary_pk->local_id = drec.recnum; keyid_from_pk( primary_pk, keyid ); if( DBG_TRUST ) log_debug("update_trust_record: %08lX.%lu\n", (ulong)keyid[1], drec.recnum ); rc = tdbio_begin_transaction(); if( rc ) return rc; /* delete the old stuff */ for( recno=drec.r.dir.keylist; recno; recno = krec.r.key.next ) { read_record( recno, &krec, RECTYPE_KEY ); delete_record( recno ); } drec.r.dir.keylist = 0; for( recno=drec.r.dir.uidlist; recno; recno = urec.r.uid.next ) { read_record( recno, &urec, RECTYPE_UID ); for(r2=urec.r.uid.prefrec ; r2; r2 = prec.r.pref.next ) { read_record( r2, &prec, RECTYPE_PREF ); delete_record( r2 ); } for(r2=urec.r.uid.siglist ; r2; r2 = helprec.r.sig.next ) { read_record( r2, &helprec, RECTYPE_SIG ); delete_record( r2 ); } delete_record( recno ); } drec.r.dir.uidlist = 0; /* insert new stuff */ drec.r.dir.dirflags &= ~DIRF_REVOKED; drec.r.dir.keylist = make_key_records( keyblock, drec.recnum, keyid ); - drec.r.dir.uidlist = make_uid_records( keyblock, drec.recnum, keyid ); + expire = 0; + drec.r.dir.uidlist = make_uid_records( keyblock, drec.recnum, keyid, + &expire ); #if 0 if( orig_uidflags != urec.r.uid.uidflags ) { write_record( &urec ); if( !( urec.r.uid.uidflags & UIDF_VALID ) || ( urec.r.uid.uidflags & UIDF_REVOKED ) ) *mod_down=1; else *mod_up=1; /*(maybe a new user id)*/ #endif /* FIXME: if the primary key has been revoked, we should set the revoked flag in the dir records */ if( rc ) rc = tdbio_cancel_transaction(); else { if( modified && tdbio_is_dirty() ) *modified = 1; drec.r.dir.dirflags |= DIRF_CHECKED; drec.r.dir.valcheck = 0; - drec.r.dir.checkat = make_timestamp(); + drec.r.dir.checkat = expire; write_record( &drec ); /*tdbio_write_modify_stamp( mod_up, mod_down );*/ rc = tdbio_end_transaction(); } return rc; } /**************** * Insert a trust record into the TrustDB * This function assumes that the record does not yet exist. */ int insert_trust_record( KBNODE keyblock ) { TRUSTREC dirrec; TRUSTREC shadow; KBNODE node; int rc = 0; PKT_public_key *pk; if( opt.dry_run ) return 0; init_trustdb(); pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key; if( pk->local_id ) { log_debug("insert_trust_record with pk->local_id=%lu (2)\n", pk->local_id ); rc = update_trust_record( keyblock, 1, NULL ); return rc; } /* We have to look for a shadow dir record which must be reused * as the dir record. */ rc = tdbio_search_sdir( pk->keyid, pk->pubkey_algo, &shadow ); if( rc && rc != -1 ) { log_error(_("tdbio_search_dir failed: %s\n"), g10_errstr(rc)); tdbio_invalid(); } memset( &dirrec, 0, sizeof dirrec ); dirrec.rectype = RECTYPE_DIR; if( !rc ) /* we have a shadow dir record - convert to dir record */ dirrec.recnum = shadow.recnum; else dirrec.recnum = tdbio_new_recnum(); dirrec.r.dir.lid = dirrec.recnum; write_record( &dirrec ); /* put the LID into the keyblock */ pk->local_id = dirrec.r.dir.lid; for( node=keyblock; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *a_pk = node->pkt->pkt.public_key; a_pk->local_id = dirrec.r.dir.lid; } else if( node->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *a_sig = node->pkt->pkt.signature; a_sig->local_id = dirrec.r.dir.lid; } } /* mark tdb as modified upwards */ tdbio_write_modify_stamp( 1, 0 ); /* and put all the other stuff into the keydb */ rc = update_trust_record( keyblock, 1, NULL ); do_sync(); return rc; } /**************** * Insert a trust record indentified by a PK into the TrustDB */ int insert_trust_record_by_pk( PKT_public_key *pk ) { KBNODE keyblock = NULL; byte fingerprint[MAX_FINGERPRINT_LEN]; size_t fingerlen; int rc; /* get the keyblock */ fingerprint_from_pk( pk, fingerprint, &fingerlen ); rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen ); if( rc ) { /* that should never happen */ log_debug( "insert_trust_record_by_pk: keyblock not found: %s\n", g10_errstr(rc) ); } else { rc = insert_trust_record( keyblock ); if( !rc ) /* copy the LID into the PK */ pk->local_id = find_kbnode( keyblock, PKT_PUBLIC_KEY ) ->pkt->pkt.public_key->local_id; } release_kbnode( keyblock ); return rc; } /**************** * Walk over the keyrings and create trustdb records for all keys * It is intended to be used after a fast-import operation. */ void update_trustdb() { KBNODE keyblock = NULL; KBPOS kbpos; int rc; if( opt.dry_run ) return; init_trustdb(); rc = enum_keyblocks( 0, &kbpos, &keyblock ); if( !rc ) { ulong count=0, upd_count=0, err_count=0, new_count=0; while( !(rc = enum_keyblocks( 1, &kbpos, &keyblock )) ) { int modified; rc = update_trust_record( keyblock, 1, &modified ); if( rc == -1 ) { /* not yet in trustdb: insert */ PKT_public_key *pk; rc = insert_trust_record( keyblock ); pk = keyblock->pkt->pkt.public_key; if( rc && !pk->local_id ) { log_error(_("lid ?: insert failed: %s\n"), g10_errstr(rc) ); err_count++; } else if( rc ) { log_error(_("lid %lu: insert failed: %s\n"), pk->local_id, g10_errstr(rc) ); err_count++; } else { if( opt.verbose ) log_info(_("lid %lu: inserted\n"), pk->local_id ); new_count++; } } else if( rc ) { log_error(_("lid %lu: update failed: %s\n"), lid_from_keyblock(keyblock), g10_errstr(rc) ); err_count++; } else if( modified ) { if( opt.verbose ) log_info(_("lid %lu: updated\n"), lid_from_keyblock(keyblock)); upd_count++; } else if( opt.verbose > 1 ) log_info(_("lid %lu: okay\n"), lid_from_keyblock(keyblock) ); release_kbnode( keyblock ); keyblock = NULL; if( !(++count % 100) ) log_info(_("%lu keys so far processed\n"), count); } log_info(_("%lu keys processed\n"), count); if( err_count ) log_info(_("\t%lu keys with errors\n"), err_count); if( upd_count ) log_info(_("\t%lu keys updated\n"), upd_count); if( new_count ) log_info(_("\t%lu keys inserted\n"), new_count); } if( rc && rc != -1 ) log_error(_("enumerate keyblocks failed: %s\n"), g10_errstr(rc)); enum_keyblocks( 2, &kbpos, &keyblock ); /* close */ release_kbnode( keyblock ); } /**************** * Do all required check in the trustdb. This function walks over all * records in the trustdb and does scheduled processing. */ void check_trustdb( const char *username ) { TRUSTREC rec; ulong recnum; ulong count=0, upd_count=0, err_count=0, skip_count=0; ulong current_time = make_timestamp(); if( username ) log_info("given user IDs ignored in check_trustdb\n"); init_trustdb(); for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) { if( rec.rectype != RECTYPE_DIR ) continue; /* we only want the dir records */ if( count && !(count % 100) && !opt.quiet ) log_info(_("%lu keys so far processed\n"), count); count++; if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) { skip_count++; continue; /* not scheduled for checking */ } if( !rec.r.dir.keylist ) { log_info(_("lid %lu: dir record w/o key - skipped\n"), recnum); skip_count++; continue; } } log_info(_("%lu keys processed\n"), count); if( skip_count ) log_info(_("\t%lu keys skipped\n"), skip_count); if( err_count ) log_info(_("\t%lu keys with errors\n"), err_count); if( upd_count ) log_info(_("\t%lu keys updated\n"), upd_count); } /*********************************************** ********* Trust calculation ***************** ***********************************************/ /**************** * Find all certification paths of a given LID. * Limit the search to MAX_DEPTH. stack is a helper variable which * should have been allocated with size max_depth, stack[0] should * be setup to the key we are investigating, so the minimal depth * we should ever see in this function is 1. * Returns: a new tree * certchain_set must be a valid set or point to NULL; this function * may modifiy it. * * Fixme: add a fastscan mode which stops ad valid validity nodes. */ static TN build_cert_tree( ulong lid, int depth, int max_depth, TN helproot ) { TRUSTREC dirrec; TRUSTREC uidrec; ulong uidrno; TN keynode; if( depth >= max_depth ) return NULL; keynode = new_tn(); if( !helproot ) helproot = keynode; keynode->lid = lid; if( !qry_lid_table_flag( ultikey_table, lid, NULL ) ) { /* this is an ultimately trusted key; * which means that we have found the end of the chain: * We do this here prior to reading the dir record * because we don't really need the info from that record */ keynode->n.k.ownertrust = TRUST_ULTIMATE; keynode->n.k.buckstop = 1; return keynode; } read_record( lid, &dirrec, 0 ); if( dirrec.rectype != RECTYPE_DIR ) { if( dirrec.rectype != RECTYPE_SDIR ) log_debug("lid %lu, has rectype %d" " - skipped\n", lid, dirrec.rectype ); m_free(keynode); return NULL; } keynode->n.k.ownertrust = dirrec.r.dir.ownertrust & TRUST_MASK; /* loop over all user ids */ for( uidrno = dirrec.r.dir.uidlist; uidrno; uidrno = uidrec.r.uid.next ) { TRUSTREC sigrec; ulong sigrno; TN uidnode = NULL; read_record( uidrno, &uidrec, RECTYPE_UID ); if( !(uidrec.r.uid.uidflags & UIDF_CHECKED) ) continue; /* user id has not been checked */ if( !(uidrec.r.uid.uidflags & UIDF_VALID) ) continue; /* user id is not valid */ if( (uidrec.r.uid.uidflags & UIDF_REVOKED) ) continue; /* user id has been revoked */ /* loop over all signature records */ for(sigrno=uidrec.r.uid.siglist; sigrno; sigrno = sigrec.r.sig.next ) { int i; TN tn; read_record( sigrno, &sigrec, RECTYPE_SIG ); for(i=0; i < SIGS_PER_RECORD; i++ ) { if( !sigrec.r.sig.sig[i].lid ) continue; /* skip deleted sigs */ if( !(sigrec.r.sig.sig[i].flag & SIGF_CHECKED) ) continue; /* skip unchecked signatures */ if( !(sigrec.r.sig.sig[i].flag & SIGF_VALID) ) continue; /* skip invalid signatures */ if( (sigrec.r.sig.sig[i].flag & SIGF_EXPIRED) ) continue; /* skip expired signatures */ if( (sigrec.r.sig.sig[i].flag & SIGF_REVOKED) ) continue; /* skip revoked signatures */ /* check for cycles */ for( tn=keynode; tn && tn->lid != sigrec.r.sig.sig[i].lid; tn = tn->back ) ; if( tn ) continue; /* cycle found */ tn = build_cert_tree( sigrec.r.sig.sig[i].lid, depth+1, max_depth, helproot ); if( !tn ) continue; /* cert chain too deep or error */ if( !uidnode ) { uidnode = new_tn(); uidnode->back = keynode; uidnode->lid = uidrno; uidnode->is_uid = 1; uidnode->next = keynode->list; keynode->list = uidnode; } tn->back = uidnode; tn->next = uidnode->list; uidnode->list = tn; #if 0 /* optimazation - fixme: reenable this later */ if( tn->n.k.buckstop ) { /* ultimately trusted key found: * no need to check more signatures of this uid */ sigrec.r.sig.next = 0; break; } #endif } } /* end loop over sig recs */ } /* end loop over user ids */ if( !keynode->list ) { release_tn_tree( keynode ); keynode = NULL; } return keynode; } static void upd_one_ownertrust( ulong lid, unsigned new_trust, unsigned *retflgs ) { TRUSTREC rec; read_record( lid, &rec, RECTYPE_DIR ); if( DBG_TRUST ) log_debug("upd_one_ownertrust of %lu from %u to %u\n", lid, (unsigned)rec.r.dir.ownertrust, new_trust ); if( retflgs ) { if( (new_trust & TRUST_MASK) > (rec.r.dir.ownertrust & TRUST_MASK) ) *retflgs |= 16; /* modified up */ else *retflgs |= 32; /* modified down */ } /* we preserve the disabled state here */ if( (rec.r.dir.ownertrust & TRUST_FLAG_DISABLED) ) rec.r.dir.ownertrust = new_trust | TRUST_FLAG_DISABLED; else rec.r.dir.ownertrust = new_trust & ~TRUST_FLAG_DISABLED; write_record( &rec ); } /**************** * Update the ownertrust in the complete tree. */ static void propagate_ownertrust( TN kr, ulong lid, unsigned trust ) { TN ur; for( ; kr; kr = kr->next ) { if( kr->lid == lid ) kr->n.k.ownertrust = trust; for( ur=kr->list; ur; ur = ur->next ) propagate_ownertrust( ur->list, lid, trust ); } } /**************** * Calculate the validity of all keys in the tree and especially * the one of the top key. If add_fnc is not NULL, it is used to * ask for missing ownertrust values (but only if this will help * us to increase the validity. * add_fnc is expected to take the LID of the key under question * and return a ownertrust value or an error: positive values * are assumed to be the new ownertrust value; a 0 does mean no change, * a -1 is a request to cancel this validation procedure, a -2 requests * a listing of the sub-tree using the tty functions. * * * Returns: 0 = okay */ static int propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs ) { TN kr, ur; int max_validity = 0; assert( !node->is_uid ); if( node->n.k.ownertrust == TRUST_ULTIMATE ) { /* this is one of our keys */ assert( !node->list ); /* it should be a leaf */ node->n.k.validity = TRUST_ULTIMATE; if( retflgs ) *retflgs |= 1; /* found a path to an ultimately trusted key */ return 0; } /* loop over all user ids */ for( ur=node->list; ur; ur = ur->next ) { assert( ur->is_uid ); /* loop over all signators */ for(kr=ur->list; kr; kr = kr->next ) { if( propagate_validity( root, kr, add_fnc, retflgs ) ) return -1; /* quit */ if( kr->n.k.validity == TRUST_ULTIMATE ) { ur->n.u.fully_count = opt.completes_needed; } else if( kr->n.k.validity == TRUST_FULLY ) { if( add_fnc && !kr->n.k.ownertrust ) { int rc; if( retflgs ) *retflgs |= 2; /* found key with undefined ownertrust*/ do { rc = add_fnc( kr->lid ); switch( rc ) { case TRUST_NEVER: case TRUST_MARGINAL: case TRUST_FULLY: propagate_ownertrust( root, kr->lid, rc ); upd_one_ownertrust( kr->lid, rc, retflgs ); if( retflgs ) *retflgs |= 4; /* changed */ break; case -1: return -1; /* cancel */ case -2: dump_tn_tree( NULL, 0, kr ); tty_printf("\n"); break; default: break; } } while( rc == -2 ); } if( kr->n.k.ownertrust == TRUST_FULLY ) ur->n.u.fully_count++; else if( kr->n.k.ownertrust == TRUST_MARGINAL ) ur->n.u.marginal_count++; } } /* fixme: We can move this test into the loop to stop as soon as * we have a level of FULLY and return from this function * We dont do this now to get better debug output */ if( ur->n.u.fully_count >= opt.completes_needed || ur->n.u.marginal_count >= opt.marginals_needed ) ur->n.u.validity = TRUST_FULLY; else if( ur->n.u.fully_count || ur->n.u.marginal_count ) ur->n.u.validity = TRUST_MARGINAL; if( ur->n.u.validity >= max_validity ) max_validity = ur->n.u.validity; } node->n.k.validity = max_validity; return 0; } /**************** * Given the directory record of a key, check whether we can * find a path to an ultimately trusted key. We do this by * checking all key signatures up to a some depth. */ static int verify_key( int max_depth, TRUSTREC *drec, const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { TN tree; int keytrust; int pv_result; tree = build_cert_tree( drec->r.dir.lid, 0, opt.max_cert_depth, NULL ); if( !tree ) return TRUST_UNDEFINED; pv_result = propagate_validity( tree, tree, add_fnc, retflgs ); if( namehash ) { /* find the matching user id. * fixme: the way we handle this is too inefficient */ TN ur; TRUSTREC rec; keytrust = 0; for( ur=tree->list; ur; ur = ur->next ) { read_record( ur->lid, &rec, RECTYPE_UID ); if( !memcmp( namehash, rec.r.uid.namehash, 20 ) ) { keytrust = ur->n.u.validity; break; } } } else keytrust = tree->n.k.validity; /* update the cached validity values */ if( !pv_result && keytrust >= TRUST_UNDEFINED && tdbio_db_matches_options() && ( !drec->r.dir.valcheck || drec->r.dir.validity != keytrust ) ) { TN ur; TRUSTREC rec; for( ur=tree->list; ur; ur = ur->next ) { read_record( ur->lid, &rec, RECTYPE_UID ); if( rec.r.uid.validity != ur->n.u.validity ) { rec.r.uid.validity = ur->n.u.validity; write_record( &rec ); } } drec->r.dir.validity = tree->n.k.validity; drec->r.dir.valcheck = make_timestamp(); write_record( drec ); do_sync(); } release_tn_tree( tree ); return keytrust; } /**************** * we have the pubkey record and all needed informations are in the trustdb * but nothing more is known. */ static int do_check( TRUSTREC *dr, unsigned *validity, const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { if( !dr->r.dir.keylist ) { log_error(_("Ooops, no keys\n")); return G10ERR_TRUSTDB; } if( !dr->r.dir.uidlist ) { log_error(_("Ooops, no user ids\n")); return G10ERR_TRUSTDB; } if( retflgs ) *retflgs &= ~(16|32); /* reset the 2 special flags */ if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) *validity = 0; /* no need to check further */ else if( namehash ) { /* Fixme: use the cache */ *validity = verify_key( opt.max_cert_depth, dr, namehash, add_fnc, retflgs ); } else if( !add_fnc && tdbio_db_matches_options() && dr->r.dir.valcheck > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) ) && dr->r.dir.validity ) *validity = dr->r.dir.validity; else *validity = verify_key( opt.max_cert_depth, dr, NULL, add_fnc, retflgs ); if( !(*validity & TRUST_MASK) ) *validity = TRUST_UNDEFINED; if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) ) *validity |= TRUST_FLAG_DISABLED; if( dr->r.dir.dirflags & DIRF_REVOKED ) *validity |= TRUST_FLAG_REVOKED; /* If we have changed some ownertrusts, set the trustdb timestamps * and do a sync */ if( retflgs && (*retflgs & (16|32)) ) { tdbio_write_modify_stamp( (*retflgs & 16), (*retflgs & 32) ); do_sync(); } return 0; } /*********************************************** ********* Change trustdb values ************** ***********************************************/ int update_ownertrust( ulong lid, unsigned new_trust ) { TRUSTREC rec; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); if( DBG_TRUST ) log_debug("update_ownertrust of %lu from %u to %u\n", lid, (unsigned)rec.r.dir.ownertrust, new_trust ); rec.r.dir.ownertrust = new_trust; write_record( &rec ); do_sync(); return 0; } int clear_trust_checked_flag( PKT_public_key *pk ) { TRUSTREC rec; int rc; if( opt.dry_run ) return 0; init_trustdb(); rc = get_dir_record( pk, &rec ); if( rc ) return rc; /* check whether they are already reset */ if( !(rec.r.dir.dirflags & DIRF_CHECKED) && !rec.r.dir.valcheck ) return 0; /* reset the flag */ rec.r.dir.dirflags &= ~DIRF_CHECKED; rec.r.dir.valcheck = 0; write_record( &rec ); do_sync(); return 0; } /*********************************************** ********* Query trustdb values ************** ***********************************************/ /**************** * This function simply looks for the key in the trustdb * and makes sure that pk->local_id is set to the correct value. * Return: 0 = found * -1 = not found * other = error */ int query_trust_record( PKT_public_key *pk ) { TRUSTREC rec; init_trustdb(); return get_dir_record( pk, &rec ); } /**************** * Get the trustlevel for this PK. * Note: This does not ask any questions * Returns: 0 okay of an errorcode * * It operates this way: * locate the pk in the trustdb * found: * Do we have a valid cache record for it? * yes: return trustlevel from cache * no: make a cache record and all the other stuff * not found: * try to insert the pubkey into the trustdb and check again * * Problems: How do we get the complete keyblock to check that the * cache record is actually valid? Think we need a clever * cache in getkey.c to keep track of this stuff. Maybe it * is not necessary to check this if we use a local pubring. Hmmmm. */ int check_trust( PKT_public_key *pk, unsigned *r_trustlevel, const byte *namehash, int (*add_fnc)(ulong), unsigned *retflgs ) { TRUSTREC rec; unsigned trustlevel = TRUST_UNKNOWN; int rc=0; u32 cur_time; u32 keyid[2]; init_trustdb(); keyid_from_pk( pk, keyid ); /* get the pubkey record */ if( pk->local_id ) { read_record( pk->local_id, &rec, RECTYPE_DIR ); } else { /* no local_id: scan the trustdb */ if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) { log_error(_("check_trust: search dir record failed: %s\n"), g10_errstr(rc)); return rc; } else if( rc == -1 ) { /* not found - insert */ rc = insert_trust_record_by_pk( pk ); if( rc ) { log_error(_("key %08lX: insert trust record failed: %s\n"), (ulong)keyid[1], g10_errstr(rc)); goto leave; } log_info(_("key %08lX.%lu: inserted into trustdb\n"), (ulong)keyid[1], pk->local_id ); /* and re-read the dir record */ read_record( pk->local_id, &rec, RECTYPE_DIR ); } } cur_time = make_timestamp(); if( pk->timestamp > cur_time ) { log_info(_("key %08lX.%lu: created in future " "(time warp or clock problem)\n"), (ulong)keyid[1], pk->local_id ); return G10ERR_TIME_CONFLICT; } if( pk->expiredate && pk->expiredate <= cur_time ) { log_info(_("key %08lX.%lu: expired at %s\n"), (ulong)keyid[1], pk->local_id, asctimestamp( pk->expiredate) ); trustlevel = TRUST_EXPIRED; } else { rc = do_check( &rec, &trustlevel, namehash, add_fnc, retflgs ); if( rc ) { log_error(_("key %08lX.%lu: trust check failed: %s\n"), (ulong)keyid[1], pk->local_id, g10_errstr(rc)); return rc; } } /* is a subkey has been requested, we have to check its keyflags */ if( !rc ) { TRUSTREC krec; byte fpr[MAX_FINGERPRINT_LEN] = {0}; /* to avoid compiler warnings */ size_t fprlen = 0; ulong recno; int kcount=0; for( recno = rec.r.dir.keylist; recno; recno = krec.r.key.next ) { read_record( recno, &krec, RECTYPE_KEY ); if( ++kcount == 1 ) continue; /* skip the primary key */ if( kcount == 2 ) /* now we need the fingerprint */ fingerprint_from_pk( pk, fpr, &fprlen ); if( krec.r.key.fingerprint_len == fprlen && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) { /* found the subkey */ if( (krec.r.key.keyflags & KEYF_REVOKED) ) trustlevel |= TRUST_FLAG_SUB_REVOKED; /* should we check for keybinding here??? */ /* Hmmm: Maybe this whole checking stuff should not go * into the trustdb, but be done direct from the keyblock. * Chnage this all when we add an abstarction layer around * the way certificates are handled by different standards */ break; } } } leave: if( DBG_TRUST ) log_debug("check_trust() returns trustlevel %04x.\n", trustlevel); *r_trustlevel = trustlevel; return 0; } int query_trust_info( PKT_public_key *pk, const byte *namehash ) { unsigned trustlevel; int c; init_trustdb(); if( check_trust( pk, &trustlevel, namehash, NULL, NULL ) ) return '?'; if( trustlevel & TRUST_FLAG_DISABLED ) return 'd'; if( trustlevel & TRUST_FLAG_REVOKED ) return 'r'; c = trust_letter( (trustlevel & TRUST_MASK) ); if( !c ) c = '?'; return c; } /**************** * Return the assigned ownertrust value for the given LID */ unsigned get_ownertrust( ulong lid ) { TRUSTREC rec; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); return rec.r.dir.ownertrust; } int get_ownertrust_info( ulong lid ) { unsigned otrust; int c; init_trustdb(); otrust = get_ownertrust( lid ); c = trust_letter( (otrust & TRUST_MASK) ); if( !c ) c = '?'; return c; } void list_trust_path( const char *username ) { int rc; ulong lid; TRUSTREC rec; TN tree; PKT_public_key *pk = m_alloc_clear( sizeof *pk ); init_trustdb(); if( (rc = get_pubkey_byname(NULL, pk, username, NULL )) ) log_error(_("user '%s' not found: %s\n"), username, g10_errstr(rc) ); else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) log_error(_("problem finding '%s' in trustdb: %s\n"), username, g10_errstr(rc)); else if( rc == -1 ) { log_info(_("user '%s' not in trustdb - inserting\n"), username); rc = insert_trust_record_by_pk( pk ); if( rc ) log_error(_("failed to put '%s' into trustdb: %s\n"), username, g10_errstr(rc)); else { assert( pk->local_id ); } } lid = pk->local_id; tree = build_cert_tree( lid, 0, opt.max_cert_depth, NULL ); if( tree ) propagate_validity( tree, tree, NULL, NULL ); if( opt.with_colons ) dump_tn_tree_with_colons( 0, tree ); else dump_tn_tree( stdout, 0, tree ); /*printf("(alloced tns=%d max=%d)\n", alloced_tns, max_alloced_tns );*/ release_tn_tree( tree ); /*printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ), query_trust_info( pk, NULL ) ); */ free_public_key( pk ); } /**************** * Enumerate all keys, which are needed to build all trust paths for * the given key. This function does not return the key itself or * the ultimate key (the last point in cerificate chain). Only * certificate chains which ends up at an ultimately trusted key * are listed. If ownertrust or validity is not NULL, the corresponding * value for the returned LID is also returned in these variable(s). * * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function. * Set lid to the key you want to enumerate and pass it by reference. * 3) call this function as long as it does not return -1 * to indicate EOF. LID does contain the next key used to build the web * 4) Always call this function a last time with LID set to NULL, * so that it can free its context. * * Returns: -1 on EOF or the level of the returned LID */ int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ) { return -1; #if 0 struct enum_cert_paths_ctx *ctx; fixme: ..... tsl; init_trustdb(); if( !lid ) { /* release the context */ if( *context ) { FIXME: ........tsl2; ctx = *context; for(tsl = ctx->tsl_head; tsl; tsl = tsl2 ) { tsl2 = tsl->next; m_free( tsl ); } *context = NULL; } return -1; } if( !*context ) { FIXME .... *tmppath; TRUSTREC rec; if( !*lid ) return -1; ctx = m_alloc_clear( sizeof *ctx ); *context = ctx; /* collect the paths */ #if 0 read_record( *lid, &rec, RECTYPE_DIR ); tmppath = m_alloc_clear( (opt.max_cert_depth+1)* sizeof *tmppath ); tsl = NULL; collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &tsl ); m_free( tmppath ); sort_tsl_list( &tsl ); #endif /* setup the context */ ctx->tsl_head = tsl; ctx->tsl = ctx->tsl_head; ctx->idx = 0; } else ctx = *context; while( ctx->tsl && ctx->idx >= ctx->tsl->pathlen ) { ctx->tsl = ctx->tsl->next; ctx->idx = 0; } tsl = ctx->tsl; if( !tsl ) return -1; /* eof */ if( ownertrust ) *ownertrust = tsl->path[ctx->idx].otrust; if( validity ) *validity = tsl->path[ctx->idx].trust; *lid = tsl->path[ctx->idx].lid; ctx->idx++; return ctx->idx-1; #endif } /**************** * Print the current path */ void enum_cert_paths_print( void **context, FILE *fp, int refresh, ulong selected_lid ) { return; #if 0 struct enum_cert_paths_ctx *ctx; FIXME......... tsl; if( !*context ) return; init_trustdb(); ctx = *context; if( !ctx->tsl ) return; tsl = ctx->tsl; if( !fp ) fp = stderr; if( refresh ) { /* update the ownertrust and if possible the validity */ int i; int match = tdbio_db_matches_options(); for( i = 0; i < tsl->pathlen; i++ ) { TRUSTREC rec; read_record( tsl->path[i].lid, &rec, RECTYPE_DIR ); tsl->path[i].otrust = rec.r.dir.ownertrust; /* update validity only if we have it in the cache * calculation is too time consuming */ if( match && rec.r.dir.valcheck && rec.r.dir.validity ) { tsl->path[i].trust = rec.r.dir.validity; if( rec.r.dir.dirflags & DIRF_REVOKED ) tsl->path[i].trust = TRUST_FLAG_REVOKED; } } } print_path( tsl->pathlen, tsl->path, fp, selected_lid ); #endif } /* * Return an allocated buffer with the preference values for * the key with LID and the userid which is identified by the * HAMEHASH or the firstone if namehash is NULL. ret_n receives * the length of the allocated buffer. Structure of the buffer is * a repeated sequences of 2 bytes; where the first byte describes the * type of the preference and the second one the value. The constants * PREFTYPE_xxxx should be used to reference a type. */ byte * get_pref_data( ulong lid, const byte *namehash, size_t *ret_n ) { TRUSTREC rec; ulong recno; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { read_record( recno, &rec, RECTYPE_UID ); if( rec.r.uid.prefrec && ( !namehash || !memcmp(namehash, rec.r.uid.namehash, 20) )) { byte *buf; /* found the correct one or the first one */ read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); if( rec.r.pref.next ) log_info(_("WARNING: can't yet handle long pref records\n")); buf = m_alloc( ITEMS_PER_PREF_RECORD ); memcpy( buf, rec.r.pref.data, ITEMS_PER_PREF_RECORD ); *ret_n = ITEMS_PER_PREF_RECORD; return buf; } } return NULL; } /**************** * Check whether the algorithm is in one of the pref records */ int is_algo_in_prefs( ulong lid, int preftype, int algo ) { TRUSTREC rec; ulong recno; int i; byte *pref; init_trustdb(); read_record( lid, &rec, RECTYPE_DIR ); for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) { read_record( recno, &rec, RECTYPE_UID ); if( rec.r.uid.prefrec ) { read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF ); if( rec.r.pref.next ) log_info(_("WARNING: can't yet handle long pref records\n")); pref = rec.r.pref.data; for(i=0; i+1 < ITEMS_PER_PREF_RECORD; i+=2 ) { if( pref[i] == preftype && pref[i+1] == algo ) return 1; } } } return 0; } diff --git a/mpi/ChangeLog b/mpi/ChangeLog index 959f3b48c..1951dd3a4 100644 --- a/mpi/ChangeLog +++ b/mpi/ChangeLog @@ -1,158 +1,164 @@ +Fri Jul 2 11:45:54 CEST 1999 Werner Koch + + + * mpi-bit.c (mpi_lshift_limbs,mpi_rshift_limbs): New. + * mpi-mpow.c (barrett_mulm): New but diabled. + Tue Jun 1 16:01:46 CEST 1999 Werner Koch * config.links (i[56]86*-*-freebsdelf*): New. Sun May 23 14:20:22 CEST 1999 Werner Koch * config.links (sysdep.h): Not any more conditionally created. Tue May 4 15:47:53 CEST 1999 Werner Koch * mpiutil.c (mpi_alloc_like): New. Mon Apr 26 17:48:15 CEST 1999 Werner Koch * mpih-add.c, mpih-sub.c: Removed * mpi-inline.c: New. * mpi-inline.h: Make it usable by mpi-inline.c. Sun Apr 18 10:11:28 CEST 1999 Werner Koch * mpih-mul.c (mpihelp_mul_n): Fixed use of memory region. (mpihelp_mul): Ditto. Wed Apr 7 20:51:39 CEST 1999 Werner Koch * Makefile.am: Explicit rules to invoke cpp on *.S Mon Mar 8 20:47:17 CET 1999 Werner Koch * config.links: Take advantage of the with_symbol_underscore macro. Add support for freebsd 4. Wed Feb 24 11:07:27 CET 1999 Werner Koch * mips3/mpih-sub1.S: Removed left over junk in last line. (Should I blame me or my editor?). Sat Feb 13 12:04:43 CET 1999 Werner Koch * Makefile.am: Removed the +=. Add MPI_OPT_FLAGS. Sat Jan 9 16:02:23 CET 1999 Werner Koch * mpi-cmp.c (mpi_cmp_ui): Normalized the arg. Thu Jan 7 18:00:58 CET 1999 Werner Koch * mpi-bit.c (mpi_normalize): New. (mpi_get_nbits): Normalize the MPI. * mpi-bit.c (mpi_cmp): Normalize the MPI before the compare. Tue Dec 8 13:15:16 CET 1998 Werner Koch * config.links: Moved the case for powerpc*linux * powerpcp32/*.S: Removed some underscores. Thu Nov 26 07:27:52 1998 Werner Koch * config.links: Support for ppc with ELF * powerpc32/syntax.h: New. * powerpc32/*.S: Applied ELF patches (glibc patches) Tue Nov 10 19:31:37 1998 Werner Koch (wk@isil.d.shuttle.de) * power*/ : Started with stuff for PPC * config.links: Some stuff for PPC. * generic/udiv-w-sdiv.c: New but disabled. Tue Oct 27 12:37:46 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links (freebsd): Fixes for FreeBSD 3.0 Wed Oct 14 09:59:30 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links (freebsd): ELF patches from Jun Kuriyama. Thu Oct 8 13:28:17 1998 Werner Koch (wk@isil.d.shuttle.de) * mpi-mpow.c (mpi_mulpowm): Fixed mem leak (m_free/mpi_free). Thu Sep 17 18:08:50 1998 Werner Koch (wk@(none)) * hppa1.1/udiv-qrnnd.S: Fix from Steffen Zahn for HPUX 10.20 Thu Aug 6 16:39:28 1998 Werner Koch,mobil,,, (wk@tobold) * mpi-bit.c (mpi_set_bytes): Removed. Wed Aug 5 15:11:12 1998 Werner Koch (wk@(none)) * mpicoder.c (mpi_read_from_buffer): New. * mpiutil.c (mpi_set_opaque): New. (mpi_get_opaque): New. (mpi_copy): Changed to support opauqe flag (mpi_free): Ditto. Sat Jul 4 10:11:11 1998 Werner Koch (wk@isil.d.shuttle.de) * mpiutil.c (mpi_clear): Reset flags. (mpi_set): Ditto. (mpi_alloc_secure): Set flag to 1 and not ored the 1 in, tsss.. Fri Jun 26 11:19:06 1998 Werner Koch (wk@isil.d.shuttle.de) * mpiutil.c (mpi_alloc): set nbits to 0. (mpi_alloc_secure): Ditto. (mpi_clear): Ditto. Thu Jun 25 11:50:01 1998 Werner Koch (wk@isil.d.shuttle.de) * mips3/*.S: New Mon May 18 13:47:06 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links: split mpih-shift into mpih-[lr]shift and changed all implementations. * mpi/alpha: add some new assembler stuff. Wed May 13 11:04:29 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links: Add support for MIPS Thu Apr 9 11:31:36 1998 Werner Koch (wk@isil.d.shuttle.de) * mpicoder.c (mpi_get_secure_buffer): New. Wed Apr 8 09:44:33 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links: Applied small fix from Ulf Möller. Mon Apr 6 12:38:52 1998 Werner Koch (wk@isil.d.shuttle.de) * mpicoder.c (mpi_get_buffer): Removed returned leading zeroes and changed all callers. Tue Mar 10 13:40:34 1998 Werner Koch (wk@isil.d.shuttle.de) * mpi-bit.c (mpi_clear_highbit): New. Mon Mar 2 19:29:00 1998 Werner Koch (wk@isil.d.shuttle.de) * Makefile.am (DISTCLEANFILES): New Thu Feb 26 06:48:54 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links (X86_BROKEN_ALIGN): Added for some systems. Mon Feb 23 12:21:40 1998 Werner Koch (wk@isil.d.shuttle.de) * mpi/m68k/mpih-shift.S (Lspecial): Changed duplicate symbol. Mon Feb 16 13:00:27 1998 Werner Koch (wk@isil.d.shuttle.de) * config.links : Add detection of m68k cpus diff --git a/mpi/mpi-bit.c b/mpi/mpi-bit.c index 00aa5d086..f1eff8636 100644 --- a/mpi/mpi-bit.c +++ b/mpi/mpi-bit.c @@ -1,214 +1,262 @@ /* mpi-bit.c - MPI bit level fucntions * Copyright (C) 1998, 1999 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include "mpi-internal.h" #include "longlong.h" #ifdef MPI_INTERNAL_NEED_CLZ_TAB #ifdef __STDC__ const #endif unsigned char __clz_tab[] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, }; #endif #define A_LIMB_1 ((mpi_limb_t)1) /**************** * Sometimes we have MSL (most significant limbs) which are 0; * this is for some reasons not good, so this function removes them. */ void mpi_normalize( MPI a ) { if( mpi_is_protected(a) ) return; for( ; a->nlimbs && !a->d[a->nlimbs-1]; a->nlimbs-- ) ; } /**************** * Return the number of bits in A. */ unsigned mpi_get_nbits( MPI a ) { unsigned n; if( mpi_is_protected(a) ) { n = mpi_get_nbit_info(a); if( !n ) n = a->nlimbs * BITS_PER_MPI_LIMB; return n; } mpi_normalize( a ); if( a->nlimbs ) { mpi_limb_t alimb = a->d[a->nlimbs-1]; if( alimb ) count_leading_zeros( n, alimb ); else n = BITS_PER_MPI_LIMB; n = BITS_PER_MPI_LIMB - n + (a->nlimbs-1) * BITS_PER_MPI_LIMB; } else n = 0; return n; } /**************** * Test whether bit N is set. */ int mpi_test_bit( MPI a, unsigned n ) { unsigned limbno, bitno; mpi_limb_t limb; limbno = n / BITS_PER_MPI_LIMB; bitno = n % BITS_PER_MPI_LIMB; if( limbno >= a->nlimbs ) return 0; /* too far left: this is a 0 */ limb = a->d[limbno]; return (limb & (A_LIMB_1 << bitno))? 1: 0; } /**************** * Set bit N of A. */ void mpi_set_bit( MPI a, unsigned n ) { unsigned limbno, bitno; limbno = n / BITS_PER_MPI_LIMB; bitno = n % BITS_PER_MPI_LIMB; if( limbno >= a->nlimbs ) { /* resize */ if( a->alloced >= limbno ) mpi_resize(a, limbno+1 ); a->nlimbs = limbno+1; } a->d[limbno] |= (A_LIMB_1<= a->nlimbs ) { /* resize */ if( a->alloced >= limbno ) mpi_resize(a, limbno+1 ); a->nlimbs = limbno+1; } a->d[limbno] |= (A_LIMB_1<d[limbno] &= ~(A_LIMB_1 << bitno); a->nlimbs = limbno+1; } /**************** * clear bit N of A and all bits above */ void mpi_clear_highbit( MPI a, unsigned n ) { unsigned limbno, bitno; limbno = n / BITS_PER_MPI_LIMB; bitno = n % BITS_PER_MPI_LIMB; if( limbno >= a->nlimbs ) return; /* not allocated, so need to clear bits :-) */ for( ; bitno < BITS_PER_MPI_LIMB; bitno++ ) a->d[limbno] &= ~(A_LIMB_1 << bitno); a->nlimbs = limbno+1; } /**************** * Clear bit N of A. */ void mpi_clear_bit( MPI a, unsigned n ) { unsigned limbno, bitno; limbno = n / BITS_PER_MPI_LIMB; bitno = n % BITS_PER_MPI_LIMB; if( limbno >= a->nlimbs ) return; /* don't need to clear this bit, it's to far to left */ a->d[limbno] &= ~(A_LIMB_1 << bitno); } /**************** * Shift A by N bits to the right * FIXME: should use alloc_limb if X and A are same. */ void mpi_rshift( MPI x, MPI a, unsigned n ) { mpi_ptr_t xp; mpi_size_t xsize; xsize = a->nlimbs; x->sign = a->sign; RESIZE_IF_NEEDED(x, xsize); xp = x->d; if( xsize ) { mpihelp_rshift( xp, a->d, xsize, n); MPN_NORMALIZE( xp, xsize); } x->nlimbs = xsize; } + +/**************** + * Shift A by COUNT limbs to the left + * This is used only within the MPI library + */ +void +mpi_lshift_limbs( MPI a, unsigned int count ) +{ + mpi_ptr_t ap = a->d; + int n = a->nlimbs; + int i; + + if( !count || !n ) + return; + + RESIZE_IF_NEEDED( a, n+count ); + + for( i = n-1; i >= 0; i-- ) + ap[i+count] = ap[i]; + for(i=0; i < count; i++ ) + ap[i] = 0; + a->nlimbs += count; +} + + +/**************** + * Shift A by COUNT limbs to the right + * This is used only within the MPI library + */ +void +mpi_rshift_limbs( MPI a, unsigned int count ) +{ + mpi_ptr_t ap = a->d; + mpi_size_t n = a->nlimbs; + unsigned int i; + + if( count >= n ) { + a->nlimbs = 0; + return; + } + + for( i = 0; i < n - count; i++ ) + ap[i] = ap[i+count]; + ap[i] = 0; + a->nlimbs -= count; +} + + diff --git a/mpi/mpi-internal.h b/mpi/mpi-internal.h index f73efb76c..035d33cb3 100644 --- a/mpi/mpi-internal.h +++ b/mpi/mpi-internal.h @@ -1,237 +1,242 @@ /* mpi-internal.h - Internal to the Multi Precision Integers * Copyright (C) 1998 Free Software Foundation, Inc. * Copyright (C) 1994, 1996 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * Note: This code is heavily based on the GNU MP Library. * Actually it's the same code with only minor changes in the * way the data is stored; this is to support the abstraction * of an optional secure memory allocation which may be used * to avoid revealing of sensitive data due to paging etc. * The GNU MP Library itself is published under the LGPL; * however I decided to publish this code under the plain GPL. */ #ifndef G10_MPI_INTERNAL_H #define G10_MPI_INTERNAL_H #include "mpi.h" /* If KARATSUBA_THRESHOLD is not already defined, define it to a * value which is good on most machines. */ /* tested 4, 16, 32 and 64, where 16 gave the best performance when * checking a 768 and a 1024 bit ElGamal signature. * (wk 22.12.97) */ #ifndef KARATSUBA_THRESHOLD #define KARATSUBA_THRESHOLD 16 #endif /* The code can't handle KARATSUBA_THRESHOLD smaller than 2. */ #if KARATSUBA_THRESHOLD < 2 #undef KARATSUBA_THRESHOLD #define KARATSUBA_THRESHOLD 2 #endif typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */ typedef int mpi_size_t; /* (must be a signed type) */ #define ABS(x) (x >= 0 ? x : -x) #define MIN(l,o) ((l) < (o) ? (l) : (o)) #define MAX(h,i) ((h) > (i) ? (h) : (i)) #define RESIZE_IF_NEEDED(a,b) \ do { \ if( (a)->alloced < (b) ) \ mpi_resize((a), (b)); \ } while(0) /* Copy N limbs from S to D. */ #define MPN_COPY( d, s, n) \ do { \ mpi_size_t _i; \ for( _i = 0; _i < (n); _i++ ) \ (d)[_i] = (s)[_i]; \ } while(0) #define MPN_COPY_INCR( d, s, n) \ do { \ mpi_size_t _i; \ for( _i = 0; _i < (n); _i++ ) \ (d)[_i] = (d)[_i]; \ } while (0) #define MPN_COPY_DECR( d, s, n ) \ do { \ mpi_size_t _i; \ for( _i = (n)-1; _i >= 0; _i--) \ (d)[_i] = (s)[_i]; \ } while(0) /* Zero N limbs at D */ #define MPN_ZERO(d, n) \ do { \ int _i; \ for( _i = 0; _i < (n); _i++ ) \ (d)[_i] = 0; \ } while (0) #define MPN_NORMALIZE(d, n) \ do { \ while( (n) > 0 ) { \ if( (d)[(n)-1] ) \ break; \ (n)--; \ } \ } while(0) #define MPN_NORMALIZE_NOT_ZERO(d, n) \ do { \ for(;;) { \ if( (d)[(n)-1] ) \ break; \ (n)--; \ } \ } while(0) #define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \ do { \ if( (size) < KARATSUBA_THRESHOLD ) \ mul_n_basecase (prodp, up, vp, size); \ else \ mul_n (prodp, up, vp, size, tspace); \ } while (0); /* Divide the two-limb number in (NH,,NL) by D, with DI being the largest * limb not larger than (2**(2*BITS_PER_MP_LIMB))/D - (2**BITS_PER_MP_LIMB). * If this would yield overflow, DI should be the largest possible number * (i.e., only ones). For correct operation, the most significant bit of D * has to be set. Put the quotient in Q and the remainder in R. */ #define UDIV_QRNND_PREINV(q, r, nh, nl, d, di) \ do { \ mpi_limb_t _q, _ql, _r; \ mpi_limb_t _xh, _xl; \ umul_ppmm (_q, _ql, (nh), (di)); \ _q += (nh); /* DI is 2**BITS_PER_MPI_LIMB too small */ \ umul_ppmm (_xh, _xl, _q, (d)); \ sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl); \ if( _xh ) { \ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \ _q++; \ if( _xh) { \ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \ _q++; \ } \ } \ if( _r >= (d) ) { \ _r -= (d); \ _q++; \ } \ (r) = _r; \ (q) = _q; \ } while (0) /*-- mpiutil.c --*/ #ifdef M_DEBUG #define mpi_alloc_limb_space(n,f) mpi_debug_alloc_limb_space((n),(f), M_DBGINFO( __LINE__ ) ) #define mpi_free_limb_space(n) mpi_debug_free_limb_space((n), M_DBGINFO( __LINE__ ) ) mpi_ptr_t mpi_debug_alloc_limb_space( unsigned nlimbs, int sec, const char *info ); void mpi_debug_free_limb_space( mpi_ptr_t a, const char *info ); #else mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int sec ); void mpi_free_limb_space( mpi_ptr_t a ); #endif void mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs ); +/*-- mpi-bit.c --*/ +void mpi_rshift_limbs( MPI a, unsigned int count ); +void mpi_lshift_limbs( MPI a, unsigned int count ); + + /*-- mpihelp-add.c --*/ mpi_limb_t mpihelp_add_1(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb ); mpi_limb_t mpihelp_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_ptr_t s2_ptr, mpi_size_t size); mpi_limb_t mpihelp_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_ptr_t s2_ptr, mpi_size_t s2_size); /*-- mpihelp-sub.c --*/ mpi_limb_t mpihelp_sub_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb ); mpi_limb_t mpihelp_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_ptr_t s2_ptr, mpi_size_t size); mpi_limb_t mpihelp_sub(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_ptr_t s2_ptr, mpi_size_t s2_size); /*-- mpihelp-cmp.c --*/ int mpihelp_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size ); /*-- mpihelp-mul.c --*/ mpi_limb_t mpihelp_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb); mpi_limb_t mpihelp_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb); void mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size); mpi_limb_t mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize, mpi_ptr_t vp, mpi_size_t vsize); void mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size ); void mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace); /*-- mpihelp-mul_1.c (or xxx/cpu/ *.S) --*/ mpi_limb_t mpihelp_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb); /*-- mpihelp-div.c --*/ mpi_limb_t mpihelp_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size, mpi_limb_t divisor_limb); mpi_limb_t mpihelp_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs, mpi_ptr_t np, mpi_size_t nsize, mpi_ptr_t dp, mpi_size_t dsize); mpi_limb_t mpihelp_divmod_1( mpi_ptr_t quot_ptr, mpi_ptr_t dividend_ptr, mpi_size_t dividend_size, mpi_limb_t divisor_limb); /*-- mpihelp-shift.c --*/ mpi_limb_t mpihelp_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt); mpi_limb_t mpihelp_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt); /* Define stuff for longlong.h. */ #define W_TYPE_SIZE BITS_PER_MPI_LIMB typedef mpi_limb_t UWtype; typedef unsigned int UHWtype; #if defined (__GNUC__) typedef unsigned int UQItype __attribute__ ((mode (QI))); typedef int SItype __attribute__ ((mode (SI))); typedef unsigned int USItype __attribute__ ((mode (SI))); typedef int DItype __attribute__ ((mode (DI))); typedef unsigned int UDItype __attribute__ ((mode (DI))); #else typedef unsigned char UQItype; typedef long SItype; typedef unsigned long USItype; #endif #ifdef __GNUC__ #include "mpi-inline.h" #endif #endif /*G10_MPI_INTERNAL_H*/ diff --git a/mpi/mpi-mpow.c b/mpi/mpi-mpow.c index 689a7600f..a8c561dd1 100644 --- a/mpi/mpi-mpow.c +++ b/mpi/mpi-mpow.c @@ -1,119 +1,222 @@ /* mpi-mpow.c - MPI functions - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include "mpi-internal.h" #include "longlong.h" #include +/* Barrett is slower than the classical way. It can be tweaked by + * using partial multiplications + */ +/*#define USE_BARRETT*/ + + + +#ifdef USE_BARRETT +static void barrett_mulm( MPI w, MPI u, MPI v, MPI m, MPI y, int k, MPI r1, MPI r2 ); +static MPI init_barrett( MPI m, int *k, MPI *r1, MPI *r2 ); +static int calc_barrett( MPI r, MPI x, MPI m, MPI y, int k, MPI r1, MPI r2 ); +#else +#define barrett_mulm( w, u, v, m, y, k, r1, r2 ) mpi_mulm( (w), (u), (v), (m) ) +#endif + + static int build_index( MPI *exparray, int k, int i, int t ) { int j, bitno; int index = 0; bitno = t-i; for(j=k-1; j >= 0; j-- ) { index <<= 1; if( mpi_test_bit( exparray[j], bitno ) ) index |= 1; } /*log_debug("t=%d i=%d index=%d\n", t, i, index );*/ return index; } /**************** * RES = (BASE[0] ^ EXP[0]) * (BASE[1] ^ EXP[1]) * ... * mod M */ void mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI m) { int k; /* number of elements */ int t; /* bit size of largest exponent */ int i, j, idx; MPI *G; /* table with precomputed values of size 2^k */ MPI tmp; + #ifdef USE_BARRETT + MPI barrett_y, barrett_r1, barrett_r2; + int barrett_k; + #endif for(k=0; basearray[k]; k++ ) ; assert(k); for(t=0, i=0; (tmp=exparray[i]); i++ ) { /*log_mpidump("exp: ", tmp );*/ j = mpi_get_nbits(tmp); if( j > t ) t = j; } /*log_mpidump("mod: ", m );*/ assert(i==k); assert(t); assert( k < 10 ); G = m_alloc_clear( (1<= 0 && idx < (1< 3 ? k-3:0; + + mpi_normalize( x ); + if( mpi_get_nlimbs(x) > 2*k ) + return 1; /* can't do it */ + + /* 1. q1 = floor( x / b^k-1) + * q2 = q1 * y + * q3 = floor( q2 / b^k+1 ) + * Actually, we don't need qx, we can work direct on r2 + */ + mpi_set( r2, x ); + mpi_rshift_limbs( r2, k-1 ); + mpi_mul( r2, r2, y ); + mpi_rshift_limbs( r2, k+1 ); + + /* 2. r1 = x mod b^k+1 + * r2 = q3 * m mod b^k+1 + * r = r1 - r2 + * 3. if r < 0 then r = r + b^k+1 + */ + mpi_set( r1, x ); + if( r1->nlimbs > k+1 ) /* quick modulo operation */ + r1->nlimbs = k+1; + mpi_mul( r2, r2, m ); + if( r2->nlimbs > k+1 ) /* quick modulo operation */ + r2->nlimbs = k+1; + mpi_sub( r, r1, r2 ); + + if( mpi_is_neg( r ) ) { + MPI tmp; + + tmp = mpi_alloc( k + 2 ); + mpi_set_ui( tmp, 1 ); + mpi_lshift_limbs( tmp, k+1 ); + mpi_add( r, r, tmp ); + mpi_free(tmp); + } + + /* 4. while r >= m do r = r - m */ + while( mpi_cmp( r, m ) >= 0 ) + mpi_sub( r, r, m ); + + return 0; +} +#endif /* USE_BARRETT */ + diff --git a/mpi/mpi-mul.c b/mpi/mpi-mul.c index f83824926..df8eb2586 100644 --- a/mpi/mpi-mul.c +++ b/mpi/mpi-mul.c @@ -1,199 +1,198 @@ /* mpi-mul.c - MPI functions * Copyright (C) 1998 Free Software Foundation, Inc. * Copyright (C) 1994, 1996 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * Note: This code is heavily based on the GNU MP Library. * Actually it's the same code with only minor changes in the * way the data is stored; this is to support the abstraction * of an optional secure memory allocation which may be used * to avoid revealing of sensitive data due to paging etc. * The GNU MP Library itself is published under the LGPL; * however I decided to publish this code under the plain GPL. */ #include #include #include #include "mpi-internal.h" void mpi_mul_ui( MPI prod, MPI mult, unsigned long small_mult ) { mpi_size_t size, prod_size; mpi_ptr_t prod_ptr; mpi_limb_t cy; int sign; size = mult->nlimbs; sign = mult->sign; if( !size || !small_mult ) { prod->nlimbs = 0; prod->sign = 0; return; } prod_size = size + 1; if( prod->alloced < prod_size ) mpi_resize( prod, prod_size ); prod_ptr = prod->d; cy = mpihelp_mul_1( prod_ptr, mult->d, size, (mpi_limb_t)small_mult ); if( cy ) prod_ptr[size++] = cy; prod->nlimbs = size; prod->sign = sign; } void mpi_mul_2exp( MPI w, MPI u, unsigned long cnt) { mpi_size_t usize, wsize, limb_cnt; mpi_ptr_t wp; mpi_limb_t wlimb; int usign, wsign; usize = u->nlimbs; usign = u->sign; if( !usize ) { w->nlimbs = 0; w->sign = 0; return; } limb_cnt = cnt / BITS_PER_MPI_LIMB; wsize = usize + limb_cnt + 1; if( w->alloced < wsize ) mpi_resize(w, wsize ); wp = w->d; wsize = usize + limb_cnt; wsign = usign; cnt %= BITS_PER_MPI_LIMB; if( cnt ) { wlimb = mpihelp_lshift( wp + limb_cnt, u->d, usize, cnt ); if( wlimb ) { wp[wsize] = wlimb; wsize++; } } else { MPN_COPY_DECR( wp + limb_cnt, u->d, usize ); } /* Zero all whole limbs at low end. Do it here and not before calling * mpn_lshift, not to lose for U == W. */ MPN_ZERO( wp, limb_cnt ); w->nlimbs = wsize; w->sign = wsign; } void mpi_mul( MPI w, MPI u, MPI v) { mpi_size_t usize, vsize, wsize; mpi_ptr_t up, vp, wp; mpi_limb_t cy; int usign, vsign, usecure, vsecure, sign_product; int assign_wp=0; mpi_ptr_t tmp_limb=NULL; if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */ usize = v->nlimbs; usign = v->sign; usecure = mpi_is_secure(v); up = v->d; vsize = u->nlimbs; vsign = u->sign; vsecure = mpi_is_secure(u); vp = u->d; } else { usize = u->nlimbs; usign = u->sign; usecure = mpi_is_secure(u); up = u->d; vsize = v->nlimbs; vsign = v->sign; vsecure = mpi_is_secure(v); vp = v->d; } sign_product = usign ^ vsign; wp = w->d; /* Ensure W has space enough to store the result. */ wsize = usize + vsize; if( w->alloced < wsize ) { if( wp == up || wp == vp ) { wp = mpi_alloc_limb_space( wsize, mpi_is_secure(w) ); assign_wp = 1; } else { mpi_resize(w, wsize ); wp = w->d; } } else { /* Make U and V not overlap with W. */ if( wp == up ) { /* W and U are identical. Allocate temporary space for U. */ up = tmp_limb = mpi_alloc_limb_space( usize, usecure ); /* Is V identical too? Keep it identical with U. */ if( wp == vp ) vp = up; /* Copy to the temporary space. */ MPN_COPY( up, wp, usize ); } else if( wp == vp ) { /* W and V are identical. Allocate temporary space for V. */ vp = tmp_limb = mpi_alloc_limb_space( vsize, vsecure ); /* Copy to the temporary space. */ MPN_COPY( vp, wp, vsize ); } } if( !vsize ) wsize = 0; else { cy = mpihelp_mul( wp, up, usize, vp, vsize ); wsize -= cy? 0:1; } if( assign_wp ) mpi_assign_limb_space( w, wp, wsize ); w->nlimbs = wsize; w->sign = sign_product; if( tmp_limb ) mpi_free_limb_space( tmp_limb ); } - void mpi_mulm( MPI w, MPI u, MPI v, MPI m) { mpi_mul(w, u, v); mpi_fdiv_r( w, w, m ); }