diff options
Diffstat (limited to 'cipher')
-rw-r--r-- | cipher/ChangeLog | 82 | ||||
-rw-r--r-- | cipher/Makefile.am | 19 | ||||
-rw-r--r-- | cipher/blowfish.c | 9 | ||||
-rw-r--r-- | cipher/cast5.c | 10 | ||||
-rw-r--r-- | cipher/des.c | 9 | ||||
-rw-r--r-- | cipher/dsa.c | 19 | ||||
-rw-r--r-- | cipher/elgamal.c | 141 | ||||
-rw-r--r-- | cipher/md.c | 10 | ||||
-rw-r--r-- | cipher/md5.c | 8 | ||||
-rw-r--r-- | cipher/primegen.c | 25 | ||||
-rw-r--r-- | cipher/pubkey.c | 59 | ||||
-rw-r--r-- | cipher/random.c | 204 | ||||
-rw-r--r-- | cipher/rmd160.c | 8 | ||||
-rw-r--r-- | cipher/rndegd.c | 11 | ||||
-rw-r--r-- | cipher/rndunix.c | 53 | ||||
-rw-r--r-- | cipher/rndw32.c | 739 | ||||
-rw-r--r-- | cipher/rsa.c | 375 | ||||
-rw-r--r-- | cipher/rsa.h | 36 | ||||
-rw-r--r-- | cipher/sha1.c | 8 | ||||
-rw-r--r-- | cipher/tiger.c | 8 | ||||
-rw-r--r-- | cipher/twofish.c | 28 |
21 files changed, 1722 insertions, 139 deletions
diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 49595a93..877a2ad2 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -1,3 +1,85 @@ +Fri Jul 14 19:38:23 CEST 2000 Werner Koch <wk@> + + * md.c (gcry_md_ctl): Support GCRYCTL_{START,STOP}_DUMP. + + * Makefile.am: Never compile mingw32 as module. + + * Makefile.am: Tweaked module build and removed libtool + + * Makefile.am: Replaced -O1 by -O. Suggested by Alec Habig. + + * elgamal.c (sign): Removed inactive code. + + * rsa.c, rsa.h: New based on the old module version (only in CVS for now). + * pubkey.c (setup_pubkey_table): Added commented support for RSA. + + * rndunix.c (waitpid): New. For UTS 2.1. All by Dave Dykstra. + (my_popen): Do the FD_CLOEXEC only if it is available + (start_gatherer): Cope with missing _SC_OPEN_MAX + + * rndunix.c: Add some more headers for QNX. By Sam Roberts. + + * rndegd.c (gather_random): Shortcut level 0. + * rndunix.c (gather_random): Ditto. + * rndw32.c (gather_random): Ditto. + + * rndw32.c: Replaced with code from Cryptlib and commented the old stuff. + * rndw32.c: Add some debuging code enabled by an environment variable. + + * random.c (read_seed_file): Binary open for DOSish system + (update_random_seed_file): Ditto. + * random.c [MINGW32]: Include process.h for getpid. + * random.c (fast_random_poll): Add clock_gettime() as fallback for + system which support this POSIX.4 fucntion. By Sam Roberts. + + * random.c (read_seed_file): Removed the S_ISLNK test becuase it + is already covered by !S_ISREG and is not defined in Unixware. + Reported by Dave Dykstra. + (update_random_seed_file): Silently ignore update request when pool + is not filled. + + * random.c (read_seed_file): New. + (set_random_seed_file): New. + (read_pool): Try to read the seeding file. + (update_random_seed_file): New. + + (read_pool): Do an initial extra seeding when level 2 quality random + is requested the first time. This requestes at least POOLSIZE/2 bytes + of entropy. Compined with the seeding file this should make normal + random bytes cheaper and increase the quality of the random bytes + used for key generation. + + * random.c (read_pool): Print a more friendly error message in + cases when too much random is requested in one call. + + * random.c (fast_random_poll): Check whether RUSAGE_SELF is defined; + this is not the case for some ESIX and Unixware, although they have + getrusage(). + + * primegen.c (generate_elg_prime): All primes are now generated with + the lowest random quality level. Because they are public anyway we + don't need stronger random and by this we do not drain the systems + entropy so much. + + * primegen.c (register_primegen_progress): New. + * dsa.c (register_pk_dsa_progress): New. + * elgamal.c (register_pk_elg_progress): New. + + * elgamal.c (wiener_map): New. + (gen_k): Use a much smaller k. + (generate): Calculate the qbits using the wiener map and + choose an x at a size comparable to the one choosen in gen_k + + * rmd160.c (rmd160_get_info): Moved casting to the left side due to a + problem with UTS4.3. Suggested by Dave Dykstra. + * sha1.c (sha1_get_info): Ditto. + * tiger.c (tiger_get_info): Ditto. + * md5.c (md5_get_info): Ditto + * des.c (des_get_info): Ditto. + * blowfish.c (blowfish_get_info): Ditto. + * cast5.c (cast5_get_info): Ditto. + * twofish.c (twofish_get_info): Ditto. + Fri Mar 24 11:25:45 CET 2000 Werner Koch <wk@openit.de> * md.c (md_open): Add hmac arg and allocate space for the pads. diff --git a/cipher/Makefile.am b/cipher/Makefile.am index aa766bbc..9792419f 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -5,15 +5,8 @@ INCLUDES = -I$(top_srcdir)/gcrypt noinst_LTLIBRARIES = libcipher.la -# The configure script greps the module names from the following lines. -# You must also add all these names to EXTRA_PROGRAMS some lines below -# and EXTRA_foo_SOURCES entries. -# Hmmm is there a more easy way to do this? (EXTRA_PROGRAMS -# might also list programs which are not modules) -# MODULES: rndunix rndlinux rndegd rndw32 -# MODULES: sha1 rmd160 md5 tiger -EXTRA_PROGRAMS = rndunix rndlinux rndegd rndw32 \ - sha1 rmd160 md5 tiger +# The configure script greps the module names from the EXTRA_PROGRAMS line +EXTRA_PROGRAMS = rndlinux rndunix rndegd rndw32 sha1 rmd160 md5 tiger EXTRA_rndlinux_SOURCES = rndlinux.c EXTRA_rndunix_SOURCES = rndunix.c @@ -73,7 +66,7 @@ libcipher_la_LIBADD = @STATIC_CIPHER_OBJS@ tiger: $(srcdir)/tiger.c `echo $(COMPILE) $(DYNLINK_MOD_CFLAGS) -o tiger $(srcdir)/tiger.c | \ - sed -e 's/-O[2-9s]*/-O1/g' ` + sed -e 's/-O[2-9s]*/-O/g' ` tiger.o: $(srcdir)/tiger.c `echo $(COMPILE) -c $(srcdir)/tiger.c | sed -e 's/-O[2-9s]*/-O1/g' ` @@ -98,9 +91,3 @@ rndlinux: $(srcdir)/rndlinux.c rndegd: $(srcdir)/rndegd.c $(COMPILE) $(DYNLINK_MOD_CFLAGS) -o rndegd $(srcdir)/rndegd.c -# We need to have a better system for selection which modules -# to compile. For now this works. -rndw32: $(srcdir)/rndw32.c - echo "#!/bin/sh" >rndw32 - echo "Not usable as a module" >>rndw32 - diff --git a/cipher/blowfish.c b/cipher/blowfish.c index e7f047ff..2bdb3b5e 100644 --- a/cipher/blowfish.c +++ b/cipher/blowfish.c @@ -584,9 +584,12 @@ blowfish_get_info( int algo, size_t *keylen, *keylen = 128; *blocksize = BLOWFISH_BLOCKSIZE; *contextsize = sizeof(BLOWFISH_context); - *r_setkey = FNCCAST_SETKEY(bf_setkey); - *r_encrypt= FNCCAST_CRYPT(encrypt_block); - *r_decrypt= FNCCAST_CRYPT(decrypt_block); + *(int (**)(BLOWFISH_context*, byte*, unsigned))r_setkey + = bf_setkey; + *(void (**)(BLOWFISH_context*, byte*, byte*))r_encrypt + = encrypt_block; + *(void (**)(BLOWFISH_context*, byte*, byte*))r_decrypt + = decrypt_block; if( algo == CIPHER_ALGO_BLOWFISH ) return "BLOWFISH"; diff --git a/cipher/cast5.c b/cipher/cast5.c index aaa0a42e..64f6bb0e 100644 --- a/cipher/cast5.c +++ b/cipher/cast5.c @@ -610,9 +610,13 @@ cast5_get_info( int algo, size_t *keylen, *keylen = 128; *blocksize = CAST5_BLOCKSIZE; *contextsize = sizeof(CAST5_context); - *r_setkey = FNCCAST_SETKEY(cast_setkey); - *r_encrypt= FNCCAST_CRYPT(encrypt_block); - *r_decrypt= FNCCAST_CRYPT(decrypt_block); + *(int (**)(CAST5_context*, byte*, unsigned))r_setkey + = cast_setkey; + *(void (**)(CAST5_context*, byte*, byte*))r_encrypt + = encrypt_block; + *(void (**)(CAST5_context*, byte*, byte*))r_decrypt + = decrypt_block; + if( algo == CIPHER_ALGO_CAST5 ) return "CAST5"; diff --git a/cipher/des.c b/cipher/des.c index af5b5845..9c3c5419 100644 --- a/cipher/des.c +++ b/cipher/des.c @@ -1001,9 +1001,12 @@ des_get_info( int algo, size_t *keylen, *keylen = 192; *blocksize = 8; *contextsize = sizeof(struct _tripledes_ctx); - *r_setkey = FNCCAST_SETKEY(do_tripledes_setkey); - *r_encrypt= FNCCAST_CRYPT(do_tripledes_encrypt); - *r_decrypt= FNCCAST_CRYPT(do_tripledes_decrypt); + *(int (**)(struct _tripledes_ctx*, byte*, unsigned))r_setkey + = do_tripledes_setkey; + *(void (**)(struct _tripledes_ctx*, byte*, byte*))r_encrypt + = do_tripledes_encrypt; + *(void (**)(struct _tripledes_ctx*, byte*, byte*))r_decrypt + = do_tripledes_decrypt; return "3DES"; } return NULL; diff --git a/cipher/dsa.c b/cipher/dsa.c index 1f132ae0..255fa372 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -1,5 +1,5 @@ /* dsa.c - DSA signature scheme - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -52,13 +52,28 @@ 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_cb) ( void *, int ); +static void *progress_cb_data; + +void +register_pk_dsa_progress ( void (*cb)( void *, int), void *cb_data ) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + + static void progress( int c ) { - fputc( c, stderr ); + if ( progress_cb ) + progress_cb ( progress_cb_data, c ); + else + fputc( c, stderr ); } + /**************** * Generate a random secret exponent k less than q */ diff --git a/cipher/elgamal.c b/cipher/elgamal.c index 02995e02..f2c029b3 100644 --- a/cipher/elgamal.c +++ b/cipher/elgamal.c @@ -1,5 +1,5 @@ /* elgamal.c - ElGamal Public Key encryption - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * For a description of the algorithm, see: * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996. @@ -56,13 +56,67 @@ 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_cb) ( void *, int ); +static void *progress_cb_data; + +void +register_pk_elg_progress ( void (*cb)( void *, int), void *cb_data ) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + + static void progress( int c ) { - fputc( c, stderr ); + if ( progress_cb ) + progress_cb ( progress_cb_data, c ); + else + fputc( c, stderr ); } +/**************** + * Michael Wiener's table on subgroup sizes to match field sizes + * (floating around somewhere - Fixme: need a reference) + */ +static unsigned int +wiener_map( unsigned int n ) +{ + static struct { unsigned int p_n, q_n; } t[] = + { /* p q attack cost */ + { 512, 119 }, /* 9 x 10^17 */ + { 768, 145 }, /* 6 x 10^21 */ + { 1024, 165 }, /* 7 x 10^24 */ + { 1280, 183 }, /* 3 x 10^27 */ + { 1536, 198 }, /* 7 x 10^29 */ + { 1792, 212 }, /* 9 x 10^31 */ + { 2048, 225 }, /* 8 x 10^33 */ + { 2304, 237 }, /* 5 x 10^35 */ + { 2560, 249 }, /* 3 x 10^37 */ + { 2816, 259 }, /* 1 x 10^39 */ + { 3072, 269 }, /* 3 x 10^40 */ + { 3328, 279 }, /* 8 x 10^41 */ + { 3584, 288 }, /* 2 x 10^43 */ + { 3840, 296 }, /* 4 x 10^44 */ + { 4096, 305 }, /* 7 x 10^45 */ + { 4352, 313 }, /* 1 x 10^47 */ + { 4608, 320 }, /* 2 x 10^48 */ + { 4864, 328 }, /* 2 x 10^49 */ + { 5120, 335 }, /* 3 x 10^50 */ + { 0, 0 } + }; + int i; + + for(i=0; t[i].p_n; i++ ) { + if( n <= t[i].p_n ) + return t[i].q_n; + } + /* not in table - use some arbitrary high number ;-) */ + return n / 8 + 200; +} + static void test_keys( ELG_secret_key *sk, unsigned nbits ) { @@ -104,38 +158,44 @@ 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 int nbits = mpi_get_nbits(p); - unsigned int nbytes = (nbits+7)/8; + unsigned int orig_nbits = mpi_get_nbits(p); + unsigned int nbits, nbytes; char *rndbuf = NULL; + /* IMO using a k much lesser than p is sufficient and it greatly + * improves the encryption performance. We use Wiener's table + * and add a large safety margin. + */ + nbits = wiener_map( orig_nbits ) * 3 / 2; + if( nbits >= orig_nbits ) + BUG(); + + nbytes = (nbits+7)/8; if( DBG_CIPHER ) log_debug("choosing a random k "); mpi_sub_ui( p_1, p, 1); for(;;) { - if( DBG_CIPHER ) - progress('.'); if( !rndbuf || nbits < 32 ) { g10_free(rndbuf); rndbuf = gcry_random_bytes_secure( nbytes, GCRY_STRONG_RANDOM ); } else { /* change only some of the higher bits */ - /* we could imporove this by directly requesting more memory + /* we could improve this by directly requesting more memory * at the first call to get_random_bytes() and use this the here - * maybe it is easier to do this directly in random.c */ + * maybe it is easier to do this directly in random.c + * Anyway, it is highly inlikely that we will ever reach this code + */ char *pp = gcry_random_bytes_secure( 4, GCRY_STRONG_RANDOM ); memcpy( rndbuf, pp, 4 ); g10_free(pp); + log_debug("gen_k: tsss, never expected to reach this\n"); } 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 ); - } + /* Hmm, actually we don't need this step here + * because we use k much smaller than p - we do it anyway + * just in case the keep on adding a one to k ;) */ if( !(mpi_cmp( k, p_1 ) < 0) ) { /* check: k < (p-1) */ if( DBG_CIPHER ) progress('+'); @@ -149,6 +209,8 @@ gen_k( MPI p ) if( mpi_gcd( temp, k, p_1 ) ) goto found; /* okay, k is relatively prime to (p-1) */ mpi_add_ui( k, k, 1 ); + if( DBG_CIPHER ) + progress('.'); } } found: @@ -167,7 +229,7 @@ gen_k( MPI p ) * and an array with n-1 factors of (p-1) */ static void -generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) +generate( ELG_secret_key *sk, unsigned int nbits, MPI **ret_factors ) { MPI p; /* the prime */ MPI p_min1; @@ -175,19 +237,15 @@ generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) MPI x; /* the secret exponent */ MPI y; MPI temp; - unsigned qbits; + unsigned int qbits; + unsigned int xbits; 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; + qbits = wiener_map( nbits ); + if( qbits & 1 ) /* better have a even one */ + qbits++; g = mpi_alloc(1); p = generate_elg_prime( 0, nbits, qbits, g, ret_factors ); mpi_sub_ui(p_min1, p, 1); @@ -198,18 +256,26 @@ generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) * 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. + * + * I don't see a reason to have a x of about the same size + * as the p. It should be sufficient to have one about the size + * of q or the later used k plus a large safety margin. Decryption + * will be much faster with such an x. */ - x = mpi_alloc_secure( nbits/BITS_PER_MPI_LIMB ); + xbits = qbits * 3 / 2; + if( xbits >= nbits ) + BUG(); + x = mpi_alloc_secure( xbits/BITS_PER_MPI_LIMB ); if( DBG_CIPHER ) - log_debug("choosing a random x "); + log_debug("choosing a random x of size %u", xbits ); rndbuf = NULL; do { if( DBG_CIPHER ) progress('.'); if( rndbuf ) { /* change only some of the higher bits */ - if( nbits < 16 ) {/* should never happen ... */ + if( xbits < 16 ) {/* should never happen ... */ g10_free(rndbuf); - rndbuf = gcry_random_bytes_secure( (nbits+7)/8, + rndbuf = gcry_random_bytes_secure( (xbits+7)/8, GCRY_VERY_STRONG_RANDOM ); } else { @@ -220,11 +286,11 @@ generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) } } else { - rndbuf = gcry_random_bytes_secure( (nbits+7)/8, + rndbuf = gcry_random_bytes_secure( (xbits+7)/8, GCRY_VERY_STRONG_RANDOM ); } - mpi_set_buffer( x, rndbuf, (nbits+7)/8, 0 ); - mpi_clear_highbit( x, nbits+1 ); + mpi_set_buffer( x, rndbuf, (xbits+7)/8, 0 ); + mpi_clear_highbit( x, xbits+1 ); } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, p_min1 )<0 ) ); g10_free(rndbuf); @@ -311,7 +377,6 @@ 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 */ - gcry_mpi_powm( t1, a, skey->x, skey->p ); mpi_invm( t1, t1, skey->p ); mpi_mulm( output, b, t1, skey->p ); @@ -351,10 +416,6 @@ sign(MPI a, MPI b, MPI input, ELG_secret_key *skey ) gcry_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) ) { - BUG(); /* That is nonsense code - left over from a very early test?*/ - mpi_add(t, t, p_1); - } mpi_invm(inv, k, p_1 ); mpi_mulm(b, t, inv, p_1 ); @@ -557,7 +618,7 @@ elg_verify( int algo, MPI hash, MPI *data, MPI *pkey, -unsigned +unsigned int elg_get_nbits( int algo, MPI *pkey ) { if( !is_ELGAMAL(algo) ) @@ -587,10 +648,10 @@ elg_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, *nsig = 2; switch( algo ) { - case PUBKEY_ALGO_ELGAMAL: + case GCRY_PK_ELG: *use = GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR; return "ELG"; - case PUBKEY_ALGO_ELGAMAL_E: + case GCRY_PK_ELG_E: *use = GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_ENCR; return "ELG-E"; default: *use = 0; return NULL; diff --git a/cipher/md.c b/cipher/md.c index 98795206..e8ac8ac2 100644 --- a/cipher/md.c +++ b/cipher/md.c @@ -562,6 +562,12 @@ gcry_md_ctl( GCRY_MD_HD hd, int cmd, byte *buffer, size_t buflen) else if ( !(rc = prepare_macpads( hd, buffer, buflen )) ) gcry_md_reset( hd ); } + else if( cmd == GCRYCTL_START_DUMP ) { + md_start_debug( hd, buffer ); + } + else if( cmd == GCRYCTL_STOP_DUMP ) { + md_stop_debug( hd ); + } else rc = GCRYERR_INV_OP; return set_lasterr( rc ); @@ -834,7 +840,7 @@ gcry_md_algo_info( int algo, int what, void *buffer, size_t *nbytes) -void +static void md_start_debug( GCRY_MD_HD md, const char *suffix ) { static int idx=0; @@ -851,7 +857,7 @@ md_start_debug( GCRY_MD_HD md, const char *suffix ) log_debug("md debug: can't open %s\n", buf ); } -void +static void md_stop_debug( GCRY_MD_HD md ) { if( md->ctx->debug ) { diff --git a/cipher/md5.c b/cipher/md5.c index 161d4430..c4351f1f 100644 --- a/cipher/md5.c +++ b/cipher/md5.c @@ -344,10 +344,10 @@ md5_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 16; - *r_init = (void (*)(void *))md5_init; - *r_write = (void (*)(void *, byte*, size_t))md5_write; - *r_final = (void (*)(void *))md5_final; - *r_read = (byte *(*)(void *))md5_read; + *(void (**)(MD5_CONTEXT *))r_init = md5_init; + *(void (**)(MD5_CONTEXT *, byte*, size_t))r_write = md5_write; + *(void (**)(MD5_CONTEXT *))r_final = md5_final; + *(byte *(**)(MD5_CONTEXT *))r_read = md5_read; return "MD5"; } diff --git a/cipher/primegen.c b/cipher/primegen.c index 2f16b083..f5dca859 100644 --- a/cipher/primegen.c +++ b/cipher/primegen.c @@ -1,5 +1,5 @@ /* primegen.c - prime number generator - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -38,11 +38,24 @@ static int check_prime( MPI prime, MPI val_2 ); static int is_prime( MPI n, int steps, int *count ); static void m_out_of_n( char *array, int m, int n ); +static void (*progress_cb) ( void *, int ); +static void *progress_cb_data; + +void +register_primegen_progress ( void (*cb)( void *, int), void *cb_data ) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + static void progress( int c ) { - fputc( c, stderr ); + if ( progress_cb ) + progress_cb ( progress_cb_data, c ); + else + fputc( c, stderr ); } @@ -117,8 +130,8 @@ generate_elg_prime( int mode, unsigned pbits, unsigned qbits, log_debug("gen prime: pbits=%u qbits=%u fbits=%u/%u n=%d\n", pbits, req_qbits, qbits, fbits, n ); prime = mpi_alloc( (pbits + BITS_PER_MPI_LIMB - 1) / BITS_PER_MPI_LIMB ); - q = gen_prime( qbits, 0, 1 ); - q_factor = mode==1? gen_prime( req_qbits, 0, 1 ) : NULL; + q = gen_prime( qbits, 0, 0 ); + q_factor = mode==1? gen_prime( req_qbits, 0, 0 ) : NULL; /* allocate an array to hold the factors + 2 for later usage */ factors = g10_xcalloc( n+2, sizeof *factors ); @@ -177,7 +190,7 @@ generate_elg_prime( int mode, unsigned pbits, unsigned qbits, count1 = 0; qbits++; progress('>'); - q = gen_prime( qbits, 0, 1 ); + q = gen_prime( qbits, 0, 0 ); goto next_try; } } @@ -188,7 +201,7 @@ generate_elg_prime( int mode, unsigned pbits, unsigned qbits, count2 = 0; qbits--; progress('<'); - q = gen_prime( qbits, 0, 1 ); + q = gen_prime( qbits, 0, 0 ); goto next_try; } } diff --git a/cipher/pubkey.c b/cipher/pubkey.c index 3ace1d83..b139720d 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -1,5 +1,5 @@ /* pubkey.c - pubkey dispatcher - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -30,6 +30,9 @@ #include "cipher.h" #include "elgamal.h" #include "dsa.h" +#if 0 +#include "rsa.h" +#endif #include "dynload.h" /* FIXME: use set_lasterr() */ @@ -193,6 +196,60 @@ setup_pubkey_table(void) BUG(); i++; + #if 0 + pubkey_table[i].algo = PUBKEY_ALGO_RSA; + pubkey_table[i].name = rsa_get_info( pubkey_table[i].algo, + &pubkey_table[i].npkey, + &pubkey_table[i].nskey, + &pubkey_table[i].nenc, + &pubkey_table[i].nsig, + &pubkey_table[i].use ); + pubkey_table[i].generate = rsa_generate; + pubkey_table[i].check_secret_key = rsa_check_secret_key; + pubkey_table[i].encrypt = rsa_encrypt; + pubkey_table[i].decrypt = rsa_decrypt; + pubkey_table[i].sign = rsa_sign; + pubkey_table[i].verify = rsa_verify; + pubkey_table[i].get_nbits = rsa_get_nbits; + if( !pubkey_table[i].name ) + BUG(); + i++; + pubkey_table[i].algo = PUBKEY_ALGO_RSA_E; + pubkey_table[i].name = rsa_get_info( pubkey_table[i].algo, + &pubkey_table[i].npkey, + &pubkey_table[i].nskey, + &pubkey_table[i].nenc, + &pubkey_table[i].nsig, + &pubkey_table[i].use ); + pubkey_table[i].generate = rsa_generate; + pubkey_table[i].check_secret_key = rsa_check_secret_key; + pubkey_table[i].encrypt = rsa_encrypt; + pubkey_table[i].decrypt = rsa_decrypt; + pubkey_table[i].sign = dummy_sign; + pubkey_table[i].verify = dummy_verify; + pubkey_table[i].get_nbits = rsa_get_nbits; + if( !pubkey_table[i].name ) + BUG(); + i++; + pubkey_table[i].algo = PUBKEY_ALGO_RSA_S; + pubkey_table[i].name = rsa_get_info( pubkey_table[i].algo, + &pubkey_table[i].npkey, + &pubkey_table[i].nskey, + &pubkey_table[i].nenc, + &pubkey_table[i].nsig, + &pubkey_table[i].use ); + pubkey_table[i].generate = rsa_generate; + pubkey_table[i].check_secret_key = rsa_check_secret_key; + pubkey_table[i].encrypt = dummy_encrypt; + pubkey_table[i].decrypt = dummy_decrypt; + pubkey_table[i].sign = rsa_sign; + pubkey_table[i].verify = rsa_verify; + pubkey_table[i].get_nbits = rsa_get_nbits; + if( !pubkey_table[i].name ) + BUG(); + i++; + #endif + for( ; i < TABLE_SIZE; i++ ) pubkey_table[i].name = NULL; } diff --git a/cipher/random.c b/cipher/random.c index 38991a18..f8faeebf 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -1,5 +1,5 @@ /* random.c - random number generator - * Copyright (C) 1998 Free Software Foundation, Inc. + * Copyright (C) 1998, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -36,15 +36,22 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include <fcntl.h> #ifdef HAVE_GETHRTIME #include <sys/times.h> #endif #ifdef HAVE_GETTIMEOFDAY #include <sys/times.h> #endif +#ifdef HAVE_CLOCK_GETTIME + #include <time.h> +#endif #ifdef HAVE_GETRUSAGE #include <sys/resource.h> #endif +#ifdef __MINGW32__ + #include <process.h> +#endif #include "g10lib.h" #include "rmd.h" #include "random.h" @@ -89,6 +96,9 @@ static size_t pool_writepos; static int pool_filled; static int pool_balance; static int just_mixed; +static int did_initial_extra_seeding; +static char *seed_file_name; +static int allow_seed_file_update; static int secure_alloc; static int quick_test; @@ -274,6 +284,139 @@ mix_pool(byte *pool) } } +void +set_random_seed_file( const char *name ) +{ + if( seed_file_name ) + BUG(); + seed_file_name = g10_xstrdup( name ); +} + +/**************** + * Read in a seed form the random_seed file + * and return true if this was successful + */ +static int +read_seed_file() +{ + int fd; + struct stat sb; + unsigned char buffer[POOLSIZE]; + int n; + + if( !seed_file_name ) + return 0; + + #ifdef HAVE_DOSISH_SYSTEM + fd = open( seed_file_name, O_RDONLY | O_BINARY ); + #else + fd = open( seed_file_name, O_RDONLY ); + #endif + if( fd == -1 && errno == ENOENT) { + allow_seed_file_update = 1; + return 0; + } + + if( fd == -1 ) { + log_info(_("can't open `%s': %s\n"), seed_file_name, strerror(errno) ); + return 0; + } + if( fstat( fd, &sb ) ) { + log_info(_("can't stat `%s': %s\n"), seed_file_name, strerror(errno) ); + close(fd); + return 0; + } + if( !S_ISREG(sb.st_mode) ) { + log_info(_("`%s' is not a regular file - ignored\n"), seed_file_name ); + close(fd); + return 0; + } + if( !sb.st_size ) { + log_info(_("note: random_seed file is empty\n") ); + close(fd); + allow_seed_file_update = 1; + return 0; + } + if( sb.st_size != POOLSIZE ) { + log_info(_("warning: invalid size of random_seed file - not used\n") ); + close(fd); + return 0; + } + do { + n = read( fd, buffer, POOLSIZE ); + } while( n == -1 && errno == EINTR ); + if( n != POOLSIZE ) { + log_fatal(_("can't read `%s': %s\n"), seed_file_name,strerror(errno) ); + close(fd); + return 0; + } + + close(fd); + + add_randomness( buffer, POOLSIZE, 0 ); + /* add some minor entropy to the pool now (this will also force a mixing) */ + { pid_t x = getpid(); + add_randomness( &x, sizeof(x), 0 ); + } + { time_t x = time(NULL); + add_randomness( &x, sizeof(x), 0 ); + } + { clock_t x = clock(); + add_randomness( &x, sizeof(x), 0 ); + } + /* And read a few bytes from our entropy source. By using + * a level of 0 this will not block and might not return anything + * with some entropy drivers, however the rndlinux driver will use + * /dev/urandom and return some stuff - Do not read to much as we + * want to be friendly to the scare system entropy resource. */ + read_random_source( 0, 16, 0 ); + + allow_seed_file_update = 1; + return 1; +} + +void +update_random_seed_file() +{ + ulong *sp, *dp; + int fd, i; + + if( !seed_file_name || !is_initialized || !pool_filled ) + return; + if( !allow_seed_file_update ) { + log_info(_("note: random_seed file not updated\n")); + return; + } + + + /* copy the entropy pool to a scratch pool and mix both of them */ + for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; + i < POOLWORDS; i++, dp++, sp++ ) { + *dp = *sp + ADD_VALUE; + } + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; + + #ifdef HAVE_DOSISH_SYSTEM + fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, + S_IRUSR|S_IWUSR ); + #else + fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR ); + #endif + if( fd == -1 ) { + log_info(_("can't create `%s': %s\n"), seed_file_name, strerror(errno) ); + return; + } + do { + i = write( fd, keypool, POOLSIZE ); + } while( i == -1 && errno == EINTR ); + if( i != POOLSIZE ) { + log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno) ); + } + if( close(fd) ) + log_info(_("can't close `%s': %s\n"), seed_file_name, strerror(errno) ); +} + static void read_pool( byte *buffer, size_t length, int level ) @@ -281,8 +424,31 @@ read_pool( byte *buffer, size_t length, int level ) int i; ulong *sp, *dp; - if( length >= POOLSIZE ) - BUG(); /* not allowed */ + if( length >= POOLSIZE ) { + log_fatal(_("too many random bits requested; the limit is %d\n"), + POOLSIZE*8-1 ); + } + + if( !pool_filled ) { + if( read_seed_file() ) + pool_filled = 1; + } + + /* For level 2 quality (key generation) we alwas make + * sure that the pool has been seeded enough initially */ + if( level == 2 && !did_initial_extra_seeding ) { + size_t needed; + + pool_balance = 0; + needed = length - pool_balance; + if( needed < POOLSIZE/2 ) + needed = POOLSIZE/2; + else if( needed > POOLSIZE ) + BUG(); + read_random_source( 3, needed, 2 ); + pool_balance += needed; + did_initial_extra_seeding=1; + } /* for level 2 make sure that there is enough random in the pool */ if( level == 2 && pool_balance < length ) { @@ -347,6 +513,12 @@ read_pool( byte *buffer, size_t length, int level ) /**************** * Add LENGTH bytes of randomness from buffer to the pool. * source may be used to specify the randomness source. + * Source is: + * 0 - used ony for initialization + * 1 - fast random poll function + * 2 - normal poll function + * 3 - used when level 2 random quality has been requested + * to do an extra pool seed. */ static void add_randomness( const void *buffer, size_t length, int source ) @@ -410,6 +582,13 @@ fast_random_poll() add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 ); } + #elif HAVE_CLOCK_GETTIME + { struct timespec tv; + if( clock_gettime( CLOCK_REALTIME, &tv ) == -1 ) + BUG(); + add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); + add_randomness( &tv.tv_nsec, sizeof(tv.tv_nsec), 1 ); + } #else /* use times */ #ifndef HAVE_DOSISH_SYSTEM { struct tms buf; @@ -419,13 +598,28 @@ fast_random_poll() #endif #endif #ifdef HAVE_GETRUSAGE + #ifndef RUSAGE_SELF + #ifdef __GCC__ + #warning There is no RUSAGE_SELF on this system + #endif + #else { struct rusage buf; if( getrusage( RUSAGE_SELF, &buf ) ) BUG(); add_randomness( &buf, sizeof buf, 1 ); memset( &buf, 0, sizeof buf ); } + #endif #endif + /* time and clock are availabe on all systems - so + * we better do it just in case one of the above functions + * didn't work */ + { time_t x = time(NULL); + add_randomness( &x, sizeof(x), 1 ); + } + { clock_t x = clock(); + add_randomness( &x, sizeof(x), 1 ); + } } @@ -472,9 +666,9 @@ gather_faked( void (*add)(const void*, size_t, int), int requester, #endif initialized=1; #ifdef HAVE_RAND - srand( time(NULL) * getpid()); + srand(make_timestamp()*getpid()); #else - srandom( time(NULL) * getpid()); + srandom(make_timestamp()*getpid()); #endif } diff --git a/cipher/rmd160.c b/cipher/rmd160.c index 7b230087..fb9d6fa5 100644 --- a/cipher/rmd160.c +++ b/cipher/rmd160.c @@ -562,10 +562,10 @@ rmd160_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 20; - *r_init = (void (*)(void *))rmd160_init; - *r_write = (void (*)(void *, byte*, size_t))rmd160_write; - *r_final = (void (*)(void *))rmd160_final; - *r_read = (byte *(*)(void *))rmd160_read; + *(void (**)(RMD160_CONTEXT *))r_init = rmd160_init; + *(void (**)(RMD160_CONTEXT *, byte*, size_t))r_write = rmd160_write; + *(void (**)(RMD160_CONTEXT *))r_final = rmd160_final; + *(byte *(**)(RMD160_CONTEXT *))r_read = rmd160_read; return "RIPEMD160"; } diff --git a/cipher/rndegd.c b/cipher/rndegd.c index 4d5f0ef3..f6870cdd 100644 --- a/cipher/rndegd.c +++ b/cipher/rndegd.c @@ -1,5 +1,5 @@ /* rndegd.c - interface to the EGD - * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -117,9 +117,13 @@ do_read( int fd, void *buf, size_t nbytes ) -/* Note: we always use the highest level. +/**************** + * Note: we always use the highest level. * TO boost the performance we may want to add some * additional code for level 1 + * + * Using a level of 0 should never block and better add nothing + * to the pool. So this is just a dummy for EGD. */ static int gather_random( void (*add)(const void*, size_t, int), int requester, @@ -133,7 +137,8 @@ gather_random( void (*add)(const void*, size_t, int), int requester, if( !length ) return 0; - + if( !level ) + return 0; restart: if( do_restart ) { diff --git a/cipher/rndunix.c b/cipher/rndunix.c index 4ab9f65f..99a416ea 100644 --- a/cipher/rndunix.c +++ b/cipher/rndunix.c @@ -77,7 +77,7 @@ #ifndef __QNX__ #include <sys/resource.h> #endif /* __QNX__ */ -#ifdef _AIX +#if defined( _AIX ) || defined( __QNX__ ) #include <sys/select.h> #endif /* _AIX */ #ifndef __QNX__ @@ -91,6 +91,10 @@ #endif /* __hpux 9.x, after that it's in unistd.h */ #include <sys/wait.h> /* #include <kitchensink.h> */ +#ifdef __QNX__ +#include <signal.h> +#include <process.h> +#endif /* __QNX__ */ #include <errno.h> #include "types.h" /* for byte and u32 typedefs */ @@ -100,7 +104,13 @@ #include "g10lib.h" #ifndef EAGAIN - #define EAGAIN EWOULDBLOCK +#define EAGAIN EWOULDBLOCK +#endif +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 #endif #define GATHER_BUFSIZE 49152 /* Usually about 25K are filled */ @@ -306,6 +316,32 @@ typedef struct { char data[500]; /* gathered data */ } GATHER_MSG; +#ifndef HAVE_WAITPID +pid_t +waitpid(pid_t pid, int *statptr, int options) +{ + #ifdef HAVE_WAIT4 + return wait4(pid, statptr, options, NULL); + #else + /* If wait4 is also not available, try wait3 for SVR3 variants */ + /* Less ideal because can't actually request a specific pid */ + /* For that reason, first check to see if pid is for an */ + /* existing process. */ + int tmp_pid, dummystat;; + if (kill(pid, 0) == -1) { + errno = ECHILD; + return -1; + } + if (statptr == NULL) + statptr = &dummystat; + while (((tmp_pid = wait3(statptr, options, 0)) != pid) && + (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) + ; + return tmp_pid; + #endif +} +#endif + /* Under SunOS popen() doesn't record the pid of the child process. When * pclose() is called, instead of calling waitpid() for the correct child, it * calls wait() repeatedly until the right child is reaped. The problem is @@ -376,7 +412,9 @@ my_popen(struct RI *entry) * close on exec, so new children won't see it */ close(pipedes[STDOUT_FILENO]); +#ifdef FD_CLOEXEC fcntl(pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC); +#endif stream = fdopen(pipedes[STDIN_FILENO], "r"); @@ -616,6 +654,7 @@ start_gatherer( int pipefd ) } /* close all files but the ones we need */ { int nmax, n1, n2, i; + #ifdef _SC_OPEN_MAX if( (nmax=sysconf( _SC_OPEN_MAX )) < 0 ) { #ifdef _POSIX_OPEN_MAX nmax = _POSIX_OPEN_MAX; @@ -623,6 +662,9 @@ start_gatherer( int pipefd ) nmax = 20; /* assume a reasonable value */ #endif } + #else + nmax = 20; /* assume a reasonable value */ + #endif n1 = fileno( stderr ); n2 = dbgfp? fileno( dbgfp ) : -1; for(i=0; i < nmax; i++ ) { @@ -718,6 +760,10 @@ read_a_msg( int fd, GATHER_MSG *msg ) } +/**************** + * Using a level of 0 should never block and better add nothing + * to the pool. So this is just a dummy for this gatherer. + */ static int gather_random( void (*add)(const void*, size_t, int), int requester, size_t length, int level ) @@ -727,6 +773,9 @@ gather_random( void (*add)(const void*, size_t, int), int requester, GATHER_MSG msg; size_t n; + if( !level ) + return 0; + if( !gatherer_pid ) { /* make sure we are not setuid */ if( getuid() != geteuid() ) diff --git a/cipher/rndw32.c b/cipher/rndw32.c index c1045851..25830693 100644 --- a/cipher/rndw32.c +++ b/cipher/rndw32.c @@ -1,5 +1,6 @@ -/* rndw32.c - interface to the Winseed DLL - * Copyright (C) 1999 Free Software Foundation, Inc. +/* rndw32.c - W32 entropy gatherer + * Copyright (C) 1999, 2000 Free Software Foundation, Inc. + * Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999 * * This file is part of GnuPG. * @@ -16,6 +17,46 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + ************************************************************************* + * The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann. + * Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this + * copyright notice: + * + * This module is part of the cryptlib continuously seeded pseudorandom + * number generator. For usage conditions, see lib_rand.c + * + * [Here is the notice from lib_rand.c, which is now called dev_sys.c] + * + * This module and the misc/rnd*.c modules represent the cryptlib + * continuously seeded pseudorandom number generator (CSPRNG) as described in + * my 1998 Usenix Security Symposium paper "The generation of random numbers + * for cryptographic purposes". + * + * The CSPRNG code is copyright Peter Gutmann (and various others) 1996, + * 1997, 1998, 1999, all rights reserved. Redistribution of the CSPRNG + * modules and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice + * and this permission notice in its entirety. + * + * 2. Redistributions in binary form must reproduce the copyright notice in + * the documentation and/or other materials provided with the distribution. + * + * 3. A copy of any bugfixes or enhancements made must be provided to the + * author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the + * baseline version of the code. + * + * ALTERNATIVELY, the code may be distributed under the terms of the GNU + * General Public License, version 2 or any later version published by the + * Free Software Foundation, in which case the provisions of the GNU GPL are + * required INSTEAD OF the above restrictions. + * + * Although not required under the terms of the GPL, it would still be nice if + * you could make any changes available to the author to allow a consistent + * code base to be maintained + ************************************************************************* */ #include <config.h> @@ -27,10 +68,16 @@ #include <windows.h> + #include "types.h" #include "g10lib.h" #include "dynload.h" +/* We do not use the netropy DLL anymore because a standalone program is + * easier to maintain and */ +/*#define USE_ENTROPY_DLL*/ + + #ifdef IS_MODULE #define _(a) (a) @@ -39,6 +86,10 @@ #endif +static int debug_me; + +#ifdef USE_ENTROPY_DLL + #define WIN32_SLOW_SEEDER 0 #define WIN32_FAST_SEEDER 1 @@ -53,6 +104,17 @@ #define PCP_DLL_FUNC 8 #define PCP_UNKNOWN_SEEDER_TYPE 9 + +/**************** + * We sometimes get a SEEDER_TOO_SMALL error, in which case we increment + * the internal buffer by SEEDER_INC_CHUNK until we reach MAX_SEEDER_SIZE + * MAX_SEEDER_SIZE is used as an arbitrary limit to protect against + * bugs in Winseed. + */ +#define MAX_SEEDER_SIZE 500000 +#define SEEDER_INC_CHUNK 50000 + + typedef void *WIN32_SEEDER; static WIN32_SEEDER (WINAPI *create_instance)( byte type, unsigned int *reason); @@ -68,8 +130,6 @@ static WIN32_SEEDER slow_seeder, fast_seeder; static byte *entropy_buffer; static size_t entropy_buffer_size; -static char *entropy_dll; - /**************** * Load and initialize the winseed DLL * NOTE: winseed is not part of the GnuPG distribution. It should be available @@ -80,11 +140,17 @@ static char *entropy_dll; static void load_and_init_winseed( void ) { - int hInstance; + HANDLE hInstance; void *addr; unsigned int reason = 0; unsigned int n1, n2; - const char *dllname = entropy_dll? entropy_dll : "c:/gnupg/entropy.dll"; + const char *dllname; + + dllname = read_w32_registry_string( "HKEY_LOCAL_MACHINE", + "Software\\GNU\\GnuPG", + "EntropyDLL" ); + if( !dllname ) + dllname = "c:/gnupg/entropy.dll"; hInstance = LoadLibrary( dllname ); if( !hInstance ) @@ -119,15 +185,14 @@ load_and_init_winseed( void ) g10_log_fatal("error creating winseed fast seeder: rc=%u\n", reason ); goto failure; } - g10_log_info("slow and fast seeders created.\n"); n1 = get_internal_seed_size( slow_seeder ); - g10_log_info("slow buffer size=%u\n", n1); + /*g10_log_info("slow buffer size=%u\n", n1);*/ n2 = get_internal_seed_size( fast_seeder ); - g10_log_info("fast buffer size=%u\n", n2); + /*g10_log_info("fast buffer size=%u\n", n2);*/ entropy_buffer_size = n1 > n2? n1: n2; - entropy_buffer = g10_xmalloc( entropy_buffer_size ); - g10_log_info("using a buffer of size=%u\n", entropy_buffer_size ); + entropy_buffer = m_alloc( entropy_buffer_size ); + /*g10_log_info("using a buffer of size=%u\n", entropy_buffer_size );*/ return; @@ -150,13 +215,16 @@ gather_random( void (*add)(const void*, size_t, int), int requester, unsigned int result; unsigned int nbytes; + if( !level ) + return 0; + if( !slow_seeder ) load_and_init_winseed(); /* Our estimation on how much entropy we should use is very vague. * Winseed delivers some amount of entropy on each slow poll and * we add it to our random pool. Depending on the required quality - * level we adjust the requested length so that for higer quality + * level we adjust the requested length so that for higher quality * we make sure to add more entropy to our pool. However, as we don't * like to waste any entropy collected by winseed, we always add * at least everything we got from winseed. @@ -169,17 +237,35 @@ gather_random( void (*add)(const void*, size_t, int), int requester, for(;;) { nbytes = entropy_buffer_size; result = get_seed( slow_seeder, entropy_buffer, &nbytes); + if( result == PCP_SEEDER_TOO_SMALL ) { + unsigned int n1 = get_internal_seed_size( slow_seeder ); + + if( n1 > MAX_SEEDER_SIZE ) { + g10_log_fatal("rndw32: internal seeder problem (size=%u)\n", + n1); + return -1; /* actually never reached */ + } + n1 += SEEDER_INC_CHUNK; + set_internal_seed_size( slow_seeder, n1 ); + if( n1 > entropy_buffer_size ) { + entropy_buffer_size = n1; + entropy_buffer = m_realloc( entropy_buffer, + entropy_buffer_size ); + } + continue; + } + + if( result ) { g10_log_fatal("rndw32: get_seed(slow) failed: rc=%u\n", result); return -1; /* actually never reached */ } - g10_log_info("rndw32: slow poll level %d, need %u, got %u\n", - level, (unsigned int)length, (unsigned int)nbytes ); + /*g10_log_info("rndw32: slow poll level %d, need %u, got %u\n", + level, (unsigned int)length, (unsigned int)nbytes );*/ (*add)( entropy_buffer, nbytes, requester ); if( length <= nbytes ) return 0; /* okay */ length -= nbytes; - g10_log_info("rndw32: need more\n"); } } @@ -206,6 +292,619 @@ gather_random_fast( void (*add)(const void*, size_t, int), int requester ) return 0; } +#else /* !USE_ENTROPY_DLL */ +/* This is the new code which does not require the entropy.dll */ + +/* + * Definitions which are missing from the current GNU Windows32Api + */ + +#define TH32CS_SNAPHEAPLIST 1 +#define TH32CS_SNAPPROCESS 2 +#define TH32CS_SNAPTHREAD 4 +#define TH32CS_SNAPMODULE 8 +#define TH32CS_SNAPALL (1|2|4|8) +#define TH32CS_INHERIT 0x80000000 + +#define IOCTL_DISK_PERFORMANCE 0x00070020 +#define VER_PLATFORM_WIN32_WINDOWS 1 + + +typedef struct { + DWORD dwSize; + DWORD th32ProcessID; + DWORD th32HeapID; + DWORD dwFlags; +} HEAPLIST32; + +typedef struct { + DWORD dwSize; + HANDLE hHandle; + DWORD dwAddress; + DWORD dwBlockSize; + DWORD dwFlags; + DWORD dwLockCount; + DWORD dwResvd; + DWORD th32ProcessID; + DWORD th32HeapID; +} HEAPENTRY32; + +typedef struct { + DWORD dwSize; + DWORD cntUsage; + DWORD th32ProcessID; + DWORD th32DefaultHeapID; + DWORD th32ModuleID; + DWORD cntThreads; + DWORD th32ParentProcessID; + LONG pcPriClassBase; + DWORD dwFlags; + char szExeFile[260]; +} PROCESSENTRY32; + +typedef struct { + DWORD dwSize; + DWORD cntUsage; + DWORD th32ThreadID; + DWORD th32OwnerProcessID; + LONG tpBasePri; + LONG tpDeltaPri; + DWORD dwFlags; +} THREADENTRY32; + +typedef struct { + DWORD dwSize; + DWORD th32ModuleID; + DWORD th32ProcessID; + DWORD GlblcntUsage; + DWORD ProccntUsage; + BYTE *modBaseAddr; + DWORD modBaseSize; + HMODULE hModule; + char szModule[256]; + char szExePath[260]; +} MODULEENTRY32; + + + +/* Type definitions for function pointers to call Toolhelp32 functions + * used with the windows95 gatherer */ +typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme); +typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte); +typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe); +typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl); +typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID, + DWORD th32HeapID); +typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe); +typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID); + +/* Type definitions for function pointers to call NetAPI32 functions */ +typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService, + DWORD dwLevel, DWORD dwOptions, + LPBYTE * lpBuffer); +typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer); +typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer); + + +/* When we query the performance counters, we allocate an initial buffer and + * then reallocate it as required until RegQueryValueEx() stops returning + * ERROR_MORE_DATA. The following values define the initial buffer size and + * step size by which the buffer is increased + */ +#define PERFORMANCE_BUFFER_SIZE 65536 /* Start at 64K */ +#define PERFORMANCE_BUFFER_STEP 16384 /* Step by 16K */ + + +static void +slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester ) +{ + static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL; + static MODULEWALK pModule32First = NULL; + static MODULEWALK pModule32Next = NULL; + static PROCESSWALK pProcess32First = NULL; + static PROCESSWALK pProcess32Next = NULL; + static THREADWALK pThread32First = NULL; + static THREADWALK pThread32Next = NULL; + static HEAPLISTWALK pHeap32ListFirst = NULL; + static HEAPLISTWALK pHeap32ListNext = NULL; + static HEAPFIRST pHeap32First = NULL; + static HEAPNEXT pHeap32Next = NULL; + HANDLE hSnapshot; + + + /* initialize the Toolhelp32 function pointers */ + if ( !pCreateToolhelp32Snapshot ) { + HANDLE hKernel; + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: init toolkit\n" ); + + /* Obtain the module handle of the kernel to retrieve the addresses + * of the Toolhelp32 functions */ + if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) { + g10_log_fatal ( "rndw32: can't get module handle\n" ); + } + + /* Now get pointers to the functions */ + pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel, + "CreateToolhelp32Snapshot"); + pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First"); + pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next"); + pProcess32First = (PROCESSWALK) GetProcAddress (hKernel, + "Process32First"); + pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel, + "Process32Next"); + pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First"); + pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next"); + pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel, + "Heap32ListFirst"); + pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel, + "Heap32ListNext"); + pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First"); + pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next"); + + if ( !pCreateToolhelp32Snapshot + || !pModule32First || !pModule32Next + || !pProcess32First || !pProcess32Next + || !pThread32First || !pThread32Next + || !pHeap32ListFirst || !pHeap32ListNext + || !pHeap32First || !pHeap32Next ) { + g10_log_fatal ( "rndw32: failed to get a toolhep function\n" ); + } + } + + /* Take a snapshot of everything we can get to which is currently + * in the system */ + if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) { + g10_log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" ); + } + + /* Walk through the local heap */ + { HEAPLIST32 hl32; + hl32.dwSize = sizeof (HEAPLIST32); + if (pHeap32ListFirst (hSnapshot, &hl32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk heap\n" ); + do { + HEAPENTRY32 he32; + + /* First add the information from the basic Heaplist32 struct */ + (*add) ( &hl32, sizeof (hl32), requester ); + + /* Now walk through the heap blocks getting information + * on each of them */ + he32.dwSize = sizeof (HEAPENTRY32); + if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){ + do { + (*add) ( &he32, sizeof (he32), requester ); + } while (pHeap32Next (&he32)); + } + } while (pHeap32ListNext (hSnapshot, &hl32)); + } + } + + + /* Walk through all processes */ + { PROCESSENTRY32 pe32; + pe32.dwSize = sizeof (PROCESSENTRY32); + if (pProcess32First (hSnapshot, &pe32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk processes\n" ); + do { + (*add) ( &pe32, sizeof (pe32), requester ); + } while (pProcess32Next (hSnapshot, &pe32)); + } + } + + /* Walk through all threads */ + { THREADENTRY32 te32; + te32.dwSize = sizeof (THREADENTRY32); + if (pThread32First (hSnapshot, &te32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk threads\n" ); + do { + (*add) ( &te32, sizeof (te32), requester ); + } while (pThread32Next (hSnapshot, &te32)); + } + } + + /* Walk through all modules associated with the process */ + { MODULEENTRY32 me32; + me32.dwSize = sizeof (MODULEENTRY32); + if (pModule32First (hSnapshot, &me32)) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_95: walk modules\n" ); + do { + (*add) ( &me32, sizeof (me32), requester ); + } while (pModule32Next (hSnapshot, &me32)); + } + } + + CloseHandle (hSnapshot); +} + + + +static void +slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester ) +{ + static int is_initialized = 0; + static NETSTATISTICSGET pNetStatisticsGet = NULL; + static NETAPIBUFFERSIZE pNetApiBufferSize = NULL; + static NETAPIBUFFERFREE pNetApiBufferFree = NULL; + static int is_workstation = 1; + + static int cbPerfData = PERFORMANCE_BUFFER_SIZE; + PERF_DATA_BLOCK *pPerfData; + HANDLE hDevice, hNetAPI32 = NULL; + DWORD dwSize, status; + int nDrive; + + if ( !is_initialized ) { + HKEY hKey; + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" ); + /* Find out whether this is an NT server or workstation if necessary */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + BYTE szValue[32]; + dwSize = sizeof (szValue); + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: check product options\n" ); + status = RegQueryValueEx (hKey, "ProductType", 0, NULL, + szValue, &dwSize); + if (status == ERROR_SUCCESS && stricmp (szValue, "WinNT")) { + /* Note: There are (at least) three cases for ProductType: + * WinNT = NT Workstation, ServerNT = NT Server, LanmanNT = + * NT Server acting as a Domain Controller */ + is_workstation = 0; + if ( debug_me ) + log_debug ("rndw32: this is a NT server\n"); + } + RegCloseKey (hKey); + } + + /* Initialize the NetAPI32 function pointers if necessary */ + if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" ); + pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32, + "NetStatisticsGet"); + pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32, + "NetApiBufferSize"); + pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32, + "NetApiBufferFree"); + + if ( !pNetStatisticsGet + || !pNetApiBufferSize || !pNetApiBufferFree ) { + FreeLibrary (hNetAPI32); + hNetAPI32 = NULL; + g10_log_debug ("rndw32: No NETAPI found\n" ); + } + } + + is_initialized = 1; + } + + /* Get network statistics. Note: Both NT Workstation and NT Server by + * default will be running both the workstation and server services. The + * heuristic below is probably useful though on the assumption that the + * majority of the network traffic will be via the appropriate service. + * In any case the network statistics return almost no randomness */ + { LPBYTE lpBuffer; + if (hNetAPI32 && !pNetStatisticsGet (NULL, + is_workstation ? L"LanmanWorkstation" : + L"LanmanServer", 0, 0, &lpBuffer) ) { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: get netstats\n" ); + pNetApiBufferSize (lpBuffer, &dwSize); + (*add) ( lpBuffer, dwSize,requester ); + pNetApiBufferFree (lpBuffer); + } + } + + /* Get disk I/O statistics for all the hard drives */ + for (nDrive = 0;; nDrive++) { + DISK_PERFORMANCE diskPerformance; + char szDevice[50]; + + /* Check whether we can access this device */ + sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive); + hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (hDevice == INVALID_HANDLE_VALUE) + break; + + /* Note: This only works if you have turned on the disk performance + * counters with 'diskperf -y'. These counters are off by default */ + if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, + &diskPerformance, sizeof (DISK_PERFORMANCE), + &dwSize, NULL)) + { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n", + nDrive ); + (*add) ( &diskPerformance, dwSize, requester ); + } + else { + log_info ("NOTE: you should run 'diskperf -y' " + "to enable the disk statistics\n"); + } + CloseHandle (hDevice); + } + + #if 0 /* we don't need this in GnuPG */ + /* Wait for any async keyset driver binding to complete. You may be + * wondering what this call is doing here... the reason it's necessary is + * because RegQueryValueEx() will hang indefinitely if the async driver + * bind is in progress. The problem occurs in the dynamic loading and + * linking of driver DLL's, which work as follows: + * + * hDriver = LoadLibrary( DRIVERNAME ); + * pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 ); + * pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 ); + * + * If RegQueryValueEx() is called while the GetProcAddress()'s are in + * progress, it will hang indefinitely. This is probably due to some + * synchronisation problem in the NT kernel where the GetProcAddress() + * calls affect something like a module reference count or function + * reference count while RegQueryValueEx() is trying to take a snapshot + * of the statistics, which include the reference counts. Because of + * this, we have to wait until any async driver bind has completed + * before we can call RegQueryValueEx() */ + waitSemaphore (SEMAPHORE_DRIVERBIND); + #endif + + /* Get information from the system performance counters. This can take + * a few seconds to do. In some environments the call to + * RegQueryValueEx() can produce an access violation at some random time + * in the future, adding a short delay after the following code block + * makes the problem go away. This problem is extremely difficult to + * reproduce, I haven't been able to get it to occur despite running it + * on a number of machines. The best explanation for the problem is that + * on the machine where it did occur, it was caused by an external driver + * or other program which adds its own values under the + * HKEY_PERFORMANCE_DATA key. The NT kernel calls the required external + * modules to map in the data, if there's a synchronisation problem the + * external module would write its data at an inappropriate moment, + * causing the access violation. A low-level memory checker indicated + * that ExpandEnvironmentStrings() in KERNEL32.DLL, called an + * interminable number of calls down inside RegQueryValueEx(), was + * overwriting memory (it wrote twice the allocated size of a buffer to a + * buffer allocated by the NT kernel). This may be what's causing the + * problem, but since it's in the kernel there isn't much which can be + * done. + * + * In addition to these problems the code in RegQueryValueEx() which + * estimates the amount of memory required to return the performance + * counter information isn't very accurate, since it always returns a + * worst-case estimate which is usually nowhere near the actual amount + * required. For example it may report that 128K of memory is required, + * but only return 64K of data */ + { pPerfData = m_alloc (cbPerfData); + for (;;) { + dwSize = cbPerfData; + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: get perf data\n" ); + status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL, + NULL, (LPBYTE) pPerfData, &dwSize); + if (status == ERROR_SUCCESS) { + if (!memcmp (pPerfData->Signature, L"PERF", 8)) { + (*add) ( pPerfData, dwSize, requester ); + } + else + g10_log_debug ( "rndw32: no PERF signature\n"); + break; + } + else if (status == ERROR_MORE_DATA) { + cbPerfData += PERFORMANCE_BUFFER_STEP; + pPerfData = m_realloc (pPerfData, cbPerfData); + } + else { + g10_log_debug ( "rndw32: get performance data problem\n"); + break; + } + } + m_free (pPerfData); + } + /* Although this isn't documented in the Win32 API docs, it's necessary + to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's + implicitly opened on the first call to RegQueryValueEx()). If this + isn't done then any system components which provide performance data + can't be removed or changed while the handle remains active */ + RegCloseKey (HKEY_PERFORMANCE_DATA); +} + + +static int +gather_random( void (*add)(const void*, size_t, int), int requester, + size_t length, int level ) +{ + static int is_initialized; + static int is_windows95; + + + if( !level ) + return 0; + /* We don't differentiate between level 1 and 2 here because + * there is no nternal entropy pool as a scary resource. It may + * all work slower, but because our entropy source will never + * block but deliver some not easy to measure entropy, we assume level 2 + */ + + + if ( !is_initialized ) { + OSVERSIONINFO osvi = { sizeof( osvi ) }; + DWORD platform; + + GetVersionEx( &osvi ); + platform = osvi.dwPlatformId; + is_windows95 = platform == VER_PLATFORM_WIN32_WINDOWS; + + if ( platform == VER_PLATFORM_WIN32s ) { + g10_log_fatal("can't run on a W32s platform\n" ); + } + is_initialized = 1; + if ( debug_me ) + log_debug ("rndw32#gather_random: platform=%d\n", (int)platform ); + } + + + if ( debug_me ) + log_debug ("rndw32#gather_random: req=%d len=%u lvl=%d\n", + requester, (unsigned int)length, level ); + + if (is_windows95 ) { + slow_gatherer_windows95( add, requester ); + } + else { + slow_gatherer_windowsNT( add, requester ); + } + + return 0; +} + + + +static int +gather_random_fast( void (*add)(const void*, size_t, int), int requester ) +{ + static int addedFixedItems = 0; + + if ( debug_me ) + log_debug ("rndw32#gather_random_fast: req=%d\n", requester ); + + /* Get various basic pieces of system information: Handle of active + * window, handle of window with mouse capture, handle of clipboard owner + * handle of start of clpboard viewer list, pseudohandle of current + * process, current process ID, pseudohandle of current thread, current + * thread ID, handle of desktop window, handle of window with keyboard + * focus, whether system queue has any events, cursor position for last + * message, 1 ms time for last message, handle of window with clipboard + * open, handle of process heap, handle of procs window station, types of + * events in input queue, and milliseconds since Windows was started */ + { byte buffer[20*sizeof(ulong)], *bufptr; + bufptr = buffer; + #define ADD(f) do { ulong along = (ulong)(f); \ + memcpy (bufptr, &along, sizeof (along) ); \ + bufptr += sizeof (along); } while (0) + ADD ( GetActiveWindow ()); + ADD ( GetCapture ()); + ADD ( GetClipboardOwner ()); + ADD ( GetClipboardViewer ()); + ADD ( GetCurrentProcess ()); + ADD ( GetCurrentProcessId ()); + ADD ( GetCurrentThread ()); + ADD ( GetCurrentThreadId ()); + ADD ( GetDesktopWindow ()); + ADD ( GetFocus ()); + ADD ( GetInputState ()); + ADD ( GetMessagePos ()); + ADD ( GetMessageTime ()); + ADD ( GetOpenClipboardWindow ()); + ADD ( GetProcessHeap ()); + ADD ( GetProcessWindowStation ()); + ADD ( GetQueueStatus (QS_ALLEVENTS)); + ADD ( GetTickCount ()); + + assert ( bufptr-buffer < sizeof (buffer) ); + (*add) ( buffer, bufptr-buffer, requester ); + #undef ADD + } + + /* Get multiword system information: Current caret position, current + * mouse cursor position */ + { POINT point; + GetCaretPos (&point); + (*add) ( &point, sizeof (point), requester ); + GetCursorPos (&point); + (*add) ( &point, sizeof (point), requester ); + } + + /* Get percent of memory in use, bytes of physical memory, bytes of free + * physical memory, bytes in paging file, free bytes in paging file, user + * bytes of address space, and free user bytes */ + { MEMORYSTATUS memoryStatus; + memoryStatus.dwLength = sizeof (MEMORYSTATUS); + GlobalMemoryStatus (&memoryStatus); + (*add) ( &memoryStatus, sizeof (memoryStatus), requester ); + } + + /* Get thread and process creation time, exit time, time in kernel mode, + and time in user mode in 100ns intervals */ + { HANDLE handle; + FILETIME creationTime, exitTime, kernelTime, userTime; + DWORD minimumWorkingSetSize, maximumWorkingSetSize; + + handle = GetCurrentThread (); + GetThreadTimes (handle, &creationTime, &exitTime, + &kernelTime, &userTime); + (*add) ( &creationTime, sizeof (creationTime), requester ); + (*add) ( &exitTime, sizeof (exitTime), requester ); + (*add) ( &kernelTime, sizeof (kernelTime), requester ); + (*add) ( &userTime, sizeof (userTime), requester ); + + handle = GetCurrentProcess (); + GetProcessTimes (handle, &creationTime, &exitTime, + &kernelTime, &userTime); + (*add) ( &creationTime, sizeof (creationTime), requester ); + (*add) ( &exitTime, sizeof (exitTime), requester ); + (*add) ( &kernelTime, sizeof (kernelTime), requester ); + (*add) ( &userTime, sizeof (userTime), requester ); + + /* Get the minimum and maximum working set size for the current process */ + GetProcessWorkingSetSize (handle, &minimumWorkingSetSize, + &maximumWorkingSetSize); + (*add) ( &minimumWorkingSetSize, + sizeof (&minimumWorkingSetSize), requester ); + (*add) ( &maximumWorkingSetSize, + sizeof (&maximumWorkingSetSize), requester ); + } + + + /* The following are fixed for the lifetime of the process so we only + * add them once */ + if (!addedFixedItems) { + STARTUPINFO startupInfo; + + /* Get name of desktop, console window title, new window position and + * size, window flags, and handles for stdin, stdout, and stderr */ + startupInfo.cb = sizeof (STARTUPINFO); + GetStartupInfo (&startupInfo); + (*add) ( &startupInfo, sizeof (STARTUPINFO), requester ); + addedFixedItems = 1; + } + + /* The performance of QPC varies depending on the architecture it's + * running on and on the OS. Under NT it reads the CPU's 64-bit timestamp + * counter (at least on a Pentium and newer '486's, it hasn't been tested + * on anything without a TSC), under Win95 it reads the 1.193180 MHz PIC + * timer. There are vague mumblings in the docs that it may fail if the + * appropriate hardware isn't available (possibly '386's or MIPS machines + * running NT), but who's going to run NT on a '386? */ + { LARGE_INTEGER performanceCount; + if (QueryPerformanceCounter (&performanceCount)) { + if ( debug_me ) + log_debug ("rndw32#gather_random_fast: perf data\n"); + (*add) (&performanceCount, sizeof (&performanceCount), requester); + } + else { /* Millisecond accuracy at best... */ + DWORD aword = GetTickCount (); + (*add) (&aword, sizeof (aword), requester ); + } + } + + return 0; +} + + + + + +#endif /* !USE_ENTROPY_DLL */ #ifndef IS_MODULE @@ -232,6 +931,8 @@ gnupgext_enum_func( int what, int *sequence, int *class, int *vers ) void *ret; int i = *sequence; + debug_me = !!getenv("DEBUG_RNDW32"); + do { if ( i >= DIM(func_table) || i < 0 ) { return NULL; @@ -246,14 +947,6 @@ gnupgext_enum_func( int what, int *sequence, int *class, int *vers ) return ret; } -#ifdef USE_STATIC_RNDW32 -void -rndw32_set_dll_name( const char *name ) -{ - entropy_dll = m_strdup( name ); -} -#endif - #ifndef IS_MODULE void rndw32_constructor(void) diff --git a/cipher/rsa.c b/cipher/rsa.c new file mode 100644 index 00000000..5d852cd8 --- /dev/null +++ b/cipher/rsa.c @@ -0,0 +1,375 @@ +/* rsa.c - RSA function + * Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn) + * Copyright (C) 2000 Free Software Foundation, Inc. + *********************************************************************** + * ATTENTION: This code should not be used in the United States + * before the U.S. Patent #4,405,829 expires on September 20, 2000! + *********************************************************************** + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "util.h" +#include "mpi.h" +#include "cipher.h" +#include "rsa.h" + + +typedef struct { + MPI n; /* modulus */ + MPI e; /* exponent */ +} RSA_public_key; + + +typedef struct { + MPI n; /* public modulus */ + MPI e; /* public exponent */ + MPI d; /* exponent */ + MPI p; /* prime p. */ + MPI q; /* prime q. */ + MPI u; /* inverse of p mod q. */ +} RSA_secret_key; + + +static void test_keys( RSA_secret_key *sk, unsigned nbits ); +static void generate( RSA_secret_key *sk, unsigned nbits ); +static int check_secret_key( RSA_secret_key *sk ); +static void public(MPI output, MPI input, RSA_public_key *skey ); +static void secret(MPI output, MPI input, RSA_secret_key *skey ); + + +static void +test_keys( RSA_secret_key *sk, unsigned nbits ) +{ + RSA_public_key pk; + MPI test = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + MPI out1 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + MPI out2 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + + pk.n = sk->n; + pk.e = sk->e; + { char *p = get_random_bits( nbits, 0, 0 ); + mpi_set_buffer( test, p, (nbits+7)/8, 0 ); + m_free(p); + } + + public( out1, test, &pk ); + secret( out2, out1, sk ); + if( mpi_cmp( test, out2 ) ) + log_fatal("RSA operation: public, secret failed\n"); + secret( out1, test, sk ); + public( out2, out1, &pk ); + if( mpi_cmp( test, out2 ) ) + log_fatal("RSA operation: secret, public failed\n"); + mpi_free( test ); + mpi_free( out1 ); + mpi_free( out2 ); +} + +/**************** + * Generate a key pair with a key of size NBITS + * Returns: 2 structures filles with all needed values + */ +static void +generate( RSA_secret_key *sk, unsigned nbits ) +{ + MPI p, q; /* the two primes */ + MPI d; /* the private key */ + MPI u; + MPI t1, t2; + MPI n; /* the public key */ + MPI e; /* the exponent */ + MPI phi; /* helper: (p-a)(q-1) */ + MPI g; + MPI f; + + /* select two (very secret) primes */ + p = generate_secret_prime( nbits / 2 ); + q = generate_secret_prime( nbits / 2 ); + if( mpi_cmp( p, q ) > 0 ) /* p shall be smaller than q (for calc of u)*/ + mpi_swap(p,q); + /* calculate Euler totient: phi = (p-1)(q-1) */ + t1 = mpi_alloc_secure( mpi_get_nlimbs(p) ); + t2 = mpi_alloc_secure( mpi_get_nlimbs(p) ); + phi = mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + g = mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + f = mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_sub_ui( t1, p, 1 ); + mpi_sub_ui( t2, q, 1 ); + mpi_mul( phi, t1, t2 ); + mpi_gcd(g, t1, t2); + mpi_fdiv_q(f, phi, g); + /* multiply them to make the private key */ + n = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_mul( n, p, q ); + /* find a public exponent */ + e = mpi_alloc( (6+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_set_ui( e, 17); /* start with 17 */ + while( !mpi_gcd(t1, e, phi) ) /* (while gcd is not 1) */ + mpi_add_ui( e, e, 2); + /* calculate the secret key d = e^1 mod phi */ + d = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_invm(d, e, f ); + /* calculate the inverse of p and q (used for chinese remainder theorem)*/ + u = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + mpi_invm(u, p, q ); + + if( DBG_CIPHER ) { + log_mpidump(" p= ", p ); + log_mpidump(" q= ", q ); + log_mpidump("phi= ", phi ); + log_mpidump(" g= ", g ); + log_mpidump(" f= ", f ); + log_mpidump(" n= ", n ); + log_mpidump(" e= ", e ); + log_mpidump(" d= ", d ); + log_mpidump(" u= ", u ); + } + + mpi_free(t1); + mpi_free(t2); + mpi_free(phi); + mpi_free(f); + mpi_free(g); + + sk->n = n; + sk->e = e; + sk->p = p; + sk->q = q; + sk->d = d; + sk->u = u; + + /* now we can test our keys (this should never fail!) */ + test_keys( sk, nbits - 64 ); +} + + +/**************** + * Test wether the secret key is valid. + * Returns: true if this is a valid key. + */ +static int +check_secret_key( RSA_secret_key *sk ) +{ + int rc; + MPI temp = mpi_alloc( mpi_get_nlimbs(sk->p)*2 ); + + mpi_mul(temp, sk->p, sk->q ); + rc = mpi_cmp( temp, sk->n ); + mpi_free(temp); + return !rc; +} + + + +/**************** + * Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT. + * + * c = m^e mod n + * + * Where c is OUTPUT, m is INPUT and e,n are elements of PKEY. + */ +static void +public(MPI output, MPI input, RSA_public_key *pkey ) +{ + if( output == input ) { /* powm doesn't like output and input the same */ + MPI x = mpi_alloc( mpi_get_nlimbs(input)*2 ); + mpi_powm( x, input, pkey->e, pkey->n ); + mpi_set(output, x); + mpi_free(x); + } + else + mpi_powm( output, input, pkey->e, pkey->n ); +} + +/**************** + * Secret key operation. Encrypt INPUT with SKEY and put result into OUTPUT. + * + * m = c^d mod n + * + * Where m is OUTPUT, c is INPUT and d,n are elements of PKEY. + * + * FIXME: We should better use the Chinese Remainder Theorem + */ +static void +secret(MPI output, MPI input, RSA_secret_key *skey ) +{ + mpi_powm( output, input, skey->d, skey->n ); +} + + +/********************************************* + ************** interface ****************** + *********************************************/ + +int +rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) +{ + RSA_secret_key sk; + + if( !is_RSA(algo) ) + return G10ERR_PUBKEY_ALGO; + + generate( &sk, nbits ); + skey[0] = sk.n; + skey[1] = sk.e; + skey[2] = sk.d; + skey[3] = sk.p; + skey[4] = sk.q; + skey[5] = sk.u; + /* make an empty list of factors */ + *retfactors = m_alloc_clear( 1 * sizeof **retfactors ); + return 0; +} + + +int +rsa_check_secret_key( int algo, MPI *skey ) +{ + RSA_secret_key sk; + + if( !is_RSA(algo) ) + return G10ERR_PUBKEY_ALGO; + + sk.n = skey[0]; + sk.e = skey[1]; + sk.d = skey[2]; + sk.p = skey[3]; + sk.q = skey[4]; + sk.u = skey[5]; + if( !check_secret_key( &sk ) ) + return G10ERR_BAD_SECKEY; + + return 0; +} + + + +int +rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) +{ + RSA_public_key pk; + + if( algo != 1 && algo != 2 ) + return G10ERR_PUBKEY_ALGO; + + pk.n = pkey[0]; + pk.e = pkey[1]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.n ) ); + public( resarr[0], data, &pk ); + return 0; +} + +int +rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) +{ + RSA_secret_key sk; + + if( algo != 1 && algo != 2 ) + return G10ERR_PUBKEY_ALGO; + + sk.n = skey[0]; + sk.e = skey[1]; + sk.d = skey[2]; + sk.p = skey[3]; + sk.q = skey[4]; + sk.u = skey[5]; + *result = mpi_alloc_secure( mpi_get_nlimbs( sk.n ) ); + secret( *result, data[0], &sk ); + return 0; +} + +int +rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) +{ + RSA_secret_key sk; + + if( algo != 1 && algo != 3 ) + return G10ERR_PUBKEY_ALGO; + + sk.n = skey[0]; + sk.e = skey[1]; + sk.d = skey[2]; + sk.p = skey[3]; + sk.q = skey[4]; + sk.u = skey[5]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.n ) ); + secret( resarr[0], data, &sk ); + + return 0; +} + +int +rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *opaque, MPI tmp), void *opaquev ) +{ + RSA_public_key pk; + MPI result; + int rc; + + if( algo != 1 && algo != 3 ) + return G10ERR_PUBKEY_ALGO; + pk.n = pkey[0]; + pk.e = pkey[1]; + result = mpi_alloc( (160+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB); + public( result, data[0], &pk ); + /*rc = (*cmp)( opaquev, result );*/ + rc = mpi_cmp( result, hash )? G10ERR_BAD_SIGN:0; + mpi_free(result); + + return rc; +} + + +unsigned int +rsa_get_nbits( int algo, MPI *pkey ) +{ + if( !is_RSA(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 + */ +const char * +rsa_get_info( int algo, + int *npkey, int *nskey, int *nenc, int *nsig, int *usage ) +{ + *npkey = 2; + *nskey = 6; + *nenc = 1; + *nsig = 1; + + switch( algo ) { + case 1: *usage = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; return "RSA"; + case 2: *usage = PUBKEY_USAGE_ENC; return "RSA-E"; + case 3: *usage = PUBKEY_USAGE_SIG; return "RSA-S"; + default:*usage = 0; return NULL; + } +} + diff --git a/cipher/rsa.h b/cipher/rsa.h new file mode 100644 index 00000000..8b60ecbb --- /dev/null +++ b/cipher/rsa.h @@ -0,0 +1,36 @@ +/* rsa.h + * Copyright (C) 1997,1998 by Werner Koch (dd9jn) + * Copyright (C) 2000 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef G10_RSA_H +#define G10_RSA_H + +int rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ); +int rsa_check_secret_key( int algo, MPI *skey ); +int rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ); +int rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey ); +int rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ); +int rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *, MPI), void *opaquev ); +unsigned rsa_get_nbits( int algo, MPI *pkey ); +const char *rsa_get_info( int algo, int *npkey, int *nskey, + int *nenc, int *nsig, int *use ); + + +#endif /*G10_RSA_H*/ diff --git a/cipher/sha1.c b/cipher/sha1.c index f231e37b..aa3ac092 100644 --- a/cipher/sha1.c +++ b/cipher/sha1.c @@ -337,10 +337,10 @@ sha1_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 20; - *r_init = (void (*)(void *))sha1_init; - *r_write = (void (*)(void *, byte*, size_t))sha1_write; - *r_final = (void (*)(void *))sha1_final; - *r_read = (byte *(*)(void *))sha1_read; + *(void (**)(SHA1_CONTEXT *))r_init = sha1_init; + *(void (**)(SHA1_CONTEXT *, byte*, size_t))r_write = sha1_write; + *(void (**)(SHA1_CONTEXT *))r_final = sha1_final; + *(byte *(**)(SHA1_CONTEXT *))r_read = sha1_read; return "SHA1"; } diff --git a/cipher/tiger.c b/cipher/tiger.c index 0e42160a..abe449ab 100644 --- a/cipher/tiger.c +++ b/cipher/tiger.c @@ -899,10 +899,10 @@ tiger_get_info( int algo, size_t *contextsize, *r_asnoid = asn; *r_asnlen = DIM(asn); *r_mdlen = 24; - *r_init = (void (*)(void *))tiger_init; - *r_write = (void (*)(void *, byte*, size_t))tiger_write; - *r_final = (void (*)(void *))tiger_final; - *r_read = (byte *(*)(void *))tiger_read; + *(void (**)(TIGER_CONTEXT *))r_init = tiger_init; + *(void (**)(TIGER_CONTEXT *, byte*, size_t))r_write = tiger_write; + *(void (**)(TIGER_CONTEXT *))r_final = tiger_final; + *(byte *(**)(TIGER_CONTEXT *))r_read = tiger_read; return "TIGER"; } diff --git a/cipher/twofish.c b/cipher/twofish.c index 1eea4b8e..5766021c 100644 --- a/cipher/twofish.c +++ b/cipher/twofish.c @@ -34,10 +34,6 @@ /* Prototype for the self-test function. */ static const char *selftest(void); -/* Macros used by the info function. */ -#define FNCCAST_SETKEY(f) ((int(*)(void*, byte*, unsigned))(f)) -#define FNCCAST_CRYPT(f) ((void(*)(void*, byte*, byte*))(f)) - /* Structure for an expanded Twofish key. s contains the key-dependent * S-boxes composed with the MDS matrix; w contains the eight "whitening" * subkeys, K[0] through K[7]. k holds the remaining, "round" subkeys. Note @@ -990,16 +986,20 @@ twofish_get_info (int algo, size_t *keylen, *keylen = algo==10? 256 : 128; *blocksize = 16; *contextsize = sizeof (TWOFISH_context); - *r_setkey = FNCCAST_SETKEY (twofish_setkey); - *r_encrypt= FNCCAST_CRYPT (twofish_encrypt); - *r_decrypt= FNCCAST_CRYPT (twofish_decrypt); - - if( algo == 10 ) - return "TWOFISH"; - if (algo == 102) /* This algorithm number is assigned for - * experiments, so we can use it */ - return "TWOFISH128"; - return NULL; + + *(int (**)(TWOFISH_context*, const byte*, const unsigned))r_setkey + = twofish_setkey; + *(void (**)(const TWOFISH_context*, byte*, const byte*))r_encrypt + = twofish_encrypt; + *(void (**)(const TWOFISH_context*, byte*, const byte*))r_decrypt + = twofish_decrypt; + + if( algo == 10 ) + return "TWOFISH"; + if (algo == 102) /* This algorithm number is assigned for + * experiments, so we can use it */ + return "TWOFISH128"; + return NULL; } |